得到"零"在没有记录的日期计数

时间:2016-03-28 12:55:15

标签: sql sql-server

所以我在计算系统中用户的活动记录。我得到某个月和每年的每一天的活动计数器,就像下面的查询一样

SELECT CONVERT(date, VIS_DATETIME) AS DATETIME, COUNT(*) AS ACTIVITY
FROM ACTIVITY
WHERE DATEPART(year, VIS_DATETIME) = 2016 AND
      DATEPART(month, VIS_DATETIME) = 3
GROUP BY CONVERT(date, VIS_DATETIME)
ORDER BY CONVERT(date, VIS_DATETIME)

问题是,如果,让我们说,3月28日没有任何活动,它甚至不会被列出。但是,对于我的图表API,我需要将其列出并使用0作为计数器。

显然,接受建议!

4 个答案:

答案 0 :(得分:4)

创建一个包含所有日期的表格。然后使用Activity表进行左连接。在日期上分组,并在Activity.id上执行COUNT。左连接确保日期表中的所有日期都包含在结果集中,即使它们在join子句中不匹配。

答案 1 :(得分:4)

Declare @DayOfMonth TinyInt Set @DayOfMonth = 1
Declare @Month TinyInt Set @Month = 1
Declare @Year Integer Set @Year = 2016
Declare @startDate datetime 
Declare @endDate datetime 
-- ------------------------------------
Select  @startDate = DateAdd(day, @DayOfMonth - 1, 
          DateAdd(month, @Month - 1, 
              DateAdd(Year, @Year-1900, 0)))

select @endDate  = dateadd(month,1,@startDate)

;with dateRange as
(
  select dt = dateadd(dd, 0, @startDate)
  where dateadd(dd, 0, @startDate) < @endDate
  union all
  select dateadd(dd, 1, dt)
  from dateRange
  where dateadd(dd, 1, dt) < @endDate
)
select *
from dateRange

上面的查询给出了该月份的所有日期,您可以将其与您的聚合查询一起加入,以获得零计数的条目。

答案 2 :(得分:0)

嗯,如果你想要创新。这有两个步骤:

  • 在表格中添加有关活动是否有效的标志
  • 向SQL Server代理添加作业

这些如何运作?

alter table activity add column ActivityIsValid smallint default 1;

这样,新行作为有效活动进入,而不是无效。

然后安排SQL Server代理作业每天运行以下代码一次:

insert into activity(vis_datetime, ActivityIsValid)
    values(cast(getdate() as date), 0);

然后您可以将查询运行为:

SELECT CONVERT(date, VIS_DATETIME) AS DATETIME, 
       SUM(CASE WHEN ActivityIsValid = 1 THEN 1 ELSE 0 END) AS ACTIVITY
FROM ACTIVITY
WHERE VIS_DATETIME >= '2016-03-01' AND
      VIS_DATETIME < '2016-04-01        
GROUP BY CONVERT(date, VIS_DATETIME)
ORDER BY CONVERT(date, VIS_DATETIME);

当然,这只会向前发展。您可以在历史数据中手动插入额外的行。

另请注意WHERE子句的更改。通过直接日期比较,SQL引擎可以使用索引。

答案 3 :(得分:0)

类似于Arun's answer,这将创建一个可以连接的临时表,并节省在数据库中创建永久表的时间。

DECLARE @@startDate DATETIME = '2016-03-01';
DECLARE @@endDate DATETIME = '2016-03-31';

DECLARE @@tempCalendar TABLE (
    [Id] INT IDENTITY(1,1) NOT NULL,
    [Year] INT NOT NULL,
    [Month] INT NOT NULL,
    [Day] INT NOT NULL
);

DECLARE @@dateCount DATETIME = @@startDate;

WHILE (@@dateCount <= @@endDate)
    BEGIN
        INSERT INTO @@tempCalendar 
        VALUES (
            DATEPART(YEAR, @@dateCount)
            , DATEPART(MONTH, @@dateCount)
            , DATEPART(DAY, @@dateCount)
        );

        SET @@dateCount = DATEADD(DAY, 1, @@dateCount);
    END


SELECT c.[Year]
    , c.[Month]
    , c.[Day]
    , COUNT(a.Id) AS ACTIVITY

FROM @@tempCalendar c
    LEFT OUTER JOIN ACTIVITY a ON c.[Year] = DATEPART(YEAR, a.VIS_DATETIME)
                                AND c.[Month] = DATEPART(MONTH, a.VIS_DATETIME)
                                AND c.[Day] = DATEPART(DAY, a.VIS_DATETIME)

WHERE DATEPART(YEAR, a.VIS_DATETIME) = DATEPART(YEAR, @@startDate) 
    AND DATEPART(MONTH, a.VIS_DATETIME) = DATEPART(MONTH, @@startDate) 

GROUP BY c.[Year], c.[Month], c.[Day]
ORDER BY c.[Year], c.[Month], c.[Day];