MySQL:总计GROUP BY WITH ROLLUP好奇心

时间:2009-03-18 19:50:58

标签: mysql

我有两个问题。其中一个对我有意义,另一个则没有。第一个:

SELECT gender AS 'Gender', count(*) AS '#'
    FROM registrations 
    GROUP BY gender WITH ROLLUP

这给了我这个:

Gender       #
Female      20
Male        19
NULL        39

所以,我得到了计数和总数。我的期望。下一个:

SELECT c.printable_name AS 'Country', count(*) AS '#' 
    FROM registrations r 
    INNER JOIN country c ON r.country = c.country_id 
    GROUP BY country WITH ROLLUP

Country         #
Denmark         9
Norway         10
Sweden         18
United States   1
Uzbekistan      1
Uzbekistan     39

结果相同。但为什么我要把乌兹别克斯坦作为总数?

3 个答案:

答案 0 :(得分:37)

  

但为什么我要获得乌兹别克斯坦的总数?

因为您没有选择您正在进行GROUPING BY的项目。如果你说:

GROUP BY c.printable_name

你会得到预期的NULL。但是,您要按其他列进行分组,以便MySQL不知道printable_name正在参与汇总组,并在所有注册的连接中选择该列中的任何旧值。 (所以你可能会看到除乌兹别克斯坦之外的其他国家。)

这是更广泛问题的一部分,MySQL允许你在GROUP BY查询中选择SELECT。例如,您可以说:

SELECT gender FROM registrations GROUP BY country;

并且MySQL会很乐意为每个国家/地区的注册选择一个性别值,即使国家和性别之间没有直接的因果关系(也就是“功能依赖”)。其他DBMS将拒绝上述命令,理由是每个国家不保证一个性别。(*)

现在,这个:

SELECT c.printable_name AS 'Country', count(*) AS '#' 
FROM registrations r 
INNER JOIN country c ON r.country = c.country_id 
GROUP BY country

没问题,因为r.country和c.printable_name之间存在功能依赖关系(假设您已正确将country_id描述为PRIMARY KEY)。

然而,MySQL的WITH ROLLUP扩展在其工作方式上有点像黑客。在最后的汇总行阶段,它在整个预分组结果集上运行以获取其值,然后将group-by列设置为NULL。 它也不会使其他列对该​​列具有功能依赖性。它可能应该这样,但MySQL目前并不真正了解有关函数依赖的所有内容。

因此,如果您选择c.printable_name,它将显示它随机选择的任何国家/地区名称值,如果您选择c.country_id,它将显示它随机选择的任何国家/地区ID - 即使c.country_id是加入条件,所以必须和r.country一样,这是NULL!

解决问题的方法是:

  • 由printable_name分组;如果printable_names是唯一的,或者
  • ,则应该没问题
  • 选择“r.country”以及printable_name,并检查是否为NULL或
  • 忘记WITH ROLLUP并对结束总和进行单独查询。这会慢一些,但它也符合ANSI SQL-92,因此您的应用程序可以在其他数据库上运行。

(*:MySQL有一个SQL_MODE选项ONLY_FULL_GROUP_BY,它应该解决这个问题,但它太过分了,只允许你从GROUP BY中选择列,而不是选择具有功能依赖性的列。 GROUP BY。因此它会使有效查询失败,使其通常无用。)

答案 1 :(得分:0)

当您使用JOIN方法时,数组的以下NULL元素将具有先前NOT NULL元素的值。但我不确定。这是我在PHP中使用它时的经验。

嗯...还有另一个问题...... '国家'因为它是表格的名称。因此改变别的东西。然后最后的结果将显示NULL。 这是我的建议:

$result = mysql_query("SELECT c.printable_name AS 'countryp', count(*) AS '#'
FROM registrations r, country c WHERE r.country = c.country_id
GROUP BY countryp WITH ROLLUP");

while($row = @mysql_fetch_array($result)) {
  $r1 = $row["countryp"];
  $r2 = $row["#"];
  if ($r1 == NULL) $r1 = 'Total';
  echo "$r1 $r2<br />";
}

答案 2 :(得分:0)

SELECT ifnull(c.printable_name, "Total Registration = ") AS 'Country', count(*) AS '#' 
FROM registrations r 
INNER JOIN country c ON r.country = c.country_id 
GROUP BY country WITH ROLLUP;

这将打印'总注册数= 39 ',并且将是最后一行/记录。