← Все курсы · Курс SQL · Все руководства

GROUP BY и HAVING: как суммировать строки, не получая ложного ответа

Одна строка на группу и фильтр, который должен выполняться после · Написано для прослушивания · Часть Analyst Prep Kit

К , аналитик данных · Опубликовано

К концу этой страницы вы сможете написать сводный запрос и быть уверенным, что ответ на него реален. Вы будете знать, что именно GROUP BY делает с вашими строками, какие столбцы вам разрешено выбирать впоследствии и почему, где WHERE идет, куда HAVING идет, и почему их замена — это разница между находкой и числом, которое ничего не значит. Это около двадцати пяти минут.

Вот что на самом деле с этим делать. В следующий сводный запрос, который вы напишете, добавьте одну строку, устанавливающую минимальный размер группы, прежде чем читать рейтинг. Одна строка, и она устраняет наиболее распространенный способ, которым сводный запрос дает уверенный неправильный ответ.

Краткая версия: WHERE фильтрует строки перед группировкой. HAVING фильтрует группы после. Без HAVING На первом этаже крошечные группы занимают верхние строчки каждого рейтинга.

Здесь одна идея решает все остальное, поэтому она и дает представление о картине. Группировка происходит в середине запроса, а два фильтра располагаются по разные стороны от него.

Where the two filters sit A left-to-right pipeline in four stages. Stage one is a column of eight individual row boxes. Stage two is a gate labelled WHERE, through which six rows pass and two are crossed out and stopped. Stage three shows the surviving six rows collapsing into three group boxes, one holding three rows, one holding two rows, and one holding a single row. Stage four is a second gate labelled HAVING, through which the group of three and the group of two pass, while the group holding only one row is crossed out and stopped. The result at the far right is two groups. The picture shows that WHERE acts on individual rows before any grouping exists, and HAVING acts on whole groups after they have been formed, which is why the two filters cannot be swapped. rows WHERE groups HAVING ✕ ✕ ✕ The two gates are on opposite sides of the grouping step. That is the whole reason they are not interchangeable.
WHERE действует на строки, которые все еще существуют индивидуально. HAVING действует на группы, которые существуют только после группировки.
Что вы узнаете
  1. Что GROUP BY на самом деле делает с вашими строками
  2. Какие столбцы вы можете выбрать, и правило, лежащее в основе этого
  3. ГДЕ против ИМЕЮЩИХ, и почему приказ вынужденный
  4. Проблема малых групп и линия, которая ее решает
  5. Полное до и после
  6. Пограничные случаи, которые нарушают сводные запросы в реальных данных
  7. Почему это работает
  8. Использование этого в вашем собственном проекте
  9. Все на одном экране
Проработанный пример реальный. Все цифры на этой странице взяты из опубликованного портфолио проекта: 82 956 игр из каталога Пар с количеством обзоров, рейтингами и жанрами. Запросы выполняются к полному набору данных по адресу Скрытые сокровища Пар на GitHub. Если SELECT и WHERE также новые, начните с Основы SQL и вернись.

1. Что GROUP BY на самом деле делает с вашими строками

Перед пояснением: в вашей таблице 82 956 строк. Вы группируете его по жанрам и получаете обратно 28 строк. Куда делись остальные 82 928 строк и можно ли их еще увидеть?

Вот правило. GROUP BY сортирует каждую строку в сегмент, затем заменяет каждый блок ровно одной выходной строкой. Ничего не удаляется. Ряды все еще там, рухнувшие. Что меняется, так это то, что означает одна строка вашего результата.

В этом последнем предложении и заключается вся идея. До группировки один ряд был одной игрой. После группировки по жанрам одна строка — это один жанр. Слово для этого grainи произнесение новой детали вслух, прежде чем писать что-либо еще, предотвращает большинство ошибок на этой странице.

Агрегатная функция позволяет получить значение обратно из свернутого сегмента. COUNT(*) спрашивает, сколько строк в этом ведре. AVG(PctPositive) запрашивает среднее значение этого столбца в ведре. SUM, MIN и MAX работать так же. Каждый из них принимает множество значений и возвращает одно, а это именно то, что нужно для одной выходной строки.

SELECT PrimaryGenre,
       COUNT(*) AS games,
       ROUND(AVG(PctPositive), 1) AS avg_rating
FROM games
GROUP BY PrimaryGenre;

