Как получить отсутствующие даты со значением 0 в SQL Server? - PullRequest
0 голосов
/ 22 февраля 2019

Я использую приведенный ниже запрос в SQL Server, чтобы найти distinct количество входов в систему, выполненных за последние 7 дней (исключая сегодняшнюю дату):

SELECT TOP (7) CONVERT(date, LoginTime) AS ActivityDate, COUNT(DISTINCT LoginID) AS UserCount
FROM Login
WHERE CONVERT(date, LoginTime) < CONVERT(date, GETDATE())
GROUP BY CONVERT(date, LoginTime)  
ORDER BY ActivityDate DESC; 

Он генерирует следующий вывод:

ActivityDate | UserCount
----------------------
2019-02-21   | 2
2019-02-20   | 3
2019-02-19   | 2
2019-02-15   | 2
2019-02-14   | 1
2019-02-13   | 2
2019-02-12   | 3

Я ожидаю, что все последние 7 дней будут в последовательности (не так, как текущий вывод, где после 2019-02-19 отсутствуют даты 2019-02-16, 2019-02-17 и 2019-02-18).Мне нужно, чтобы, если дата отсутствует, она должна отображаться со счетом 0.

Мой ожидаемый результат такой, как показано ниже:

ActivityDate | UserCount
----------------------
2019-02-21   | 2
2019-02-20   | 3
2019-02-19   | 2
2019-02-18   | 0
2019-02-17   | 0
2019-02-16   | 0
2019-02-15   | 2

Ответы [ 4 ]

0 голосов
/ 22 февраля 2019

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

Вот чрезвычайно простая календарная таблица на один год, основанная на этом ответе:

CREATE TABLE [Calendar]
(
    [CalendarDate] DATETIME
)

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SET @StartDate = GETDATE()
SET @EndDate = DATEADD(d, 365, @StartDate)

WHILE @StartDate <= @EndDate
      BEGIN
             INSERT INTO [Calendar]
             (
                   CalendarDate
             )
             SELECT
                   @StartDate

             SET @StartDate = DATEADD(dd, 1, @StartDate)
      END

(Вы можете изменить этот запрос, чтобы добавить еще много дат в будущем, так что его не нужно будет поддерживать некоторое время.)

Теперь вы можете присоединиться к таблице календаря в своем запросе следующим образом:

select top (7) c.CalendarDate as ActivityDate,count(distinct LoginID) as UserCount
from Calendar c
left join Login l
    ON c.CalendarDate = CONVERT(date, l.LoginTime)
    and CONVERT(date,LoginTime )< convert(date,getdate())
group by c.CalendarDate 
order by c.CalendarDate desc; 

Это стоит местаподнимается, это пригодится и во многих других случаях.

0 голосов
/ 22 февраля 2019

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

. Вы можете использовать простой рекурсивный CTE для генерации 1 строки в день между определеннымиинтервал, затем используйте LEFT JOIN, чтобы присоединиться к логинам, которые совпадают в этот конкретный день.Те, которые не совпадают, будут отображаться, так как мы используем LEFT JOIN.

DECLARE @GeneratingDateFrom DATE = DATEADD(DAY, -7, GETDATE())
DECLARE @GeneratingDateTo DATE = GETDATE()

;WITH GeneratedDates AS
(
    SELECT
        GeneratedDate = @GeneratingDateFrom

    UNION ALL

    SELECT
        GeneratedDate = DATEADD(DAY, 1, G.GeneratedDate)
    FROM
        GeneratedDates AS G
    WHERE
        DATEADD(DAY, 1, G.GeneratedDate) < @GeneratingDateTo
)
SELECT
    G.GeneratedDate,
    count(distinct L.LoginID) as UserCount
FROM 
    GeneratedDates AS G
    LEFT JOIN [Login] AS L ON G.GeneratedDate = CONVERT(date, L.LoginTime)
GROUP BY
    G.GeneratedDate
ORDER BY 
    G.GeneratedDate desc
0 голосов
/ 22 февраля 2019

Это всего лишь 7 дней, поэтому просто введите эти даты:

SELECT ActivityDate, COUNT(DISTINCT LoginID) AS UserCount
FROM (VALUES
    (CAST(CURRENT_TIMESTAMP - 1 AS DATE)), -- build the list of dates
    (CAST(CURRENT_TIMESTAMP - 2 AS DATE)),
    (CAST(CURRENT_TIMESTAMP - 3 AS DATE)),
    (CAST(CURRENT_TIMESTAMP - 4 AS DATE)),
    (CAST(CURRENT_TIMESTAMP - 5 AS DATE)),
    (CAST(CURRENT_TIMESTAMP - 6 AS DATE)),
    (CAST(CURRENT_TIMESTAMP - 7 AS DATE))
) datelist(ActivityDate)
LEFT JOIN Login ON CAST(LoginTime AS DATE) = ActivityDate
GROUP BY ActivityDate
ORDER BY ActivityDate DESC
0 голосов
/ 22 февраля 2019

Вы можете попробовать это.Здесь вам нужно получить первую минимальную дату и максимальную дату.После этого вам нужно сгенерировать все даты между этими двумя днями.И наконец, вам нужно присоединиться к обоим столам.

declare @MinDate date
declare @MaxDate date

select * into #temp from(
select top (7) CONVERT(date,LoginTime) as ActivityDate,count(distinct LoginID) as UserCount
        from Login
        where CONVERT(date,LoginTime )< convert(date,getdate())
        group by CONVERT(date,LoginTime )  
        order by ActivityDate desc; 
)a        

Set @MinDate = (select min (ActivityDate) from #temp)
Set @MaxDate = (select max (ActivityDate) from #temp)  

Select a.Date, isnull(b.UserCount,0) as UserCount from(
SELECT  TOP (DATEDIFF(DAY, @MinDate, @MaxDate) + 1)
        Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @MinDate)
FROM    sys.all_objects a
        CROSS JOIN sys.all_objects b;
)a left join #temp b on a.Date = b.ActivityDate

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

...