MySQL: Total GROUP BY WITH ROLLUP любопытство - PullRequest
18 голосов
/ 18 марта 2009

У меня два запроса. Один из них имеет смысл для меня, другой нет. Первый:

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 ]

37 голосов
/ 19 марта 2009

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

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

GROUP BY c.printable_name

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

Это часть более широкой проблемы, связанной с тем, что 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 вместо; должно быть в порядке, если printable_names уникальны, или
  • выберите «r.country», а также printable_name и проверьте, что это NULL, или
  • забудьте WITH ROLLUP и выполните отдельный запрос для конечной суммы. Это будет немного медленнее, но также будет совместимо с ANSI SQL-92, чтобы ваше приложение могло работать с другими базами данных.

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

0 голосов
/ 13 декабря 2017
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;

Это вывело бы 100 Total Registration = 39 ’и будет последней строкой / записью.

0 голосов
/ 18 марта 2009

Поскольку при использовании метода 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 />";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...