SQL Count包含零值

时间:2012-09-11 15:02:53

标签: sql sql-server-2008 count

我创建了以下存储过程,用于计算所选位置的特定范围之间每天的记录数:

[dbo].[getRecordsCount] 
@LOCATION as INT,
@BEGIN as datetime,
@END as datetime

SELECT
ISNULL(COUNT(*), 0) AS counted_leads, 
CONVERT(VARCHAR, DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp)), 3) as TIME_STAMP 
FROM HL_Logs
WHERE Time_Stamp between @BEGIN and @END and ID_Location = @LOCATION
GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp))

但问题是结果没有显示记录为零的日子,我很确定它与我的WHERE语句有关,不允许显示零值,但我不知道如何结束来这个问题。

提前致谢 尼尔

4 个答案:

答案 0 :(得分:9)

不是WHERE子句,而是GROUP BY。简单地说,查询将只返回存在的行的数据。这意味着当您按时间戳的日期进行分组时,将仅返回有行的天数。 SQL Server无法从上下文中知道您要“填空”,而且它不知道用什么。

正常答案是CTE,它会产生您想要看到的所有日子,从而填补空白。这个有点棘手,因为它需要一个递归的SQL语句,但这是一个众所周知的技巧:

WITH CTE_Dates AS
(
    SELECT @START AS cte_date
    UNION ALL
    SELECT DATEADD(DAY, 1, cte_date)
    FROM CTE_Dates
    WHERE DATEADD(DAY, 1, cte_date) <= @END
)
SELECT
cte_date as TIME_STAMP,
ISNULL(COUNT(*), 0) AS counted_leads, 
FROM CTE_Dates
LEFT JOIN HL_Logs ON DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp)) = cte_date
WHERE Time_Stamp between @BEGIN and @END and ID_Location = @LOCATION
GROUP BY cte_date

将其分解,CTE使用一个引用自身的联合,以递归方式一次添加一天到上一个日期,并记住该日期作为表的一部分。如果你运行了一个使用CTE并且刚从中选择了*的简单语句,你会看到开始和结束之间的日期列表。然后,语句根据日志时间戳日期将此日期列表连接到日志表,同时使用左连接保留没有日志条目的日期(从“左”侧获取所有行,无论它们是否在“对“或不”。最后,我们按日期和计数进行分组,我们应该得到你想要的答案。

答案 1 :(得分:4)

如果没有要计算的数据,则不会返回任何行。

如果要将空日包含为0,则需要创建一个表(或临时表或子查询)来存储日期,并从中继续加入查询。

例如:

之类的东西
SELECT 
    COUNT(*) AS counted_leads,  
    CONVERT(VARCHAR, DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp)), 3) as TIME_STAMP  
    FROM 
        TableOfDays
           left join
        HL_Logs 
           on TableOfDays.Date = convert(date,HL_Logs.Time_Stamp)
           and ID_Location = @LOCATION 
    WHERE TableOfDays.Date between @BEGIN and @END 
    GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp)) 

答案 2 :(得分:0)

使用左外连接。如

select count(stuff_ID), extra_NAME 
from dbo.EXTRAS 
left outer join dbo.STUFF on suff_EXTRA = extra_STUFF 
group by extra_NAME 

答案 3 :(得分:0)

我最近才有类似的任务,并以此作为我工作的背景。然而,正如robwilliams所解释的那样,无法让KeithS解决方案起作用。我的任务略有不同我按小时与天相比,但我认为neilrudds问题的解决方案将是

DECLARE @Start as DATETIME
       ,@End as DATETIME
       ,@LOCATION AS INT;


WITH CTE_Dates AS
(
    SELECT @Start AS cte_date, 0 as 'counted_leads'
    UNION ALL
    SELECT DATEADD(DAY, 1, cte_date) as cte_date, 0 AS 'counted_leads'
    FROM CTE_Dates  
    WHERE DATEADD(DAY, 1, cte_date) <= @End
)
SELECT cte_date AS 'TIME_STAMP'
      ,COUNT(HL.ID_Location) AS 'counted_leads'
FROM CTE_Dates 
LEFT JOIN HL_Logs AS HL ON CAST(HL.Time_Stamp as date) = CAST(cte_date as date)
AND DATEPART(day, HL.Time_Stamp) = DATEPART(day,cte_date)
AND HL.ID_Location = @LOCATION
group by cte_date
OPTION (MAXRECURSION 0)