Adr*_*ach 6 mysql null group-by
我有下表(在 SQL Fiddle 上查看)(我创建是为了解决我的问题):
| ID | word |
----------------
| 5 | "Hello" |
| 6 | NULL |
| 7 | "World" |
| 8 | "World" |
Run Code Online (Sandbox Code Playgroud)
现在我想使用GROUP BY word WITH ROLLUP. ROLLUP 生成的行的列 word 中的 NULL 应替换为“total”:
SELECT
ID,
ifnull(word, "total") as word,
count(*) as occurrences
FROM test
GROUP BY word WITH ROLLUP;
Run Code Online (Sandbox Code Playgroud)
问题是它还会用NULL单词为 NULL 的行数替换记录中的 :
| ID | word | occurrences |
|----|-------|-------------|
| 6 | total | 1 | <- Here lies the problem
| 5 | Hello | 1 |
| 7 | world | 2 |
| 7 | total | 4 |
Run Code Online (Sandbox Code Playgroud)
所以我现在count(word)用来区分它是我的数据中的 NULL 还是 ROLLUP 创建的 NULL 以便我可以用“空”或“总计”替换它:
SELECT
ID,
if(count(word) = 0, "empty", ifnull(word, "total")) as word,
count(*) as occurrences
FROM test
group by word with ROLLUP;
Run Code Online (Sandbox Code Playgroud)
这适用于我的用例,但它仍然有一个缺陷:如果单词在所有行中都是 NULL,count(word)则在显示总数的最后一行中也是 0:( SQL Fiddle with different data (only record#6) )
| ID | word | occurrences |
|----|-------|-------------|
| 6 | empty | 1 |
| 6 | empty | 1 | <- word should be "total"
Run Code Online (Sandbox Code Playgroud)
以下语句提供了我想要的结果:
SELECT
ID,
ifnull(word, "total") as word,
count(*) as occurrences
FROM
(
SELECT
ID,
ifnull(word, "empty") as word
FROM test
) tmp
GROUP BY word WITH ROLLUP
Run Code Online (Sandbox Code Playgroud)
但是因为在这里的大多数答案中似乎不鼓励使用子查询,所以我想知道是否有更好的解决方案。
你有一个“解决方案”,所以说
我想知道是否有更好的解决方案。
我想你想对此发表评论。我重点讲几个方面:
SELECT ID [...] GROUP BY word,无论是否有 aWITH ROLLUP都是错误的。您正在选择一个值未确定的字段。特别是,使用 MySQL,您会遇到返回随机值的情况;或者如果您使用严格模式(推荐,并且在最新版本的 MySQL 中默认),查询将失败:“db_9_b0ff7.test.ID”不在 GROUP BY 中
无论正式与否,WITH ROLLUP注意-(我指的是 MySQL 实现)在某些情况下是很好的,因为它避免了对表的双重重新扫描,但我在生产代码中还没有看到很多。这没有什么问题,但它们是 MSSQL 独有的功能,直到它在 SQL1999 上实现为止,并且要么在当时没有广泛使用,要么像MySQL 一样,它是一个有限的实现。参考: https: //www.percona.com/blog/2007/09/17/using-group-by-with-rollup-for-reporting-performance-optimization/
原则上我没有看到你的最终查询有问题,如果我们对两个查询(没有 ID)运行解释,我们会看到:
MariaDB [test]> EXPLAIN SELECT if(count(word) = 0, "empty", ifnull(word, "total")) as word, count(*) as occurrences FROM test group by word with ROLLUP\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: test
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4
Extra: Using filesort
1 row in set, 1 warning (0.00 sec)
Warning (Code 1052): Column 'word' in group statement is ambiguous
MariaDB [test]> EXPLAIN SELECT ifnull(word, "total") as word, count(*) as occurrences FROM ( SELECT ifnull(word, "empty") as word FROM test ) tmp GROUP BY word WITH ROLLUP\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: test
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4
Extra: Using filesort
1 row in set, 1 warning (0.01 sec)
Run Code Online (Sandbox Code Playgroud)
通常对 MySQL 的憎恨是因为它对 MySQL 早期版本的限制。我并不是说它现在已经很完美了,仍然会有问题,因为 MySQL 的开发一如既往,专注于快速、易入、易出的 RDBMS,而不是成熟的关系引擎,但是在正确的位置使用子查询是可以的(毕竟,无论如何,有些查询都需要子查询。您的用法是可以的- 它只会产生再次遍历汇总行的开销,我想这是真正的交易,与全套相比,这些并不重要。
注意-在我的实例上,子查询没有显示,但您可以在 5.6 中通过具体化子查询看到它:http://sqlfiddle.com/#! 9/b0ff7/7(它将执行内部查询,然后外部部分,没有问题)。索引绝对可以在这里提供帮助(由于文件排序),但这取决于最终的记录数。如果索引(现在不存在)无法使用,则子查询可能会损害性能。您必须测试您的特定 mysql 版本并创建这样的索引。
现在最后一个问题是,是否可以在 MySQL 上不使用子查询来完成此操作,并且它会更好/不那么难看吗?我真的没有看到一种方法- 我们可以进行连接,但它仍然是子查询。我可以想到一个替代方案,那就是:
SELECT * FROM (
SELECT word,
count(*) as occurrences
FROM test
GROUP BY word with rollup)
tmp
ORDER BY occurrences;
Run Code Online (Sandbox Code Playgroud)
这确保最后一行包含摘要(如果有其他空值,则它是实际的空值)。即使在只有空值的情况下也会发生该顺序。相反,它可能会更慢,因为它需要排序,并且您可能再次需要一个索引,并且该索引可能会也可能不会被使用。