#sql #sql-server #average
#sql #sql-server #среднее
Вопрос:
У меня есть следующая таблица:
----------- ---------- ------------- ---------------- ---------------
| FirstName | LastName | SessionDate | SessionTitle | TotalDistance |
----------- ---------- ------------- ---------------- ---------------
| Player | Striker | 05.08.2020 | Entire Session | 10.0 |
| Player | Striker | 05.08.2020 | 1v1 | 5.0 |
| Player | Striker | 06.08.2020 | Entire Session | 12.0 |
| Coach | Defender | 06.08.2020 | Entire Session | 7.0 |
----------- ---------- ------------- ---------------- ---------------
Что мне нужно сделать, так это рассчитать среднее значение для totalDistance для каждого игрока в отдельности. Среднее значение должно вычисляться только со значениями, где SessionTitle равен ‘EntireSession’ и только за последние 50 дней, включая дату строки для каждой строки. Таким образом, ожидаемый результат будет выглядеть следующим образом:
----------- ---------- ------------- ---------------- --------------- ---------
| FirstName | LastName | SessionDate | SessionTitle | TotalDistance | average |
----------- ---------- ------------- ---------------- --------------- ---------
| Player | Striker | 05.08.2020 | Entire Session | 10.0 | 10.0 |
| Player | Striker | 05.08.2020 | 1v1 | 5.0 | 10.0 |
| Player | Striker | 06.08.2020 | Entire Session | 12.0 | 11.0 |
| Coach | Defender | 06.08.2020 | Entire Session | 7.0 | 7.0 |
----------- ---------- ------------- ---------------- --------------- ---------
Я пробовал что-то подобное, но это ужасно медленно, когда добавляется больше строк:
SELECT t1.FirstName,
t1.LastName,
t1.SessionDate,
t1.SessionTitle,
t1.TotalDistance,
(SELECT (AVG(t2.TotalDistance) FROM myTable t2 WHERE t1.FirstName = t2.FirstName AND t1.LastName = t2.LastName AND t2.SessionTitle = 'EntireSession' AND t2.SessionDate <= DATEADD(DAY,50,t1.SessionDate)) as average
FROM myTable t1
Он работает на SQL-сервере Azure
Каким будет самый быстрый способ сделать это в инструкции select?
Заранее спасибо!
Ответ №1:
Это ваш запрос:
SELECT t1.FirstName, t1.LastName, t1.SessionDate, t1.SessionTitle,
t1.TotalDistance,
(SELECT AVG(t2.TotalDistance)
FROM myTable t2
WHERE t1.FirstName = t2.FirstName AND
t1.LastName = t2.LastName AND
t2.SessionTitle = 'EntireSession' AND
t2.SessionDate <= DATEADD(DAY,50,t1.SessionDate)
) as average
FROM myTable t1;
Во-первых, вы можете попробовать добавить индекс на (FirstName, LastName, SessionDate, SessionTitle, TotalDistance)
.
Во-вторых, для меня это немного похоже на функцию окна. Если у вас есть данные за каждый день, вы можете использовать:
select t.*,
avg(case when SessionTitle = 'EntireSession' then TotalDistance end) over (
(partition by FirstName, LastName
order by SessionDate
rows between unbounded preceding and 50 following
)
from myTable t;
Комментарии:
1. Оконная функция во втором запросе должна быть
RANGE
основана, аROWS
не основана. К сожалению, SQL Server не поддерживаетRANGE
временные интервалы. Однако первый запрос хорош.2. @TheImpaler, да, это сложно сделать эффективно в SQL Server, потому что оконные функции не поддерживают
RANGE
оконный фрейм с интервалами. Это подробно обсуждалось на dba.se : Скользящая сумма диапазона дат с использованием оконных функций3. @TheImpaler . , , (1) SQL Server doe не поддерживает это. (2) Предполагается, что в каждой строке есть данные (упомянутые непосредственно перед запросом).
Ответ №2:
Вы можете просто добиться этого,
SELECT FirstName, LastName, avg([TotalDistance])
FROM myTable
WHERE SessionDate>= DATEADD(Day, -50, getdate())
AND SessionTitle = 'EntireSession'
GROUP BY FirstName, Last Name