MYSQL - ROLLUP - 用SUBTOTAL和TOTAL替换NULL

the*_*let 7 mysql rollup totals ifnull

我试图使用IFNULL来替换ROLLUP返回的NULL字段的小计和总计,但它似乎没有工作.

查询:

select IFNULL(usergroups.name, 'GROUP') AS DEALER,
IFNULL(users.name, 'TOTAL') AS SERVICE_ADVISOR,
COUNT(DISTINCT vcrs.uid) AS COMPLETED,
..........
..........
and vcrs.vcrSubStatus = 4
group by DEALER, SERVICE_ADVISOR with ROLLUP;
Run Code Online (Sandbox Code Playgroud)

输出:

DEALER          SERVICE_ADVISOR COMPLETED   IDENTIFIED  AUTHORISED
Aston Martin    Chris           3           664.56      0
Aston Martin    Graham          6           0           0
Aston Martin    (NULL)          15          664.56      0
Bentley         Sukraj          1           0           0
Bentley         Terry           4           0           0
Bentley         (NULL)          5           0           0
Jaguar          Emma            10          2448        1224
Jaguar          Paul            1           0           0
Jaguar          Simon           7           2754        918
Jaguar          (NULL)          18          5202        2142
(NULL)          (NULL)          2611        96591.62    42130.14
Run Code Online (Sandbox Code Playgroud)

期望的输出:

DEALER          SERVICE_ADVISOR COMPLETED   IDENTIFIED  AUTHORISED
Aston Martin    Chris           3           664.56      0
Aston Martin    Graham          6           0           0
Aston Martin    TOTAL           15          664.56      0
Bentley         Sukraj          1           0           0
Bentley         Terry           4           0           0
Bentley         TOTAL           5           0           0
Jaguar          Emma            10          2448        1224
Jaguar          Paul            1           0           0
Jaguar          Simon           7           2754        918
Jaguar          TOTAL           18          5202        2142
GROUP           TOTAL           2611        96591.62    42130.14
Run Code Online (Sandbox Code Playgroud)

提前致谢!

fyr*_*rye 7

与解决该问题COALESCE/IFNULL仍然返回NULLWITH ROLLUP占位符,你需要GROUP BY的表的列名,而不是别名列表达式。

该问题是由在别GROUP BY名列表达式上指定的子句引起的,因为在处理列表达式之后分配别名。
导致WITH ROLLUP NULL占位符不在要评估的记录集中COALESCE
含义的别名DEALERSERVICE_ADVISORGROUP BY不存在,直到 IFNULL/COALESCE已经执行。有关更多详细信息,请参阅 MySQL处理GROUP BY

示例 数据库小提琴

CREATE TABLE foo (
  `amount` INTEGER,
  `created` INTEGER
);

INSERT INTO foo
  (`amount`, `created`)
VALUES
  ('1', '2019'),
  ('2', '2019');
Run Code Online (Sandbox Code Playgroud)

查询 #1(重现问题)

SELECT
    SUM(amount) AS amounts,
    COALESCE(created, 'Total') AS created_coalesce
FROM foo
GROUP BY created_coalesce WITH ROLLUP;

| amounts | created_coalesce |
| ------- | ---------------- |
| 3       | 2019             |
| 3       |                  |
Run Code Online (Sandbox Code Playgroud)

查询 #2(更正)

SELECT
    SUM(amount) AS amounts,
    COALESCE(created, 'Total') AS created_coalesce
FROM foo
GROUP BY foo.created WITH ROLLUP;

| amounts | created_coalesce |
| ------- | ---------------- |
| 3       | 2019             |
| 3       | Total            |
Run Code Online (Sandbox Code Playgroud)

用例特定

示例 数据库小提琴

SELECT
    COALESCE(usergroups.name, 'GROUP') AS DEALER,
    COALESCE(users.name, 'TOTAL') AS SERVICE_ADVISOR,
    COUNT(DISTINCT vcrs.uid) AS COMPLETED,
/* ... */
GROUP BY usergroups.name, users.name WITH ROLLUP;
Run Code Online (Sandbox Code Playgroud)

查询 #1(原始)

SELECT
    COALESCE(usergroups.name, 'GROUP') AS DEALER,
    COALESCE(users.name, 'TOTAL') AS SERVICE_ADVISOR,
    COUNT(DISTINCT vcrs.uid) AS COMPLETED
/* ... */
GROUP BY DEALER, SERVICE_ADVISOR WITH ROLLUP;

| DEALER | SERVICE_ADVISOR | COMPLETED |
| ------ | --------------- | --------- |
| Foo    | Jane Doe        | 1         |
| Foo    | John Doe        | 1         |
| Foo    |                 | 2         |
|        |                 | 2         |
Run Code Online (Sandbox Code Playgroud)

查询 #2(更正)

SELECT
    COALESCE(usergroups.name, 'GROUP') AS DEALER,
    COALESCE(users.name, 'TOTAL') AS SERVICE_ADVISOR,
    COUNT(DISTINCT vcrs.uid) AS COMPLETED
/* ... */
GROUP BY usergroups.name, users.name WITH ROLLUP;

| DEALER | SERVICE_ADVISOR | COMPLETED |
| ------ | --------------- | --------- |
| Foo    | Jane Doe        | 1         |
| Foo    | John Doe        | 1         |
| Foo    | TOTAL           | 2         |
| GROUP  | TOTAL           | 2         |
Run Code Online (Sandbox Code Playgroud)

注意事项

  1. 启用MySQL 5.7+ 和ONLY_FULL_GROUP_BY 后GROUP BY子句中未指定的选定非聚合列将失败。这意味着以下查询不会按预期工作:DB-Fiddle

    SELECT COALESCE(YEAR(foo), 'foo') /* ... */ GROUP BY YEAR(foo) WITH ROLLUP
    -> ER_WRONG_FIELD_WITH_GROUP: Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.foo_bar.foo' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
    
    Run Code Online (Sandbox Code Playgroud)
  2. COALESCE, IFNULL, IF(... IS NULL)CASE WHEN ... IS NULL 功能类似。哪里IFNULL是 MySQL 的专有,是功能较弱的COALESCE. 由于COALESCE可以接受两个以上的参数进行检查NULL,因此返回第一个non-NULL值。

    mysql> SELECT COALESCE(NULL, NULL, 1, NULL);
           -> 1
    
    mysql> SELECT IFNULL(NULL, 1);
           -> 1
    
    mysql> SELECT IF(NULL IS NULL, 1, '');
           -> 1
    
    mysql> SELECT CASE WHEN NULL IS NULL THEN 1 END;
           -> 1
    
    Run Code Online (Sandbox Code Playgroud)
  3. GROUP BY作为别名或列名的可为空的列将导致NULL值显示为WITH ROLLUP占位符。这适用WITH ROLLUP于一般使用。例如如果 users.name可以返回NULL数据库小提琴

    | DEALER | SERVICE_ADVISOR | COMPLETED | 
    | ------ | --------------- | --------- | 
    | Foo    | TOTAL           | 1         |
    | Foo    | Jane Doe        | 1         |
    | Foo    | John Doe        | 1         |
    | Foo    | TOTAL           | 3         |
    | GROUP  | TOTAL           | 3         |
    
    Run Code Online (Sandbox Code Playgroud)

防止NULL显示列值

为确保不会意外包含可为空的列,您需要在条件中指定以排除它们。

示例 数据库小提琴

CREATE TABLE foo (
  `amount` INTEGER,
  `created` INTEGER
);

INSERT INTO foo
  (`amount`, `created`)
VALUES
  ('1', '2019'),
  ('2', '2019');
Run Code Online (Sandbox Code Playgroud)

结果

| DEALER | SERVICE_ADVISOR | COMPLETED |
| ------ | --------------- | --------- |
| Foo    | Jane Doe        | 1         |
| Foo    | John Doe        | 1         |
| Foo    | TOTAL           | 2         |
| GROUP  | TOTAL           | 2         |
Run Code Online (Sandbox Code Playgroud)

既然LEFT JOIN是用在vcrs表上,IS NOT NULL就必须应用于WHERE子句,而不是ON子句。作为不匹配标准的LEFT JOIN回报NULL。为了规避这个问题,使用 anINNER JOIN将结果集限制为仅具有匹配ON条件的结果集。

/* ... */
INNER JOIN users
ON users.id = vcrs.uid
AND users.name IS NOT NULL
/* ... */
WHERE vcrs.vcrSubStatus = 4
GROUP BY usergroups.name, users.name
WITH ROLLUP;
Run Code Online (Sandbox Code Playgroud)

