SQL Server — записи SUM() и Group() на основе условия

В моем игровом приложении у меня есть команды, и в каждой команде может быть любое количество игроков; если игрок участвует в матче, я даю ему 5 очков. Каждый раз, когда игрок участвует в матче, к его счету добавляются 5 очков.

Моя хранимая процедура принимает TeamId в качестве входного параметра.

Теперь я хочу рассчитать общее количество очков участия, которое каждая команда получила по месяцам, но здесь очки участия, которые набрал каждый игрок, должны быть добавлены к последнему месяцу, в котором игрок играл в матче.

Допустим, у Team1 есть Player1, а Player1 сыграл всего 4 матча, 1 матч в 04/2020, 2 матча в 06/2020 и 1 матч в 08/2020, здесь за 4 матча Player1 из Team1 получил 20 очков участия, а последний матч, сыгранный Игроком 1, приходится на 08/2020, поэтому все 20 очков должны быть добавлены к 08/2020 для Команды 1.

В таблице игроков для каждого игрока у меня есть [TotalMatchesPlayed] для каждого игрока, [TotalMatchesPlayed] * 5 даст мне [TotalParticipationPoints] для каждого игрока.

Это должно повториться для всех игроков в команде.

    SELECT SUM(ISNULL(P.[TotalMatchesPlayed], 0) * 5) AS [ParticipationPoints], CAST(MONTH(PA.[ActivityDate]) AS VARCHAR(2)) AS [Month], CAST(YEAR(PA.[ActivityDate]) AS VARCHAR(4)) AS [Year] FROM [TeamPlayer] TP
INNER JOIN dbo.[Player] P
ON TP.[PlayerId] = P.[PlayerId]
INNER JOIN dbo.[PlayerActivity] PA
ON PA.[PlayerId] = P.[PlayerId] AND PA.[PlayerActivityTyepId] = 14
WHERE TP.[TeamId] = 45
GROUP BY CAST(MONTH(PA.[ActivityDate]) AS VARCHAR(2)), CAST(YEAR(PA.[ActivityDate]) AS VARCHAR(4))

Моя проблема с этим запросом заключается в том, что в таблице [PlayerActivity] есть строка каждый раз, когда игрок участвует в матче, теперь я хочу взять только самую последнюю дату и добавить все очки участия в этот месяц и год, чего я не могу достичь.

Мой образец вывода должен быть таким, как показано ниже

ParticipationPoints | Month |  Year
        5                06     2020
       10                11     2020

CREATE TABLE TeamPlayer (
    TeamPlayerID int,
    PlayerId int,
    TeamId INT
);

INSERT INTO TeamPlayer VALUES (1, 101, 45)
INSERT INTO TeamPlayer VALUES (2, 104, 19)
INSERT INTO TeamPlayer VALUES (3, 108, 45)

CREATE TABLE Player (
    PlayerID int,
    FirstName VARCHAR(1000),
    LastName VARCHAR(1000),
    [TotalMatchesPlayed] INT
);

INSERT INTO Player VALUES (101, 'Joe', 'Abbey', 2)
INSERT INTO Player VALUES (102, 'Vince', 'Abbott', 3)
INSERT INTO Player VALUES (103, 'Duke', 'Abbruzzi', 7)
INSERT INTO Player VALUES (104, 'Kamlesh', 'Abu', 9)
INSERT INTO Player VALUES (105, 'Evika', 'Abram', 0)
INSERT INTO Player VALUES (106, 'Prince', 'Subtle', 2)
INSERT INTO Player VALUES (107, 'Dick', 'Absher', 1)
INSERT INTO Player VALUES (108, 'George', 'Abrell', 1)
INSERT INTO Player VALUES (109, 'William', 'Peck', 2)
INSERT INTO Player VALUES (110, 'Aaron', 'Adams', 0)

CREATE TABLE PlayerActivity (
    PlayerActivityId int,
    PlayerId int,
    PlayerActivityTyepId INT,
    ActivityDate DATE
);

INSERT INTO PlayerActivity VALUES (1, 101, 14, '2020-04-21')
INSERT INTO PlayerActivity VALUES (2, 108, 14, '2020-06-17')
INSERT INTO PlayerActivity VALUES (3, 101, 14, '2020-11-24')

Образцы таблиц и образцы данных в приведенной ниже ссылке с моим запросом.

http://sqlfiddle.com/#!18/870f92/1

Повторюсь еще раз: если игрок в команде сыграл несколько матчей, скажем, 6, мне нужно добавить 6*5 = 30 очков к месяцу, в котором он сыграл свой последний матч.

Скрипки должны дополнять вопрос, а не требоваться от нас для получения выборочных данных.

Thom A 23.12.2020 16:04

