2011-02-23 97 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