包括NULL列值

要显式包含可为空的列值,而不复制WITH ROLLUP占位符名称,您需要利用派生表子查询将该NULL值替换为文本值。

示例 数据库小提琴

SELECT
    SUM(amount) AS amounts,
    COALESCE(created, 'Total') AS created_coalesce
FROM foo
GROUP BY created_coalesce WITH ROLLUP;

| amounts | created_coalesce |
| ------- | ---------------- |
| 3       | 2019             |
| 3       |                  |
Run Code Online (Sandbox Code Playgroud)

结果

| DEALER | SERVICE_ADVISOR | COMPLETED |
| ------ | --------------- | --------- |
| Foo    | Jane Doe        | 1         |
| Foo    | John Doe        | 1         |
| Foo    | NULL            | 1         |
| Foo    | TOTAL           | 3         |
| GROUP  | TOTAL           | 3         |
Run Code Online (Sandbox Code Playgroud)

您还可以'NULL'根据需要选择替换文本占位符,甚至将其显示为NULL.

SELECT
    SUM(amount) AS amounts,
    COALESCE(created, 'Total') AS created_coalesce
FROM foo
GROUP BY foo.created WITH ROLLUP;

| amounts | created_coalesce |
| ------- | ---------------- |
| 3       | 2019             |
| 3       | Total            |
Run Code Online (Sandbox Code Playgroud)


Dav*_*e P 5

我只晚了 2 年,但是因为我遇到了与@the_gimlet 相同的问题,所以我想我会发布答案。

所以不知道这是 mySQL 版本控制还是什么,但是使用 mysql 5.6 我遇到了同样的问题...... ifnull 不会替换汇总“nulls”。

只需通过将汇总作为子查询并在主选择中执行 ifnulls 来解决此问题...重复选择很烦人,但它有效!

例如上面的例子

SELECT 
  IFNULL(`DEALER`, 'GROUP') AS DEALER,
  IFNULL(`SERVICE_ADVISOR`, 'TOTAL') AS SERVICE_ADVISOR,
  `COMPLETED`,
  /* .......... */
FROM (SELECT 
        usergroups.name AS DEALER,
        users.name AS SERVICE_ADVISOR,
        COUNT(DISTINCT vcrs.uid) AS COMPLETED,
      /* .......... */
      AND       vcrs.vcrSubStatus = 4
      GROUP BY  DEALER, SERVICE_ADVISOR with ROLLUP);
Run Code Online (Sandbox Code Playgroud)


fan*_*nts 3

你想要这样的东西吗?

SELECT COALESCE(usergroups.name, 'GROUP') AS DEALER,
COALESCE(users.name, IF(usergroups.name IS NULL, 'TOTAL', 'SUBTOTAL')) AS SERVICE_ADVISOR,
COUNT(DISTINCT vcrs.uid) AS COMPLETED,
..........
..........
AND vcrs.vcrSubStatus = 4
GROUP BY DEALER, SERVICE_ADVISOR with ROLLUP;
Run Code Online (Sandbox Code Playgroud)

测试:

mysql;root@localhost(playground)> select * from t;
+------+----------+-------+--------+
| id   | car      | state | tstamp |
+------+----------+-------+--------+
|    1 | toyota   | new   |   1900 |
|    2 | toyota   | old   |   1950 |
|    3 | toyota   | scrap |   1980 |
|    4 | mercedes | new   |   1990 |
|    5 | mercedes | old   |   2010 |
|    6 | tesla    | new   |   2013 |
+------+----------+-------+--------+
6 rows in set (0.04 sec)

mysql;root@localhost(playground)> select car, sum(tstamp) from t group by car with rollup;
+----------+-------------+
| car      | sum(tstamp) |
+----------+-------------+
| mercedes |        4000 |
| tesla    |        2013 |
| toyota   |        5830 |
| NULL     |       11843 |
+----------+-------------+
4 rows in set (0.03 sec)

mysql;root@localhost(playground)> select coalesce(car, 'huhu'), sum(tstamp) from t group by car with rollup;
+-----------------------+-------------+
| coalesce(car, 'huhu') | sum(tstamp) |
+-----------------------+-------------+
| mercedes              |        4000 |
| tesla                 |        2013 |
| toyota                |        5830 |
| huhu                  |       11843 |
+-----------------------+-------------+
4 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)