ПРИСОЕДИНЯЙТЕСЬ к другому столу после GROUP BY и COUNT - PullRequest
2 голосов
/ 20 июня 2010

Я пытаюсь найти правильный способ использования JOIN, COUNT(*) и GROUP BY для выполнения довольно простого запроса. Я действительно заставил его работать (см. Ниже), но из того, что я прочитал, я использую дополнительные GROUP BY, которых у меня не должно быть.

(Примечание: проблема ниже не является моей реальной проблемой (которая имеет дело с более сложными таблицами), но я попытался найти аналогичную проблему)

У меня есть две таблицы:

Table: Person
-------------
key  name     cityKey
1    Alice    1
2    Bob      2
3    Charles  2
4    David    1

Table: City
-------------
key  name
1    Albany
2    Berkeley
3    Chico

Я бы хотел сделать запрос в People (с некоторым предложением WHERE), который возвращает

  • количество подходящих людей в каждом городе
  • ключ от города
  • название города.

Если я сделаю

SELECT COUNT(Person.key) AS count, City.key AS cityKey, City.name AS cityName
FROM Person 
LEFT JOIN City ON Person.cityKey = City.key 
GROUP BY Person.cityKey, City.name

Я получаю желаемый результат

count   cityKey   cityName
2       1         Albany
2       2         Berkeley

Однако , я прочитал , что добавление последней части предложения GROUP BY (City.name) просто для того, чтобы заставить его работать, неверно.

Так каков правильный способ сделать это? Я пытался найти ответ в Google, но я чувствую, что есть нечто фундаментальное, чего я просто не понимаю.

Ответы [ 4 ]

3 голосов
/ 20 июня 2010

... Я читал, что вставлять последнюю часть предложения GROUP BY (City.name) просто для того, чтобы заставить его работать, неправильно.

Вы неправильно поняли, вы получили его задом наперед.
Стандартный SQL требует , чтобы вы указали в GROUP BY все столбцы, упомянутые в SELECT, чтоне включаются в агрегатные функции.Если вам не нужны определенные столбцы в GROUP BY, оберните их в агрегатные функции.В зависимости от базы данных вы можете использовать аналитическую / оконную функцию OVER ...

Однако MySQL и SQLite предоставляют «функцию», позволяющую исключить эти столбцы из группы, что приводит кконец "почему этот порт не из MySQL в базу данных fill_in_the_blank ?!"Stackoverflow и множество других сайтов и форумов.

3 голосов
/ 20 июня 2010

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

select PC.ct, City.key, City.name
  from City
  join (select count(Person.key) ct, cityKey key from Person group by cityKey) PC
    on City.key = PC.key

, если мой SQL не слишком ржавый: -)

1 голос
/ 20 июня 2010

Тем не менее, я читал, что вбрасывание эта последняя часть предложения GROUP BY (City.name) просто чтобы это работало неправильно.

Это не так. Вы должны понимать, как Оптимизатор запросов видит ваш запрос. Порядок, в котором он анализируется, - это то, что требует, чтобы вы «добавили последнюю часть». Оптимизатор видит ваш запрос в чем-то похожем на этот порядок:

  • необходимые таблицы объединены
  • составной набор данных фильтруется с помощью предложения WHERE
  • остальные строки разбиваются на группы по предложению GROUP BY и агрегируются
  • затем они снова фильтруются через предложение HAVING
  • окончательно активируется с помощью SELECT / ORDER BY, UPDATE или DELETE.

Суть в том, что GROUP BY не должен называть все столбцы в SELECT, но на самом деле все наоборот - SELECT не может включать столбцы, которых еще нет в GROUP BY.

1 голос
/ 20 июня 2010

Ваш запрос будет работать только на MySQL, потому что вы группируете на Person.cityKey, но выбираете city.key.Во всех других базах данных вам потребуется использовать агрегат, такой как min(city.key), или добавить City.key к предложению group by.

Поскольку комбинация названия города и ключа города уникальна, следующее эквивалентно:

select    count(person.key), min(city.key), min(city.name)
...
group by  person.citykey

Или:

select    count(person.key), city.key, city.name
...
group by  person.citykey, city.key, city.name

Или:

select    count(person.key), city.key, max(city.name)
...
group by  city.key

Все строки в группе будут иметь одинаковые название города и ключ, поэтому это не такНе имеет значения, используете ли вы агрегат max или min.

PS Если вы хотите считать только разных людей, даже если у них несколько строк, попробуйте:

count(DISTINCT person.key)

вместо

count(person.key)
...