Это возвращает 28 строк, по одной на каждый жанр. На счету Действие 35 149 игр и средний показатель положительных результатов 75,4%. Приключение имеет 17 486 и составляет в среднем 77,7%. Все 82 956 исходных строк все еще учтены, поскольку каждая игра попала ровно в одну корзину.

Назовите новое уровень детализации, прежде чем писать совокупность. «Одна строка на жанр» сразу сообщает вам, что индивидуальный рейтинг игры больше не доступен и что все, что вы выбираете, должно относиться ко всему жанру.

2. Какие столбцы можно выбирать и какое правило стоит за этим

Перед пояснением: группируете по жанрам, а также выбираете название игры. База данных либо выдаёт ошибку, либо выдает вам одно имя из 35 149. Какое имя оно должно вам дать?

Разумного ответа нет, поэтому и существует правило. После группировки каждый столбец в вашем SELECT должно быть одно из двух.

Все остальное — это столбец с 35 149 различными значениями и одним слотом для их размещения. Большинство баз данных отказываются. PostgreSQL и SQL Server выдают ошибку при присвоении имени столбцу. Настройки SQLite и старых MySQL делают нечто худшее: они выбирают значение произвольно и возвращают его без комментариев, поэтому вы получаете реально выглядящее имя, привязанное к среднему значению по жанру.

-- Errors on PostgreSQL, returns a misleading row on SQLite
SELECT PrimaryGenre, Name, AVG(PctPositive)
FROM games
GROUP BY PrimaryGenre;

Прежде чем читать дальше, скажите своими словами, почему на этот вопрос нет правильного ответа. Если вы сможете указать причину, сообщение об ошибке больше никогда вас не смутит.

Причина в том, что Name находится не в том зерне. Он описывает одну игру, а строка теперь описывает жанр. Если вы действительно хотите, чтобы в каждой группе было первое имя, это другой инструмент, и это предмет Оконные функции.

Группировка по нескольким столбцам работает таким же образом, но с более мелким сегментом. GROUP BY PrimaryGenre, ReleaseYear составляет одну строку для каждого жанра в год, и тогда оба этих столбца можно безопасно выбрать.

3. ГДЕ против ИМЕТЬ, и почему приказ вынужденный

Перед пояснением: вам нужны жанры со средним рейтингом выше 80. Попробуйте поставить это условие в WHERE и база данных отказывается. Почему это не может просто решиться?

Потому что когда WHERE работает, среднего еще не существует. Это не стилевое предпочтение. Это порядок, в котором база данных оценивает предложения, и этот порядок фиксирован.

ПорядокПунктЧто он может видеть
1FROMСтолы
2WHEREОтдельные строки. Никаких агрегатов пока не существует.
3GROUP BYСортирует оставшиеся строки в сегменты.
4HAVINGЦелые группы и их совокупности
5SELECTСтроит выходную строку для каждой группы
6ORDER BYСортирует готовый результат

Так WHERE COUNT(*) >= 500 запрашивает у второго шага число, которое придумывает четвертый шаг. Ошибка заключается в том, что база данных сообщает вам правду о своей последовательности.

Практическая версия короткая. Если ваше условие составляет около одной строки, оно входит в WHERE. Если речь идет о целой группе, то она входит HAVING.

SELECT PrimaryGenre,
       COUNT(*) AS games,
       ROUND(AVG(PctPositive), 1) AS avg_rating
FROM games
WHERE TotalReviews >= 500        -- about one game
GROUP BY PrimaryGenre
HAVING COUNT(*) >= 500           -- about a whole genre
ORDER BY avg_rating DESC;

Оба фильтра в одном запросе и выполняют разную работу. WHERE линия говорит, что игре нужно достаточное количество обзоров, чтобы ее собственный рейтинг мог что-то значить, в результате чего каталог упал с 82 956 игр до 10 507. HAVING линия говорит, что жанру нужно достаточно игр, чтобы его средний показатель мог что-то значить.

Предпочитайте ГДЕ, когда у вас есть выбор. Ранняя фильтрация означает меньшее количество строк для сортировки по сегментам, поэтому запрос выполняется быстрее. Использовать HAVING для условий, которые действительно невозможно проверить, пока группа не существует.

4. Проблема малых групп и способ ее решения

Перед пояснением: как вы думаете, какой жанр в каталоге из 82 956 игр имеет самый высокий средний рейтинг? Прежде чем прочитать ответ, сделайте предположение.

Это бухгалтерский учет, на 79,5% положительный. Впереди приключения, впереди РПГ, впереди все. Запрос правильный, данные реальные, а ответ ничего не стоит, потому что во всем каталоге одиннадцать бухгалтерских игр.

Вот рейтинг без пола.

SELECT PrimaryGenre,
       COUNT(*) AS games,
       ROUND(AVG(PctPositive), 1) AS avg_rating
FROM games
GROUP BY PrimaryGenre
ORDER BY avg_rating DESC;
ЖанрИгрыСредний рейтинг
Бухгалтерский учет1179.5
Разработка игр779.1
Приключение17,48677.7
Редактирование фотографий2177.2
Повседневный15,00776.9

Три из пяти первых категорий — это категории, содержащие менее 25 позиций. Это не невезение. Это арифметика: среднее значение более одиннадцати значений движется свободно, а среднее значение более семнадцати тысяч почти не движется вообще. Таким образом, крайние точки любого рейтинга каждый раз заполняются наименьшими группами в обоих направлениях.

Одна строка исправляет это.

SELECT PrimaryGenre,
       COUNT(*) AS games,
       ROUND(AVG(PctPositive), 1) AS avg_rating
FROM games
GROUP BY PrimaryGenre
HAVING COUNT(*) >= 500
ORDER BY avg_rating DESC;
ЖанрИгрыСредний рейтинг
Приключение17,48677.7
Повседневный15,00776.9
RPG1,32676.2
Стратегия1,22775.8
Инди7,98275.6
Действие35,14975.4

На этом этаже были удалены 20 из 28 жанров и создан первый рейтинг, на который можно было опереться. Спред также рухнул с вершины 79,5 до 77,7, что является честной картиной: жанры гораздо более похожи, чем предполагалось в первой таблице.

Число 500 — это суждение, а не факт, и оно должно быть оправдано. Выбирайте его на основании данных, а не по вкусу, запишите почему и проверьте, выдержит ли рейтинг другой выбор. Выбор порогов из данных рассказывает, как это сделать честно.

Теперь представьте себе последний отправленный вами сводный запрос. Если вы добавили HAVING COUNT(*) >= 30 к этому, изменится ли верхняя часть результата? Это основа данного руководства, и для большинства реальных отчетов ответ — да.

5. Полное до и после

Та же таблица, тот же вопрос: какие жанры игроки оценивают выше всего?

До

SELECT PrimaryGenre, AVG(PctPositive) AS avg_rating
FROM games
GROUP BY PrimaryGenre
ORDER BY avg_rating DESC;

Три проблемы, ни одна из которых не приводит к ошибке. Игры с тремя обзорами засчитываются так же, как игры с тридцатью тысячами. Жанры с одиннадцатью играми считаются так же, как жанры с тридцатью пятью тысячами. И читатель не видит ни одной проблемы, потому что размеров групп нет на экране.

после

-- ============================================================
-- STEP 3: Which genres do players actually rate highest?
-- WHY: A rating is only meaningful if enough people gave one,
--      and a genre average is only meaningful if the genre has
--      enough games. Both floors are here for that reason.
-- COVERAGE: 10,507 of 82,956 games have 500+ reviews. Eight of
--      28 genres clear the 500-game floor. The other 20 are
--      reported separately, not silently dropped.
-- ============================================================
--SELECT the genre, how many games are behind the number,
--   and their average positive rating:
--   COUNT(*)          = games in this genre that passed WHERE
--   AVG(PctPositive)  = mean positive rating across them
--FROM the full game catalogue
--WHERE the game itself has at least 500 reviews
--GROUP BY genre, so each genre collapses to one row
--HAVING at least 500 such games, so the average is stable
--ORDER BY average rating, highest first
SELECT PrimaryGenre,
       COUNT(*) AS games,
       ROUND(AVG(PctPositive), 1) AS avg_rating
FROM games
WHERE TotalReviews >= 500
GROUP BY PrimaryGenre
HAVING COUNT(*) >= 500
ORDER BY avg_rating DESC;

Размер группы теперь отображается в виде видимого столбца, поэтому читатель может самостоятельно оценить среднее значение. Оба этажа указаны не без причины. В строке покрытия указано, что было исключено. Формат комментариев тот же, что и у Как комментировать SQL, чтобы он обучал.

6. Крайние случаи, которые нарушают сводные запросы в реальных данных

Перед объяснением: жанр, о существовании которого вы знаете, полностью отсутствует в вашем сгруппированном результате. Не ноль. Отсутствующий. Что бы вы проверили в первую очередь?

