将记录与SQL中的连续日期合并

时间:2011-12-01 18:30:54

标签: sql-server tsql

我有以下表格架构:

RecordId    EmpID       AbsCode DateFrom    DateTo
---------------------------------------------------------------
666542      1511        AB      09/11/2011  10/11/2011
666986      1511        AB      11/11/2011  11/11/2011
666996      1511        EL      13/11/2011  17/11/2011
755485      1787        SL      01/11/2011  14/11/2011
758545      1787        SL      15/11/2011  26/11/2011
796956      1954        AB      09/11/2011  09/11/2011
799656      1367        AB      09/11/2011  09/11/2011
808845      1527        EL      16/11/2011  16/11/2011
823323      1527        EL      17/11/2011  17/11/2011
823669      1527        EL      18/11/2011  18/11/2011
899555      1123        AB      09/11/2011  09/11/2011
990990      1511        AB      12/11/2011  12/11/2011

如您所见,数据是为同一员工单独输入的。假设他报告SL(病假)一天,输入数据,然后他在第二天打电话报告另一天病假两天......等等。现在我想要的是在将这些条目呈现给客户端时合并这些条目,以便合并具有相同缺失代码的所有连续缺勤。例如,上面的表格应如下所示:

EmpID       AbsCode DateFrom    DateTo
-------------------------------------------------------------
1511        AB      09/11/2011  12/11/2011
1511        EL      13/11/2011  17/11/2011
1787        SL      01/11/2011  26/11/2011
1954        AB      09/11/2011  09/11/2011
1367        AB      09/11/2011  09/11/2011
1527        EL      16/11/2011  18/11/2011
1123        AB      09/11/2011  09/11/2011

我不是SQL人,我可以使用C#中的循环来迭代DataSetDataReader,但我希望在存储过程中使用T-SQL执行此操作。我在StackOverFlow中发现了类似的问题并对它们进行了全部检查,它们都不适用于上面的示例表。

修改 有时我会遇到这样的情况:

RecordId    EmpID       AbsCode DateFrom    DateTo
---------------------------------------------------------------
666542      1511        AB      09/11/2011  10/11/2011
666986      1511        AB      11/11/2011  25/12/2011

如您所见,此员工从2011年11月9日(d / M / yyyy)到(2011年12月25日)缺席,但客户要求从12月1日到12月31日有缺勤清单,所以结果应该是:

EmpID       AbsCode DateFrom    DateTo
-------------------------------------------------------------
1511        AB      01/12/2011  12/11/2011

基本上,它将根据提供的参数(from,to)显示结果。如果记录在请求的时间段之前盯着,它将显示它,但同时它将根据提供的参数显示记录的开始,同样适用于(from,to)参数之后的记录结尾。

3 个答案:

答案 0 :(得分:1)

这将为您提供每位员工休息的天数。我认为他们希望在日期范围内看到这一点。您可以使用以下代码将varchars转换为日期:DATEDIFF(DAY, CONVERT(DATETIME,[DateFrom],103), CONVERT(DATETIME,[DateTo],103))

DECLARE @myTable TABLE
(
    RecordId    INT,
    EmpID       INT,
    AbsCode     VARCHAR(2),
    DateFrom    VARCHAR(12),
    DateTo      VARCHAR(12)
)
INSERT INTO @myTable
(
    RecordId,
    EmpID,
    AbsCode,
    DateFrom,
    DateTo
)
SELECT 666542, 1511, 'AB', '09/11/2011', '10/11/2011' UNION ALL 
SELECT 666986, 1511, 'AB', '11/11/2011', '11/11/2011' UNION ALL 
SELECT 666996, 1511, 'EL', '13/11/2011', '17/11/2011' UNION ALL 
SELECT 755485, 1787, 'SL', '01/11/2011', '14/11/2011' UNION ALL 
SELECT 758545, 1787, 'SL', '15/11/2011', '26/11/2011' UNION ALL 
SELECT 796956, 1954, 'AB', '09/11/2011', '09/11/2011' UNION ALL 
SELECT 799656, 1367, 'AB', '09/11/2011', '09/11/2011' UNION ALL 
SELECT 808845, 1527, 'EL', '16/11/2011', '16/11/2011' UNION ALL 
SELECT 823323, 1527, 'EL', '17/11/2011', '17/11/2011' UNION ALL 
SELECT 823669, 1527, 'EL', '18/11/2011', '18/11/2011' UNION ALL 
SELECT 899555, 1123, 'AB', '09/11/2011', '09/11/2011' UNION ALL 
SELECT 990990, 1511, 'AB', '12/11/2011', '12/11/2011'


SELECT [RecordId], [EmpID], [AbsCode], SUM(DAYS) NoDays
FROM
(
    SELECT [RecordId], [EmpID], [AbsCode], DATEDIFF(DAY, CONVERT(DATETIME,[DateFrom],103), CONVERT(DATETIME,[DateTo],103)) Days
    FROM @myTable
    GROUP BY [RecordId], [EmpID], [AbsCode], DATEDIFF(DAY, CONVERT(DATETIME,[DateFrom],103), CONVERT(DATETIME,[DateTo],103))
) subQuery
GROUP BY [RecordId], [EmpID], [AbsCode]

答案 1 :(得分:1)

这是一个CTE所以它都需要一个一个地执行,但我会在我去的时候解释。

首先,我将设置我们感兴趣的日期范围的参数:

DECLARE @StartDate DateTime; SET @StartDate = '2011-11-01';  
DECLARE @EndDate DateTime; SET @EndDate = '2011-11-30';  

然后我会使用递归CTE将它们变成日期列表

WITH 
    ValidDates ( ValidDate ) AS 
        (
            SELECT @StartDate 
                UNION ALL
            SELECT DateAdd(day, 1, ValidDate) 
                FROM ValidDates 
                WHERE ValidDate < @EndDate
        ),

通过将其与原始记录中的范围相结合,我得到了个人缺席日期的列表。

使用row_number和datediff的组合我可以对连续日期进行分组。这假设没有重复。

    DaysAbsent AS 
        (
            SELECT 
                  A.RecordID
                , A.EmpID
                , A.AbsCode
                , DateDiff(Day, @StartDate, D.ValidDate) 
                    - row_number() 
                        over (partition by A.EmpID, A.AbsCode  
                            order by D.ValidDate) AS DayGroup
                , D.ValidDate AS AbsentDay
            FROM 
                dbo.Absence A
                    INNER JOIN  
                ValidDates D
                    ON D.ValidDate >= DateFrom 
                       and  D.ValidDate <= DateTo 
        )

现在这是一个简单的选择,使用min和max将其重新调整为范围。

SELECT 
      EmpID
    , AbsCode
    , MIN(AbsentDay) AS DateFrom
    , MAX(AbsentDay) AS DateTo
FROM
    DaysAbsent
GROUP BY
      EmpID
    , AbsCode
    , DayGroup

输出中不需要DayGroup,但分组需要DayGroup,否则非连续组将折叠成一个。

答案 2 :(得分:0)

Select EmpId, AbsCode, MIN(DateFrom) as DateFrom, MAX(DateTo) as DateTo From YOURTABLE
Group By EmpId, AbsCode