@Larnu, извини, Ларну, я не понял, что ты пытаешься донести .. что-то не так в том, что я написал?

adityaa 23.12.2020 16:10

Вы только что разместили ссылку, когда в вопросе также должны быть DDL и DML.

Thom A 23.12.2020 16:13

исправлю это в следующий раз, это первый раз, когда я использовал скрипку

adityaa 23.12.2020 16:14

Выходные данные образца не соответствуют фактическим датам активности. например, нет месяца 03 или 05, но есть месяцы, не указанные в качестве результата. Либо я не понял вашего вопроса, либо пример неверный.

Olivier Jacot-Descombes 23.12.2020 16:29

@OlivierJacot-Descombes Образец вывода, который я опубликовал, представляет собой просто случайные данные, просто чтобы указать, как должен выглядеть вывод ... вы можете сослаться на это sqlfiddle.com/#!18/4aca64/1

adityaa 23.12.2020 16:58
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
6
245
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В моем решении используется подзапрос, который определяет дату последней активности для каждого игрока.

SELECT
    PlayerId,
    MAX(ActivityDate) AS LastDate
FROM
    dbo.PlayerActivity
WHERE PlayerActivityTyepId = 14
GROUP BY PlayerId

Основной запрос также использует GROUP BY для агрегирования результатов по командам и годам/месяцам. Трюк делается в выражении CASE. Здесь я сравниваю фактическую дату активности с последней, определенной подзапросом. Баллы начисляются только за последнее действие, в противном случае 0.

SELECT
    TP.TeamId,
    SUM(
        CASE
           WHEN PA.ActivityDate = X.LastDate
           THEN P.TotalMatchesPlayed * 5
           ELSE 0
        END
    ) AS ParticipationPoints,
    RIGHT(CAST(100+MONTH(PA.ActivityDate) AS VARCHAR(3)), 2) AS [Month],
    CAST(YEAR(PA.ActivityDate) AS VARCHAR(4)) AS [Year]
FROM
    dbo.TeamPlayer TP
    INNER JOIN dbo.Player P
        ON TP.PlayerId = P.PlayerId
    INNER JOIN dbo.PlayerActivity PA
        ON PA.PlayerId = P.PlayerId AND PA.PlayerActivityTyepId = 14
    INNER JOIN (
        SELECT
            PlayerId,
            MAX(ActivityDate) AS LastDate
        FROM
            dbo.PlayerActivity
        WHERE PlayerActivityTyepId = 14
        GROUP BY PlayerId
    ) AS X
        ON X.PlayerId = P.PlayerId
WHERE TP.TeamId = 45
GROUP BY
    TP.TeamId, YEAR(PA.ActivityDate), MONTH(PA.ActivityDate)

http://sqlfiddle.com/#!18/41766/28/0

Обратите внимание, что я предполагаю, что каждый игрок совершает только одно действие в день. Если их больше, баллы будут суммироваться повторно. Если значения PlayerActivityId вставлены строго в порядке возрастания, вы можете исправить это, работая с MAX(PlayerActivityId) вместо MAX(ActivityDate).

Чтобы получить месяц с двумя цифрами, я сначала вычисляю месяц + 100 и конвертирую это в VARCHAR(3). Это дает '104' за апрель. Затем я получаю результат, возвращая два последних символа с помощью функции RIGHT. Конечно, вы могли бы использовать правильные функции форматирования, но мне было лень искать нужные функции и форматы для использования.

В соответствии с моим первоначальным тестированием приведенный выше запрос отлично работает, но все еще нужно тестировать различные сценарии, у меня более 1 активности в день, и я думаю, что MAX (ActivityDate) должен работать, поскольку я также фиксирую дату по минутам и секундам, и я этого не сделал. разместите минуты и секунды в моем примере.

adityaa 23.12.2020 17:56

Вы можете использовать оконные функции:

select year(activitydate) as yyyy, month(activitydate) as mm, 
    sum(cnt) * 5 as partitionpoints
from (
    select pa.*, 
        count(*) over(partition by playerid) cnt,
        row_number() over(partition by playerid order by activitydate desc) rn
    from playeractivity pa
) t
where rn = 1
group by year(activitydate), month(activitydate)
order by yyyy, mm

Подзапрос вычисляет общее количество действий каждого игрока и ранжирует записи каждого игрока от самых новых до самых старых. Внешний запрос фильтрует самые новые записи, агрегирует по годам и месяцам и выполняет вычисления.

Демо на DB Fiddle:

yyyy | mm | partitionpoints
---: | -: | --------------:
2020 |  6 |               5
2020 | 11 |              10

Обратите внимание, что мы можем получить этот результат, взглянув только на таблицу действий. Не похоже, что нам нужна таблица команд или таблица игроков.

Другие вопросы по теме