В моем игровом приложении у меня есть команды, и в каждой команде может быть любое количество игроков; если игрок участвует в матче, я даю ему 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 очков к месяцу, в котором он сыграл свой последний матч.
@Larnu, извини, Ларну, я не понял, что ты пытаешься донести .. что-то не так в том, что я написал?
Вы только что разместили ссылку, когда в вопросе также должны быть DDL и DML.
исправлю это в следующий раз, это первый раз, когда я использовал скрипку
Выходные данные образца не соответствуют фактическим датам активности. например, нет месяца 03 или 05, но есть месяцы, не указанные в качестве результата. Либо я не понял вашего вопроса, либо пример неверный.
@OlivierJacot-Descombes Образец вывода, который я опубликовал, представляет собой просто случайные данные, просто чтобы указать, как должен выглядеть вывод ... вы можете сослаться на это sqlfiddle.com/#!18/4aca64/1
В моем решении используется подзапрос, который определяет дату последней активности для каждого игрока.
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) должен работать, поскольку я также фиксирую дату по минутам и секундам, и я этого не сделал. разместите минуты и секунды в моем примере.
Вы можете использовать оконные функции:
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
Подзапрос вычисляет общее количество действий каждого игрока и ранжирует записи каждого игрока от самых новых до самых старых. Внешний запрос фильтрует самые новые записи, агрегирует по годам и месяцам и выполняет вычисления.
yyyy | mm | partitionpoints ---: | -: | --------------: 2020 | 6 | 5 2020 | 11 | 10
Обратите внимание, что мы можем получить этот результат, взглянув только на таблицу действий. Не похоже, что нам нужна таблица команд или таблица игроков.
Скрипки должны дополнять вопрос, а не требоваться от нас для получения выборочных данных.