У меня есть таблица match
, которая выглядит так (см. прикрепленное изображение). Я хотел получить набор данных со столбцом средних значений для home_goal
и away_goal
, используя этот код.
SELECT
m.country_id,
m.season,
m.home_goal,
m.away_goal,
AVG(m.home_goal + m.away_goal) AS avg_goal
FROM match AS m;
Однако я получил эту ошибку
column "m.country_id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 3: m.country_id,
Мой вопрос: зачем нужен пункт GROUP BY
? Почему SQL не может знать, как усреднить два столбца построчно?
Спасибо.
попробуй это:
SELECT
m.country_id,
m.season,
m.home_goal,
m.away_goal,
(m.home_goal + m.away_goal)/2 AS avg_goal
FROM match AS m;
Вас попросили указать group_by, поскольку avg() так же, как и sum(), работает с несколькими значениями одного столбца, где вы классифицируете все столбцы, которые не являются операцией по столбцам в группе, с помощью
Вы хотите усреднить два разных столбца - это операции по строкам, а не по столбцам.
Спасибо, ДПХ. Теперь я понимаю причину. Просто для уточнения: встроенные функции, такие как sum
, avg
, предназначены для столбцов, иначе нужно использовать group by
, если вы хотите применить построчно?
@Nemo, если вам нужны операции по столбцам (горизонтальным), подумайте о формуле (a + b и т. д.). При работе по столбцам вы используете GROUP BY для определения переменных, которые составляют группы (подгруппы и т. д.), для которых вы хотите вычислить (sum(), avg() и т. д.). Когда вы сообщаете столбцу в операции по столбцу без какой-либо функции (sum(), avg() и т. д.), SQL требуется явная информация для группировки, поскольку набор данных сжимается по вертикали операцией по столбцу (что означает значения без операции не будут сжаты, и теперь SQL теряется, поскольку другие сжимаются)
Благодарю за разъяснение. Я не ожидал, что узнаю так много, когда задавал вопрос.
как взять среднее значение двух столбцов построчно?
Вы не используете AVG()
для этого; это агрегатная функция, которая работает с набором строк. Здесь кажется, что вам просто нужно простое математическое вычисление:
SELECT
m.country_id,
m.season,
m.home_goal,
m.away_goal,
(m.home_goal + m.away_goal) / 2.0 AS avg_goal
FROM match AS m;
Обратите внимание на десятичный знаменатель (2.0
): это позволяет избежать целочисленного деления в базах данных, которые его реализуют.
Спасибо за решение, GMB. Но почему я не мог использовать функцию AVG
? Не могли бы вы уточнить это в своем ответе, чтобы я принял это как решение?
@Nemo: нет, это не то, что делает AVG()
. Нет встроенной функции для простых вычислений, которые вы хотите сделать.
Вы имеете в виду, что функции AVG
, SUM
и т. д. нельзя использовать на уровне строк, а только на уровне агрегатов?
SQL любит столбцы, поэтому встроенные функции обычно ориентированы именно на это. С SQL можно делать все, что угодно, но это не значит, что он будет эффективным или простым в реализации.
Спасибо, Марк. Ваш комментарий помог мне лучше понять механизм SQL.
Avg в контексте упомянутой выше функции вычисляет среднее значение значений столбцов, а не среднее значение двух значений в одной строке. Это агрегатная функция, и поэтому требуется предложение group by.
Чтобы получить среднее значение двух столбцов в одной строке, нужно разделить на 2.
Рассмотрим следующую таблицу:
CREATE TABLE Numbers([x] int, [y] int, [category] nvarchar(10));
INSERT INTO Numbers ([x], [y], [category])
VALUES
(1, 11, 'odd'),
(2, 22, 'even'),
(3, 33, 'odd'),
(4, 44, 'even');
Вот пример использования двух агрегатных функций — AVG и SUM — с GROUP BY:
SELECT
Category,
AVG(x) as avg_x,
AVG(x+y) as avg_xy,
SUM(x) as sum_x,
SUM(x+y) as sum_xy
FROM Numbers
GROUP BY Category
Результат состоит из двух строк:
Category avg_x avg_xy sum_x sum_xy
even 3 36 6 72
odd 2 24 4 48
Обратите внимание, что Category
доступен в части SELECT, потому что результаты сгруппированы по нему. Если GROUP BY не указан, результатом будет 1 строка, а Category
недоступен (какое значение должно отображаться, если у нас есть суммы и средние значения для нескольких строк с разными категориями?).
Что вы хотите, так это вычислить новый столбец, и для этого вы не используете агрегатные функции:
SELECT
(x+y)/2 as avg_xy,
(x+y) as sum_xy
FROM Numbers
Это возвращает все строки:
avg_xy sum_xy
6 12
12 24
18 36
24 48
Если ваши столбцы являются целыми числами, не забудьте обработать округление, если это необходимо. Например (CAST(x AS DECIMAL)+y)/2 as avg_xy,
Простой арифметический расчет:
(m.home_goal + m.away_goal) / 2.0
не совсем эквивалентен AVG()
, потому что значения NULL
путают его. Базы данных, поддерживающие боковые соединения, предоставляют довольно простой (и эффективный) способ использования AVG()
в строке.
Безопасная версия выглядит так:
(coalesce(m.home_goal, 0) + coalesce(m.away_goal, 0)) /
nullif ( (case when m.home_goal is not null then 1 else 0 end +
case when m.away_goal is not null then 1 else 0 end
), 0
)
Некоторые базы данных имеют расширения синтаксиса, которые позволяют упростить выражение.
Спасибо за новые концепции (coalesce
, nullif
), Гордон. Что вы имели в виду под некоторыми базами данных, такими как иерархические, реляционные, графовые, сетевые и т. д.?
@Немо. . . Нет. Все традиционные реляционные базы данных так или иначе расширяют стандарт или поддерживают «необычные» функции стандарта. Например, Postgers поддерживает filter
, а MySQL обрабатывает логические значения как целые числа.
Отметьте свой вопрос с помощью базы данных, которую вы используете.