Слияние записей с последовательными датами в SQL - PullRequest
2 голосов
/ 01 декабря 2011

У меня есть следующая схема таблицы:

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 # для итерации DataSet или DataReader, но я надеюсь сделать это с T-SQL в хранимом процессе. Я нашел похожие вопросы в StackOverFlow и проверил их все, ни один из них не подходит для приведенной выше таблицы.

EDIT: Иногда у меня будут ситуации, подобные этой:

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

Как вы можете видеть, этот сотрудник отсутствовал с 11 сентября 2011 года (д / м / гггг) по (25 декабря 2011 года), но клиент запросил список отсутствия с 1 декабря по 31 декабря, Таким образом, результат должен быть:

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

Таким образом, в основном, он покажет результат в соответствии с предоставленными параметрами (от, до). Если запись сохранялась до запрошенного периода, она будет показывать ее, но в то же время она будет показывать начало записи в соответствии с предоставленными параметрами. То же самое применяется к окончаниям записей после параметров (from, to).

Ответы [ 3 ]

1 голос
/ 02 декабря 2011

Это даст вам количество дней, в течение которых каждый сотрудник не работал.Я думаю, что они хотели бы видеть это по диапазонам дат.Вы можете конвертировать 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 голос
/ 02 декабря 2011

Это 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 
        )

Теперь просто выбрать минимальное и максимальное значения, чтобы превратить его в диапазон.вывод, но необходим для группировки, в противном случае непоследовательные группы будут объединены в одну.

0 голосов
/ 01 декабря 2011
Select EmpId, AbsCode, MIN(DateFrom) as DateFrom, MAX(DateTo) as DateTo From YOURTABLE
Group By EmpId, AbsCode
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...