SQL 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, не позволяющей показывать нулевые значения, но я не знаю, как преодолеть эту проблему.

спасибо заранее Нил!--2-->

4 ответов


не столько предложение WHERE, сколько группа 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 и просто выбрал * из него, вы увидите список дат между началом и концом. Затем оператор присоединяет этот список дат к таблице журнала на основе даты метки времени журнала, сохраняя при этом даты, которые не имеют записей журнала, используя левое соединение (принимает все строки с "левой" стороны, имеют ли они совпадающие строки с "правой" стороны или нет). Наконец, мы группируем по дате и подсчитываем вместо этого, и мы должны получить ответ, который вы хотите.


когда нет данных для подсчета, нет строки для возврата.

Если вы хотите включить пустые дни в качестве 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)) 

использовать левое внешнее соединение. Такие как

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

недавно у меня была аналогичная задача, и я использовал ее в качестве фона для своей работы. Однако, как объяснил робуильямс, я тоже не смог заставить его работать. Моя задача была немного другой, я делал это часами против дней, но я думаю, что решение вопроса 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)