2009-03-18 2 views
15

У меня есть два запроса. Один из них имеет смысл для меня, а другой нет. Первый из них:MySQL: Total GROUP BY WITH ROLLUP Любопытство

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 

Тот же результат. Но почему я вообще получаю Узбекистан?

+0

Насколько уверены, что проблема заключается не в том, как отображаются результаты? –

+0

это происходит как в моем коде, так и в phpmyadmin. хотел бы попробовать протестировать его с помощью обычного браузера запросов mysql или чего-то еще, но у вас нет доступа. – Svish

+0

Я просто играл с Rollup тоже. Я думаю, проблема здесь в том, что вы присоединяетесь к идентификатору страны к имени страны, поэтому все значения NULL не делают этого соединения и по какой-то причине сохраняют последнее значение имени вместо того, чтобы не иметь имени. – sphism

ответ

36

Но почему я получаю Узбекистан на общую сумму ??

Потому что вы НЕ ВЫБРАТЬ элемент, который вы GROUPING BY. Если вы сказали:

GROUP BY c.printable_name 

Вы получите ожидаемый NULL. Однако вы группируете другой столбец, поэтому MySQL не знает, что имя_таблицы принимает участие в группе rollup, и выбирает любое старое значение из этого столбца, в объединении все регистрации. (Таким образом, вы можете увидеть другие страны, кроме Узбекистана.)

Это часть более широкой проблемы с тем, что MySQL разрешима тем, что вы можете выбрать в запросе GROUP BY. Например, вы можете сказать:

SELECT gender FROM registrations GROUP BY country; 

и MySQL будет счастливо выбрать один из гендерных ценностей для регистрации от каждой страны, несмотря на то, что нет прямой причинно-следственной связи (ака «функциональной зависимости») между страной и пола , Другие СУБД откажется выше команды на том основании, что не гарантировано быть одного пола в стране (*)

Теперь это:.

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 как ПЕРВИЧНЫЙ КЛЮЧ).

Однако расширение MySQL WITH ROLLUP является чем-то вроде взлома в способе его работы. На этапе свертки в конце он запускается по всему набору результатов предварительной группировки, чтобы захватить его значения, а затем устанавливает столбец «по группам» в NULL. Он также не содержит нулевые столбцы, которые имеют функциональную зависимость от этого столбца. Это, вероятно, должно, но MySQL в настоящее время не совсем понимает всю суть функциональных зависимостей.

Так что, если вы выберете c.printable_name, он покажет вам, какое значение имени страны выбрано в случайном порядке, и если вы выберете c.country_id покажет вам, какой из идентификаторов страны он выбрал случайно - хотя c.country_id является критерием соединения, поэтому он должен быть таким же, как r.country, что равно NULL!

Что вы можете сделать, чтобы работать вокруг проблемы:

  • группа по printable_name вместо; должно быть ОК, если имена для печати уникальны, или
  • выберите «r.country», а также имя для печати, и убедитесь, что для NULL или
  • забудьте WITH ROLLUP и выполните отдельный запрос для конечной суммы. Это будет немного медленнее, но также будет совместимым с ANSI SQL-92, чтобы ваше приложение могло работать с другими базами данных.

(*: MySQL имеет возможность ONLY_FULL_GROUP_BY sql_mode, который должен решить эту проблему, но она идет слишком далеко и только позволяет выбрать столбцы из GROUP BY, а не столбцы, которые имеют функциональную зависимость от GROUP BY. так что он будет делать действительные запросы не так хорошо, что делает его вообще бесполезно.)

+0

+1 потрясающая информация – diEcho

0

Coz, когда вы используете метод JOIN, следующий элемент NULL массива будет иметь значение предыдущего элемента NOT NULL. Но я не уверен. Это мой опыт, когда я использую его в PHP.

hm ... есть еще одна проблема ... «Страна» canot, потому что это имя таблицы. Так что измените что-нибудь еще. Тогда в последнем результате будет отображаться 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 />"; 
} 
+0

Могу ли я настроить запрос для работы одинаково, но с NULL, как общая вещь, как в другой? – Svish

+0

Это просто странно ... Я не понимаю этого, ха-ха. поэтому, когда я использую AS 'countryp' и GROUP BY countryp, он работает. Когда я использую «имя», это не так. Когда я использую «qe», он делает ... – Svish

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 ', и будет последняя строка/запись.