Шесть вещей, каждая из которых стоит кому-то дня.

A WHERE фильтр очистил группу. Это ответ на предварительный вопрос. Группировка может создавать сегмент только для сохранившихся строк. WHERE. Если каждая игра определенного жанра имеет менее 500 обзоров, в этом жанре нет строк, которые можно было бы свернуть, и вообще нет выходной строки. Группа, которая должна читать ноль, вместо этого невидима, что является другой и более вводящей в заблуждение вещью.

COUNT(*) и COUNT(column) не одинаковы. COUNT(*) считает строки. COUNT(column) подсчитывает строки, где этого столбца нет NULL. В столбце с пропущенными значениями возвращаются разные числа, и только одно из них отвечает на ваш вопрос. СЧЕТ в SQL проходит всю семью.

NULL становится отдельной группой. Строки с отсутствующим жанром сворачиваются в одну корзину, которая обычно печатается как пустая. Это похоже на ошибку форматирования, и на самом деле это подсчет недостающих данных, которые стоит прочитать, а не скрывать.

Агрегаты пропускают NULL, и это перемещает ваше среднее значение. AVG делится на количество ненулевых значений, а не на количество строк. Таким образом, среднее значение по столбцу, пустому на 30 %, представляет собой среднее значение для 70 % ответивших, а это совсем другое утверждение.

Вы не можете использовать SELECT Псевдоним в HAVING повсюду. HAVING avg_rating > 80 работает в MySQL и SQLite и не работает в PostgreSQL и SQL Server, потому что SELECT бежит за HAVING. Повторяю полностью AVG(PctPositive) > 80 работает везде.

Группировка после объединения учитывает дубликаты. Если соединение умножило ваши строки, COUNT(*) теперь считает пары, а не вещи. COUNT(DISTINCT id) обычно это то, что вы имели в виду. Это наиболее распространенная неправильная цифра в реальной отчетности. SQL-СОЕДИНЕНИЯ объясняет, почему это происходит.

Почему это работает

Порядок предложений не является соглашением, выбранным базой данных. Группировка определяется как разделение строк на непересекающиеся наборы, а агрегат — это функция от набора к одному значению. Реляционная модель Кодда определяет операции именно над отношениями, а не над отдельными записями (Кодд, 1970, Коммуникации ACM, 13(6), 377–387). Если группировка представляет собой раздел, фильтр группы не может работать до того, как раздел существует, а столбец на уровне строки не может сохраниться в строке, представляющей набор. Оба правила на этой странице выпадают из этого, а не закреплены болтами.

Проблема малых групп старше SQL и на самом деле не является ошибкой запроса. Любое ранжирование средних значений помещает наименьшие выборки в обе крайности, поскольку дисперсия среднего значения падает по мере роста выборки. Гельман и Прайс показывают, что это приводит к тому, что карты и рейтинговые таблицы систематически вводят в заблуждение, и что от этого не ускользает простое ранжирование оценок (Гельман & Цена, 1999, Статистика в медицине, 18(23), 3221–3234). Одиннадцать бухгалтерских игр дают именно такой эффект. А HAVING пол — это грубое решение, и оно подходит для отчета.

Есть и вторая причина, по которой эта страница постоянно просит вас угадать, прежде чем она ответит. Если вам задают вопрос до того, как вам дадут ответ, это улучшает память на эту конкретную информацию, даже если предположение неверно, и эффект надежен в большом количестве исследований (Бисра, Лю, Несбит, Салими и Винн, 2018, Обзор педагогической психологии, 30(3), 703–725). Ответ остается неизменным, если вы угадаете, какой жанр занял первое место.

Использование этого в вашем собственном проекте

Подумайте о резюме, которое вы уже кому-то отправили. Можете ли вы сказать, сколько записей находится за верхним рядом? Если вы не можете, это то, что нужно проверить в первую очередь.

