2011-02-23 145 views
4

我有以下的架構,我已經略微簡化:SQL返回缺少的行

CREATE TABLE [dbo].[Header] (
    [HeaderId] [int] IDENTITY(1,1) NOT NULL, 
    [StaffId] [int] NOT NULL, 
    [WeekEnding] [smalldatetime] NOT NULL, 
    ... 
) 

CREATE TABLE [dbo].[Staff] (
    [StaffId] [int] NOT NULL, 
    [FirstWeekEnding] [smalldatetime] NULL, 
    ... 
) 

,STAFFID頁眉表的外鍵。

標題表跟蹤與員工相關的數據(在未顯示的子表中),並且對於給定的星期,如果子表中的數據存在,則將存在用於「週末」的條目。在這種情況下,「週末」總是一個星期天。因此,樣本數據看起來是這樣的:

HeaderId StaffId WeekEnding 
--------------------------------- 
1   1   13/02/2011 
2   1   20/02/2011 
etc... 

對員工表中的「FirstWeekEnding」值是他們開始在Header表跟蹤信息的第一次約會。

我的問題

考慮的第一個星期結束的每一個工作人員的日期,我該如何構造查詢,這將使我從報頭表中的所有缺失的記錄到現在的日期?

例如,給出以下數據:

StaffId  FirstWeekEnding 
--------------------------- 
1   02/01/2011 

HeaderId StaffId WeekEnding 
--------------------------------- 
1   1   02/01/2011 
2   1   09/01/2011 
3   1   16/01/2011 
4   1   13/02/2011 
5   1   20/02/2011 

其結果將是:

StaffId WeekEnding 
--------------------- 
1   23/01/2011 
1   30/01/2011 
1   06/02/2011 

理想情況下,查詢應該處理多個工作人員,由他們進行分組STAFFID。

+7

請郵寄到現在爲止您編寫的代碼。人們通常不喜歡只爲你寫代碼。事實上,這是一個工作描述,而不是一個問題。 – 2011-02-23 00:09:22

+3

@Mitch:這是一個有效的IMO問題。 – mellamokb 2011-02-23 00:12:46

+2

@mellamokb:沒有任何海報試圖解決問題的證據,它只是一個工作描述。 – 2011-02-23 00:15:41

回答

2

假設你使用SQL 2005+你可以生成所有每位員工的周嘗試加入反對Header表的CTE(有點像,但也許不完全是這樣):

;WITH cte AS 
(SELECT StaffId, FirstWeekEnding AS WeekEnding 
    FROM STAFF 
    UNION ALL 
    SELECT StaffId, DATEADD(D, 7, WeekEnding) FROM cte 
    WHERE DATEADD(D, 7, WeekEnding) <= GETDATE() 
) 
SELECT StaffId, WeekEnding 
FROM cte LEFT JOIN Header ON cte.StaffId = Header.StaffId AND cte.WeekEnding = Header.WeekEnding 
WHERE Header.WeekEnding IS NULL 
OPTION (MAXRECURSION 32767) 
+0

每個工作人員休息的聖誕節周呢?我認爲你需要在這一年中詳細列出幾周來保證安全。 – 2011-02-23 00:24:54

+0

離開的星期列表將更加簡單和高效。 – mellamokb 2011-02-23 00:25:52

+0

@BobTodd你誤會了。這是自從他們自己的第一次交易以來每個工作人員所有星期的清單。絕對不是最節省空間的。 – 2011-02-23 00:28:32

3

您需要一種生成日期序列的方法。例如參見http://syntaxhelp.com/SQLServer/Recursive_CTE。然後搜索日期序列中沒有匹配記錄的條目。

DECLARE @startDate DATETIME, @endDate DATETIME 
SELECT @startDate = '2011-01-02', @endDate = GETDATE() 

;WITH DateSeries AS (
    SELECT @startDate AS dt 
    UNION ALL 
    SELECT dt + 7 FROM DateSeries  -- using 7 for weekly interval 
    WHERE dt + 7 <= @endDate 
) 
SELECT 
    * 
FROM 
    DateSeries ds 
LEFT JOIN 
    (your data here) t 
ON 
    ds.dt = t.WeekEnding 
WHERE 
    t.WeekEnding IS NULL 
+0

很優雅。很高興我掛了。我學到了東西:) – 2011-02-23 00:41:38

+0

感謝您的回答。 – 2011-02-23 01:09:48

1

好,所以我建議創建一個日曆表並加入。

這裏的最終查詢:

select StaffID, BaseDate 
From (
select 
StaffID, 
BaseDate, 
(
    Select count(*) 
    from Header h 
    where h.WeekEnding = c.BaseDate And h2.StaffID = h.StaffID 
) as Count 
From Header h, Calendar c) as Subquery 
Where Count = 0 

和歷:

create table Calendar (
BaseDate datetime primary key, 
DayOfWeek varchar(10) not null, 
WeekOfYear int not null, 
MonthOfYear varchar(10) not null, 
Quarter int not null 
/* Add any other useful columns */ 
) 
go 

declare @d datetime 
set @d = '20090101' 
while @d < '20250101' 
begin 
insert into dbo.Calendar values (
@d, 
datename(dw, @d), 
datepart(ww, @d), 
datename(mm, @d), 
datepart(qq, @d)) 
set @d = dateadd(dd, 1, @d) 
end 
go 

select * 
from dbo.Calendar 
where DayOfWeek = 'Sunday' and 
BaseDate between '20090101' and '20250101' 
go 
+0

感謝您的回答。 – 2011-02-23 01:00:17