简体   繁体   中英

SQL Average based on multiple conditions

I have the following table:

+-----------+----------+-------------+----------------+---------------+
| 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 |
+-----------+----------+-------------+----------------+---------------+

What I need to do is to calculate the average for totalDistance, for each player individually. The average should only be calculated with values, where the SessionTitle is 'EntireSession' and only for the last 50 days for each row. So the expected result would look like this:

+-----------+----------+-------------+----------------+---------------+---------+
| 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 |
+-----------+----------+-------------+----------------+---------------+---------+

I tried something like this, but it is awfully slow, when more rows are added:

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

It is running on a Azure SQL-Server How would the fastest way to get this done in a select statement?

Thanks in advance!

We can use AVG here as an analytic function:

SELECT *, AVG(CASE WHEN SessionTitle = 'Entire Session' THEN TotalDistance END)
              OVER (PARTITION BY LastName, FirstName) AS average
FROM yourTable;

The strategy here is to compute the average total distance for each group of records belonging to each person. We use a CASE expression which will return the TotalDistance when the session title happens to be Entire Session . Otherwise, it would return NULL which by default is ignored by the AVG function.

Unfortunately, the "last 50 days" part is problematic in SQL Server, because it does not support range window frames with date/time intervals. That precludes the simple use of. a window function (unless you have data on every day).

But, you can easily work around this using apply :

select t1.*, t2.average
from mytable t1 outer apply
     (select avg(t2.total_distance) as average
      from mytable t2
      where t2.FirstName = t1.FirstName and
            t2.LastName = t1.LastName and
            t2.SessionTitle = 'EntireSession' and
            t2.SessionDate >= DATEADD(DAY, -50, t1.SessionDate) and
            t2.SessionDate <= t1.SessionDate
     ) t2;

This is correctly phrased for what you want to do. Otherwise, it is similar to the correlated subquery.

Next, you want an index to facilitate this calculation. I would suggestion an index on mytable(lastname, firstname, sessiontitle, sessiondate, total_distance) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM