Выборка данных по датам с помощью GROUP BY, включая даты для которые данные отстутствуют
К примеру, в БД MS SQL Server есть таблица Logs в которую заносятся данные об активности пользователей. По этим данным необходимо сделать выборку, например, подсчитать количество действий пользователей по дням месяца. По выходным и праздникам пользователи приложением не пользуются, поэтому при использовании обычного GROUP BY в результате выборки будут представлены только те даты, для которых нашлись данные, что, например, для построения графиков не подходит.
Общая идея такова: данные, полученные с помощью GROUP BY необходимо объединить с помощью JOIN со списком всех дат за период. Даты могут быть заготовлены в виде отдельной таблицы, но существует и более изящное решение с использованием CTE:
SqlCommand command = new SqlCommand();
command.Parameters.AddWithValue("DateFrom", dateFrom);
command.Parameters.AddWithValue("DateTo", dateTo.AddDays(1));
command.Parameters.AddWithValue("DateToExclusive", dateTo);
command.CommandText = String.Format(@"
DECLARE @LowDate DATETIME
SET @LowDate = @DateFrom;
DECLARE @HighDate DATETIME
SET @HighDate = @DateToExclusive;
WITH [Dates]([MyDate])
AS
(
SELECT @LowDate [MyDate]
UNION ALL
SELECT ([MyDate]+1) [MyDate]
FROM [Dates]
WHERE [MyDate] < @HighDate
)
SELECT
[Dates].[MyDate] AS [Date],
ISNULL([Totals].[Count], 0) AS [Count]
FROM [Dates]
LEFT JOIN (
SELECT
COUNT([NUserAudit]) AS [Count],
CAST(FLOOR(CAST([LogTimestamp] AS FLOAT)) AS DATETIME) AS [Date]
FROM [dbo].[Logs]
WHERE
([LogTimestamp] BETWEEN @DateFrom AND @DateTo)
GROUP BY CAST(FLOOR(CAST([LogTimestamp] AS FLOAT)) AS DATETIME)
) AS [Totals]
ON [Dates].[MyDate] = [Totals].[Date]
ORDER BY [Date]
OPTION (MAXRECURSION {0})
",
(DateTo-DateFrom).Days)
);
Несколько комментариев:
- Для получения только даты из поля типа
DATETIMEприменяется один из возможных способов:CAST(FLOOR(CAST([LogTimestamp] AS FLOAT)) AS DATETIME) - При большом диапазоне дат возможны проблемы с глубиной рекурсии в CTE, поэтому значение
MAXRECURSIONможно заранее ограничить.
