13

我尝试使用 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;

输出:

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

期望的输出:

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
4

4 回答 4

8

要解决COALESCE/IFNULL仍然返回NULL占位符的问题WITH ROLLUP,您需要GROUP BY表列名称,而不是别名列表达式。

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

示例 DB-Fiddle

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

INSERT INTO foo
  (`amount`, `created`)
VALUES
  ('1', '2019'),
  ('2', '2019');

查询 #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       |                  |

查询 #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            |

特定用例

示例 DB-Fiddle

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;

查询 #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         |

查询 #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         |

注意事项

  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
    
  2. COALESCE, IFNULL, IF(... IS NULL)CASE WHEN ... IS NULL 都将具有类似的功能。哪里IFNULL是 MySQL 专有的,是COALESCE. AsCOALESCE可以接受两个以上的参数进行检查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
    
  3. 中的可空列GROUP BY作为别名或列名,将导致NULL值显示为WITH ROLLUP占位符。这适用WITH ROLLUP于一般使用。例如 if users.name可以返回NULL. DB-小提琴

    | DEALER | SERVICE_ADVISOR | COMPLETED | 
    | ------ | --------------- | --------- | 
    | Foo    | TOTAL           | 1         |
    | Foo    | Jane Doe        | 1         |
    | Foo    | John Doe        | 1         |
    | Foo    | TOTAL           | 3         |
    | GROUP  | TOTAL           | 3         |
    

防止NULL显示列值

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

示例 DB-Fiddle

SELECT
    COALESCE(usergroups.name, 'GROUP') AS DEALER,
    COALESCE(users.name, 'TOTAL') AS SERVICE_ADVISOR,
    COUNT(DISTINCT vcrs.uid) AS COMPLETED
FROM vcrs
LEFT JOIN users
ON users.id = vcrs.uid
LEFT JOIN usergroups
ON usergroups.id = users.group_id
WHERE vcrs.vcrSubStatus = 4
AND users.name IS NOT NULL
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         |

由于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;

包括NULL列值

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

示例 DB-Fiddle

SELECT 
    COALESCE(v.usergroup_name, 'GROUP') AS DEALER,
    COALESCE(v.user_name, 'TOTAL') AS SERVICE_ADVISOR,
    COUNT(DISTINCT v.uid) AS COMPLETED
FROM (
  SELECT
      usergroups.name AS usergroup_name,
      COALESCE(users.name, 'NULL') AS user_name,
      vcrs.uid
  FROM vcrs
  LEFT JOIN users
  ON users.id = vcrs.uid
  LEFT JOIN usergroups
  ON usergroups.id = users.group_id
  WHERE vcrs.vcrSubStatus = 4
) AS v
GROUP BY v.usergroup_name, v.user_name
WITH ROLLUP;

结果

| DEALER | SERVICE_ADVISOR | COMPLETED |
| ------ | --------------- | --------- |
| Foo    | Jane Doe        | 1         |
| Foo    | John Doe        | 1         |
| Foo    | NULL            | 1         |
| Foo    | TOTAL           | 3         |
| GROUP  | TOTAL           | 3         |

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

SELECT
    COALESCE(v.usergroup_name, 'GROUP') AS DEALER,
    CASE v.user_name WHEN 'NULL' THEN NULL ELSE COALESCE(v.user_name, 'TOTAL') END AS SERVICE_ADVISOR,
    COUNT(DISTINCT v.uid) AS COMPLETED
FROM (
    /* ... */
) AS v
GROUP BY v.usergroup_name, v.user_name
WITH ROLLUP;
于 2019-09-20T20:31:40.577 回答
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);
于 2016-10-13T16:00:17.400 回答
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;

测试:

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)
于 2014-08-22T09:47:18.760 回答
0

您正在寻找的是案例陈述。您的意思是在某种条件下将找到的值替换为指定的值。您可以使用多个 when/then 语句,具体取决于您希望替换的自定义程度。

select IFNULL(usergroups.name, 'GROUP') AS DEALER,
case when(users.name is null) then 'TOTAL' else users.name end AS SERVICE_ADVISOR,
COUNT(DISTINCT vcrs.uid) AS COMPLETED,
..........
..........
and vcrs.vcrSubStatus = 4
group by DEALER, SERVICE_ADVISOR with ROLLUP;
于 2016-01-04T13:28:11.643 回答