排除另一个表

时间:2017-10-31 15:55:16

标签: sql sql-server

我有两张桌子,一张是工作模式,另一张是缺席。

1)工作模式

ID | Shift Start | Shift End
123| 01-03-2017  | 02-03-2017

2)缺席

ID| Absence Start | Absence End
123| 01-03-2017   | 04-03-2017

从工作模式中选择行时,排除任何在缺席表中标记为缺席的日期的最佳方法是什么?

例如,我有一份报告,它使用工作模式表来计算员工每周工作的天数,但我不希望它包含已标记为缺勤的日期。缺席表是否有意义?也不希望它包括在缺席开始和缺席结束日期之间的任何日期?

4 个答案:

答案 0 :(得分:4)

如果{ ContentType: "image/jpeg; charset=utf-8", 'x-amz-acl': 'public-read' }的范围始终包含要排除的班次,您可以使用absence

not exists()

rextester演示:http://rextester.com/DCODC76816

返回:

select *
from WorkPatterns w
where not exists (
  select 1
  from Absences a
  where a.Id = w.Id
    and a.AbsenceStart <= w.ShiftStart
    and a.AbsenceEnd   >= w.ShiftEnd
  )

给出此测试设置:

+-----+------------+------------+
| id  | ShiftStart |  ShiftEnd  |
+-----+------------+------------+
| 123 | 2017-02-27 | 2017-02-28 |
| 123 | 2017-03-05 | 2017-03-06 |
+-----+------------+------------+

答案 1 :(得分:1)

  

从工作模式中选择行时,最好的方法是什么

如果您只处理白色日期(没有时间)并控制数据库架构, 一种方法是创建日历表, 您将在公司成立以来的所有日期和未来几年的所有日期 填写该表一次。 之后很容易加入其他表格的白日期并做数学。

如果您在构建TSQL查询时遇到问题,请编辑问题,详细了解表格,关系和所需结果的列和值。

答案 2 :(得分:0)

这个怎么样:

SELECT WP_START.[id], WP_START.[shift_start], WP_START.[shift_end]
FROM work_pattern AS WP_START
INNER JOIN absences AS A ON WP_START.id = A.id 
WHERE WP_START.[shift_start] NOT BETWEEN A.[absence_start] AND A.[absence_end]

UNION

SELECT WP_END.[id], WP_END.[shift_start], WP_END.[shift_end]
FROM work_pattern AS WP_END
INNER JOIN absences AS A ON WP_END.id = A.id 
WHERE WP_END.[shift_end] NOT BETWEEN A.[absence_start] AND A.[absence_end]

在SQL小提琴上看到它:http://sqlfiddle.com/#!6/49ae6/6

答案 3 :(得分:0)

这是我的示例,其中包含Date Dimension表。如果您的DBA不能添加它,您可以创建#dateDim作为临时表,就像我用SQLFiddle 做的那样(我不知道我能做什么那)。典型的日期维度将包含有关日期的更多详细信息,但如果无法添加表格,只需使用您需要的内容即可。您必须填写您需要的其他假期。我经常使用的DateDim是https://github.com/shawnoden/SQL_Stuff/blob/master/sql_CreateDateDimension.sql

SQL Fiddle

MS SQL Server 2014架构设置

/* Tables for your test data. */
CREATE TABLE WorkPatterns ( id int, ShiftStart date, ShiftEnd date ) ;
INSERT INTO WorkPatterns ( id, ShiftStart, ShiftEnd )
VALUES
      (123, '20170101', '20171031')
    , (124, '20170601', '20170831')
;

CREATE TABLE Absences ( id int, AbsenceStart date, AbsenceEnd date ) ;
INSERT INTO Absences ( id, AbsenceStart, AbsenceEnd )
VALUES 
      ( 123, '20170123', '20170127' )
    , ( 123, '20170710', '20170831' )
    , ( 124, '20170801', '20170820' )
;

/* ******** MAKE SIMPLE CALENDAR TABLE ******** */

CREATE TABLE dateDim (
      theDate             DATE        NOT NULL
    , IsWeekend           BIT         DEFAULT 0
    , IsHoliday           BIT         DEFAULT 0
    , IsWorkDay           BIT         DEFAULT 0
);

/* Populate basic details of dates. */
INSERT dateDim(theDate, IsWeekend, IsHoliday) 
SELECT d
  , CONVERT(BIT, CASE WHEN DATEPART(dw,d) IN (1,7) THEN 1 ELSE 0 END)
  , CONVERT(BIT, CASE WHEN d = '20170704' THEN 1 ELSE 0 END) /* 4th of July. */
FROM (
    SELECT d = DATEADD(DAY, rn - 1, '20170101')
    FROM 
    (
        SELECT TOP (DATEDIFF(DAY, '20170101', '20171231')) 
            rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
        FROM sys.all_objects AS s1
        CROSS JOIN sys.all_objects AS s2
        ORDER BY s1.[object_id]
    ) AS x
) AS y ;

/* If not a weekend or holiday, it's a WorkDay. */
UPDATE dateDim 
SET IsWorkDay = CASE WHEN IsWeekend = 0 AND IsHoliday = 0 THEN 1 ELSE 0 END
;

查询计算

SELECT wp.ID, COUNT(d.theDate) AS workDayCount
FROM WorkPatterns wp
INNER JOIN dateDim d ON d.theDate BETWEEN wp.ShiftStart AND wp.ShiftEnd
  AND d.IsWorkDay = 1
LEFT OUTER JOIN Absences a ON d.theDate BETWEEN a.AbsenceStart AND a.AbsenceEnd
  AND wp.ID = a.ID
WHERE a.ID IS NULL
GROUP BY wp.ID
ORDER BY wp.ID

<强> Results

|  ID | workDayCount |
|-----|--------------|
| 123 |          172 |  << 216 total days, 44 non-working
| 124 |           51 |  <<  65 total days, 14 non-working