Я пытаюсь определить, насколько ниже или выше среднего показатели заведения. Например, запрос вернет место проведения, дату и то, насколько ниже ожидаемого среднего показателя. Из приведенных ниже данных вы можете видеть, что средний балл Пола составлял 100, не считая Лейквью, поэтому он прошел точно свой средний балл, а Мел на 20 ниже своего среднего показателя в Лейквью, поэтому Лейквью показала в среднем на -10 результат ниже средней дистанции для людей.
Результат запроса будет выглядеть так:
Venue Date Performance
-------------------------------------
Lakeview 12/08/2024 -10
Во-первых, мне нужно сгруппировать по имени, чтобы получить среднее значение. Однако я хочу включить только последние 5 дистанций.
Я могу сделать это с помощью этого запроса
SELECT name, AVG(distance) as avg_distance
FROM
(SELECT
database.*,
ROW_NUMBER() OVER (PARTITION BY name ORDER BY date DESC) AS seqnum
FROM database) s
WHERE seqnum <= 5
GROUP BY name
Затем мне нужно сгруппировать по местам проведения и найти среднее расстояние всех, кто выступал в этот день и в этом месте. Что я могу сделать с помощью этого запроса
SELECT venue, AVG(distance)
FROM "database"
GROUP BY venue, date
Затем мне нужно вычесть расстояние в этот день из их среднего значения, чтобы увидеть, как оно работает. Какой самый простой способ сделать это?
Пример данных
Name Venue Distance Date
-------------------------------------
Paul Holland 120 20/07/2024
Paul Venus 80 21/07/2024
Paul Lakeview 100 12/08/2024
Tom Lakeview 120 12/08/2024
Mel Lakeview 140 12/08/2024
Mel Hightail 160 05/05/2024
база данных — очень запутанное имя таблицы.
Рассчитываются ли средние расстояния для каждого человека по всем объектам или по исключенным объектам? Представленный запрос для расчета средних расстояний делает первое, но из описания следует, что предназначено второе.
Один из вариантов — создать cte (или подзапрос) с добавленными столбцами со средними расстояниями для каждого имени и места/даты. Это можно сделать с помощью аналитической функции Avg() Over().
WITH -- S a m p l e D a t a :
tbl ( a_name, venue, distance, a_date ) AS
( Select 'Paul', 'Holland', 120, To_Date('20/07/2024', 'dd/mm/yyyy') Union All
Select 'Paul', 'Venus', 80, To_Date('21/07/2024', 'dd/mm/yyyy') Union All
Select 'Paul', 'Lakeview', 100, To_Date('12/08/2024', 'dd/mm/yyyy') Union All
Select 'Tom', 'Lakeview', 120, To_Date('12/08/2024', 'dd/mm/yyyy') Union All
Select 'Mel', 'Lakeview', 140, To_Date('12/08/2024', 'dd/mm/yyyy') Union All
Select 'Mel', 'Hightail', 160, To_Date('05/05/2024', 'dd/mm/yyyy')
),
... cte сетка, чтобы получить средние значения по имени (последние 5 дат) и по месту/дате (все расстояния)
grid AS
( SELECT a.*,
Round(Avg(Case When rn_name <= 5 Then a.distance End) Over(Partition By a_name ), 2) as avg_name,
Round(Avg(a.distance) Over(Partition By venue, a_date ), 2) as avg_venue_date
FROM ( Select Row_Number() Over(Partition By a_name Order By a_name, a_date Desc) as rn_name,
a_name, venue, distance, a_date
From tbl
) a
)
-- M a i n S Q L :
Select venue, a_date, Sum(avg_venue_date - avg_name) as performance
From grid
Group By a_date, venue
Order By a_date, venue
/* R e s u l t :
venue a_date performance
----------- ---------- -----------
Hightail 2024-05-05 10.00
Holland 2024-07-20 20.00
Venus 2024-07-21 -20.00
Lakeview 2024-08-12 -10.00 */
См. скрипку здесь.
Дополнение:
Если имя вашей таблицы — база данных (что в любом случае плохо), и если имена/типы столбцов в этой таблице — это имя /varchar, место проведения /varchar, расстояние /целое число и дата /дата (дата также является очень, очень плохим именем столбца потому что это тоже тип данных), тогда попробуйте использовать полный код, как показано ниже. Есть еще один (первый) способ, который должен привести вашу таблицу в соответствие с остальным кодом:
WITH
tbl AS
( Select t.name as a_name, t.venue, t.distance, t.date as a_date
From database t
),
grid AS
( SELECT a.*,
Round(Avg(Case When rn_name <= 5 Then a.distance End) Over(Partition By a_name ), 2) as avg_name,
Round(Avg(a.distance) Over(Partition By venue, a_date ), 2) as avg_venue_date
FROM ( Select Row_Number() Over(Partition By a_name Order By a_name, a_date Desc) as rn_name,
a_name, venue, distance, a_date
From tbl
) a
)
Select venue, a_date, Sum(avg_venue_date - avg_name) as performance
From grid
Group By a_date, venue
Order By a_date, venue
Спасибо за ваш ответ! Первый ввод сделан только для того, чтобы предоставить вам образец данных? Как мне это сделать, если у меня уже есть все данные? У меня более 100 000 строк данных, поэтому я не думаю, что смогу вручную поместить их все в запрос.
@undecided000 Это всего лишь пример данных, которые вы опубликовали в конце своего вопроса. Это НЕ часть ответа. Вам нужно всего лишь: С сеткой AS (код сетки sql) Основной код SQL
Спасибо. Он говорит мне, что все значения a_ не существуют? Например, имя_а.
@undecided000 Вам нужно адаптировать код к вашему контексту. Укажите имя своей таблицы и имена столбцов, чтобы все заработало. В этом ответе используются имена из образцов данных, а не из вашей базы данных...
Ах, я вижу. Я считаю, что внес необходимые изменения, но получаю эту ошибку. ОШИБКА: оператор не существует: варьируется символ <= целое число LINE 4: ... Round(Avg(Case When data.name <= 5 then... ^ ПОДСКАЗКА: ни один оператор не соответствует данному имени и типам аргументов. Возможно, вам понадобится чтобы добавить явное приведение типов.
@undecided000 это rn_name <= 5 . rn_name — это псевдоним из подзапроса в сетке cte. Вам следует изменить только имя вашей таблицы (вместо моей tbl) и имена столбцов, если они отличаются. Имена столбцов, которые следует изменить на ваши: a_name, a_date, location и distance. Не меняйте мои псевдонимы (например, rn_name, avg_name, avg_venue_date). И, пожалуйста, не используйте базу данных в качестве имени таблицы.
@undecided000 Только что опубликовал дополнение, которое может вам помочь...
Потрясающий! Спасибо, приятель. Это работает, и спасибо за совет по поводу моих имен, я новичок в sql. Единственное, что я изменил, это четвертая последняя строка: вместо суммы я хотел Avg. Я просто изменил это, и теперь все именно так, как я хотел. Огромное спасибо за ваше время и мудрость.
Пожалуйста, обновите сообщение, добавив таблицу DDL, и предоставьте исполняемый SQL для заполнения таблицы примерами данных (чтобы другим было максимально просто решить проблему). Предоставьте полный ожидаемый результат запроса, а не только одну точку. Данные примера должны включать достаточно значений для выполнения всех требований. В этом случае недостаточно точек данных, чтобы продемонстрировать фильтрацию только по пяти самым последним по имени. Является ли сочетание имени и даты уникальным? Если нет, то какие дополнительные критерии следует использовать для определения того, какие из них включены?