用于在特定时间段内获取活动员工的SQL查询

8 sql sql-server sql-server-2008

有下表:

    ID     EmployeeID      Status       EffectiveDate
  ------------------------------------------------------
     1       110545        Active        01AUG2011
     2       110700        Active        05JAN2012
     3       110060        Active        05JAN2012
     4       110222        Active        30JUN2012
     5       110545        Resigned      01JUL2012
     6       110545        Active        12FEB2013
Run Code Online (Sandbox Code Playgroud)

如何获取特定时间段内的活动(或部分活动)数量?例如,如果我想知道所有活跃(或部分活跃)的员工01JAN2011,01AUG2012我应该得到4(根据上表).如果我想知道所有在职职工01AUG201201JAN2013它应该是3只(因为员工110454被辞职).

我该怎么做?

小智 8

样本数据:

CREATE TABLE #Employee
(
    ID              integer NOT NULL,
    EmployeeID      integer NOT NULL,
    [Status]        varchar(8) NOT NULL,
    EffectiveDate   date NOT NULL,

    CONSTRAINT [PK #Employee ID]
        PRIMARY KEY CLUSTERED (ID)
);

INSERT #Employee
    (ID, EmployeeID, [Status], EffectiveDate)
VALUES
     (1, 110545, 'Active', '20110801'),
     (2, 110700, 'Active', '20120105'),
     (3, 110060, 'Active', '20120105'),
     (4, 110222, 'Active', '20120630'),
     (5, 110545, 'Resigned', '20120701'),
     (6, 110545, 'Active', '20130212');
Run Code Online (Sandbox Code Playgroud)

有用的索引:

CREATE NONCLUSTERED INDEX Active
ON #Employee
    (EffectiveDate)
INCLUDE
    (EmployeeID)
WHERE
    [Status] = 'Active';

CREATE NONCLUSTERED INDEX Resigned
ON #Employee
    (EmployeeID, EffectiveDate)
WHERE
    [Status] = 'Resigned';
Run Code Online (Sandbox Code Playgroud)

带有注释的解决方案:

CREATE TABLE #Selected (EmployeeID integer NOT NULL);

DECLARE 
    @start date = '20110101',
    @end   date = '20120801';

INSERT #Selected (EmployeeID)
SELECT
    E.EmployeeID
FROM #Employee AS E
WHERE
    -- Employees active before the end of the range
    E.[Status] = 'Active'
    AND E.EffectiveDate <= @end
    AND NOT EXISTS
    (
        SELECT * 
        FROM #Employee AS E2
        WHERE
            -- No record of the employee
            -- resigning before the start of the range
            -- and after the active date
            E2.EmployeeID = E.EmployeeID
            AND E2.[Status] = 'Resigned'
            AND E2.EffectiveDate >= E.EffectiveDate
            AND E2.EffectiveDate <= @start
    )
OPTION (RECOMPILE);

-- Return a distinct list of employees
SELECT DISTINCT
    S.EmployeeID 
FROM #Selected AS S;
Run Code Online (Sandbox Code Playgroud)

执行计划:

执行计划

SQLFiddle在这里