Аудит каждого сгруппированного запроса сразу — жалкая работа, и вы остановитесь на полпути. Вместо этого сделайте это по порядку.

  1. Скажи вслух новое уровень детализации прежде чем писать запрос. «Одна строка на жанр». Пять секунд, и вы узнаете, какие столбцы являются допустимыми.
  2. Помещать COUNT(*) в каждом сгруппированном запросе, даже когда никто об этом не просил. Именно колонка позволяет читателю судить обо всем остальном, и это ничего не стоит.
  3. Добавить HAVING этаж, прежде чем прочитать рейтинг, а не после. Чтение нефильтрованной версии сначала дает вам ответ, от которого вы не захотите отказаться.
  4. Запишите пол и его причину в файл в качестве комментария в том же проходе. Это первый номер, который задает рецензент.
  5. Проверьте каждый неагрегированный столбец против GROUP BY список. Если столбец отсутствует ни в одном из этих мест, возможно, ваша база данных незаметно выбирает значение за вас.

Если у вас под рукой есть бумага и пять свободных минут, стоит сделать один рисунок, и он не является обязательным. Нарисуйте восемь рядов, вычеркните два, сложите оставшиеся шесть в три ведра, затем вычеркните ведро, в котором находится один ряд. Обозначьте два зачеркивания самостоятельно. Перерисовка этого по памяти — это и попытка извлечения, и проверка того, есть ли оно у вас.

Подробнее об этом и еще что-нибудь подобное. Все инструкции находятся в одном месте на индекс направляющих: SQL, Tableau, миграция данных и связанные с ними рабочие привычки.

Все на одном экране

Это лист поиска. Закройте правую колонку, проработайте левую и произнесите каждый ответ вслух, прежде чем проверять его.

концепцияЧто он делает
GROUP BYСортирует строки по сегментам, а затем возвращает по одной строке для каждого сегмента.
Уровень детализации после группировкиОдна строка на группу, а не одна строка на запись. Скажите это вслух.
Юридические колонкиТолько сгруппированные столбцы и агрегаты. Все остальное не в том направлении.
COUNT(*)Строки в сегменте, включая строки с нулями.
COUNT(col)Строки, в которых этот столбец не равен нулю. Часто другой номер.
COUNT(DISTINCT col)Разные ценности. Что вы обычно хотите после присоединения.
AVG, SUM, MIN, MAXМножество значений в одном значении. Все они пропускают нули.
WHEREФильтрует отдельные строки до создания какой-либо группы.
HAVINGФильтрует целые группы после их формирования. Единственный, который видит агрегат.
Порядок пунктовОТКУДА, ГДЕ, ГРУППИРУЙТЕ ПО, ИМЕЕТ, ВЫБИРАЙТЕ, Упорядочивайте.
Почему они не могут поменятьсяАгрегат не существует, когда WHERE бежит.
Псевдоним в HAVINGРаботает в MySQL и SQLite, не работает в PostgreSQL и SQL Server. Повторите выражение.
Проблема маленькой группыКрошечные группы заполняют оба конца каждого рейтинга средних показателей.
ИсправлениеA HAVING COUNT(*) этаж, выбранный из данных и записанный.
Отсутствует группаКаждая из его строк была удалена WHERE, поэтому ведро не образовалось.
NULL ГруппаСваливается в одно пустое на вид ведро. Это подсчет недостающих данных.
Группировка после объединенияCOUNT(*) считает пары, а не вещи. Использовать COUNT(DISTINCT id).
Единственная привычка, которую следует сохранить. Если вы больше ничего не берете с этой страницы, поставьте COUNT(*) в каждом сгруппированном запросе и установите минимальный уровень, прежде чем читать рейтинг. Неправильный ответ здесь никогда не выглядит неправильным. Похоже на результат. Если сводный запрос нарушается так, как это не описано на этой странице, существует общая шлейф диагностики на предмет застревания.

Еще одна мысль, и мне бы искренне хотелось услышать ответы других людей. Мои одиннадцать бухгалтерских игр стали явно глупыми, когда я увидел подсчет, но я увидел подсчет только потому, что включил его в запрос. Какая самая маленькая группа когда-либо возглавляла один из ваших чартов, и сколько времени вам понадобилось, чтобы это заметить?

Ссылки

Фильтр где-то принадлежит, и WHERE, и HAVING выглядят правдоподобно.

SQL для аналитиков составляет 458 страниц, GROUP BY и HAVING читаются построчно, включая порядок, в котором база данных выполняет действия.

SQL для аналитиков, 19 $ →
Попрактикуйтесь в группировке данных, находящихся перед вами.

тот Курс SQL запускает SQLite в браузере, поэтому вы пишете запрос, запускаете его и наблюдаете за коллапсом количества строк. Рядом с ним лежат рабочие примеры, карточки и пробный экзамен. Ничего устанавливать, учетная запись не требуется.

Откройте набор SQL →