2017-08-04 86 views
2

我正在尋找解決這個問題的方法。我有一張名爲LogEntry的表格,用於存儲多個辦公室使用的信息,他們必須在任何給定日期記錄任何進入他們辦公室的訪客。如果沒有遊客進入,他們仍然需要在當天登記「無遊客」。我如何運行一個查詢來提取辦公室未能創建「No Visitors」日誌的所有日期?獲取SQL Server中沒有條目的日期列表

我已經看過this question(和內部鏈接的文章),但即使調整該查詢,我也只能爲辦公室缺少日期條目的日期創建空行,而不指定沒有創建條目的實際辦公室。有沒有辦法做我想做的事情?

declare @temp table (

CDate datetime, 
loc_id varchar(50) 
) 

insert into @temp SELECT DISTINCT entryDate, locationID FROM LogEntry WHERE entryDate >= '05/01/2017' AND entryDate <= '07-31-2017' 

;with d(date) as (
    select cast('05/01/2017' as datetime) 
    union all 
    select date+1 
    from d 
    where date < '07/31/2017' 
) 

select DISTINCT t.loc_id, CONVERT(date, d.date) 
    FROM d LEFT OUTER JOIN @temp t ON d.date = t.CDate 
    GROUP BY t.loc_id, d.date 
    ORDER BY t.loc_id 

正如我所說的,這個查詢將返回我的日期範圍中的列表,並提交了一份關於該日期的條目的所有位置,但我想找到一種方法,本質上提取相反的信息:如果某個辦公室(由locationID指定)未在指定日期提交條目,則只返回那些locationID和他們錯過的日期。快一點

select t.loc_id, CONVERT(date, d.date) 
FROM d 
    -- Cross join dates to all available locs 
    CROSS JOIN (SELECT DISTINCT loc_id FROM @temp) AS Locs 
    LEFT JOIN 
     (SELECT loc_id, t.CDate 
     FROM @temp 
     GROUP BY loc_id, d.date) AS t ON d.date = t.CDate AND Locs.loc_id = t.loc_id 
ORDER BY Locs.loc_id 

這應該是::

Sample data 

EntryID | locationID | entryDate 
================================= 
1   1   07-01-2017 
2   1   07-02-2017 
3   2   07-02-2017 
4   1   07-04-2017 

Expected Result (for date range of 07-01 to 07-04) 

locationID | missedEntryDate 
============================ 
1   07-03-2017 
2   07-01-2017 
2   07-03-2017 
2   07-04-2017 
+0

「DISTINCT」無關緊要,並且使用「RIGHT JOIN」而不是「LEFT JOIN」。 – Siyual

+0

我添加了不同,因爲一個位置可以爲給定日期創建多個條目;我只知道他們創造了至少一個。 –

+2

我的意思是,「GROUP BY」將固有地使查詢「DISTINCT」。 – Siyual

回答

0

如果數據集不是太大,你可以試試這個

;WITH cte AS (
    SELECT a.LocID, RangeStart.CDate, (CASE WHEN Input.LocID IS NULL THEN 1 ELSE 0 END) AS IsMissing 
    FROM (SELECT DISTINCT LocID FROM #temp) AS a 
    CROSS JOIN (SELECT CONVERT(DATETIME, '2017-05-01') AS CDate) AS RangeStart 
    LEFT JOIN 
      (SELECT LocID, MIN(CDate) AS CDate 
      FROM #temp 
      WHERE CDate = '2017-05-01' 
      GROUP BY LocID) AS Input ON a.LocID = Input.LocID AND RangeStart.CDate = Input.CDate 
    UNION ALL 
    SELECT a.LocID, a.CDate + 1 AS CDate, 
     ISNULL(ItExists, 0) AS IsMissing 
    FROM cte AS a 
     OUTER APPLY(SELECT LocID, 1 AS ItExists FROM #temp AS b WHERE a.LocID = b.LocID AND a.CDate + 1 = b.CDate) AS c 
    WHERE a.CDate < '2017-07-01' 
) 
SELECT * FROM cte OPTION(MAXRECURSION 0) 

您還可以添加一個索引:

CREATE INDEX IX_tmp_LocID_CDate ON #temp(LocID, CDate) 

se的樣本數據集cond查詢:

CREATE TABLE #temp(LocID VARCHAR(50), CDate DATETIME) 
INSERT INTO #temp 
VALUES 
('1', '2017-05-01'), ('1', '2017-05-02'), ('1', '2017-05-03'), ('1', '2017-05-04'), ('1', '2017-05-05'), 
('2', '2017-05-01'), ('2', '2017-05-02'), ('2', '2017-05-03'), ('2', '2017-05-04'), ('2', '2017-05-05') 

;WITH d AS (
    SELECT CAST('05/01/2017' AS DATETIME) AS date 
    UNION ALL 
    SELECT date + 2 
    FROM d 
    WHERE date < '2018-07-31' 
) 
INSERT INTO #temp 
SELECT LocID, d.date 
FROM (SELECT DISTINCT LocID FROM #temp) AS a 
    CROSS JOIN d 
OPTION(MAXRECURSION 0) 
+0

起初由於格式不理解你的查詢。與我的非常相似。但有兩件事。 ** A)**你不需要'GROUP BY' ** B)**只需要返回'LEFT JOIN'中的'NULL'值來顯示哪些日期沒有任何輸入。 –

+0

@JuanCarlosOropeza - 良好的格式喜歡是主觀的 - 我不喜歡你的,沒有足夠的縮進爲我喜歡。 Re ** A **:我使用「Group By」作爲原始海報,在查詢中刪除了因** B **而重要的重複項。 Re ** B **:我沒有包含WHERE子句,以便OP可以比較輸出。基於這個問題,我認爲他有足夠的技巧可以自己解決這個問題。 – Alex

+0

我不是在批評你的格式,只是我起初不明白的事實;) –

2

您的第一步很好,您創建了所有日期的列表,但您還需要所有位置的列表。然後創建一個交叉連接以獲得所有組合,然後執行left join以查找缺少的內容。

;with allDates(date) as (
    select cast('05/01/2017' as datetime) 
    union all 
    select date+1 
    from d 
    where date < '07/31/2017' 
), allLocations as (
    SELECT DISTINCT loc_id 
    FROM @temp 
), allCombinations as ( 
    SELECT date, loc_id 
    FROM allDates 
    CROSS JOIN allLocations 
) 
SELECT AC.loc_id, AC.date 
FROM allCombinations AC 
LEFT JOIN @temp t 
    ON AC.date = t.CDate 
AND AC.loc_id = t.loc_id 
WHERE t.loc_id IS NULL -- didnt find a match on @temp 
+0

請注意,您不需要多個CTE,您可以使用更直接的查詢作爲@Alex答案,但是我想分開每一步,因此很容易理解需要什麼做完了。 –

+0

爲什麼在連接上將'AC.loc_id'與't.CDate'匹配? –

+0

錯字,我做了一個複製粘貼..只需糾正它。 –

相關問題