Построение матрицы дат сотрудников - PullRequest
1 голос
/ 09 марта 2020

У меня есть список сотрудников, а также список городов, в которых они работали. Мне нужно построить матрицу (на SQL сервере) дат начала / окончания по городам, чтобы определить, где они находились в любой данный период во времени.

«Датой окончания» будет именно та дата, до которой они появились в новом месте.

Я включил пример исходной таблицы и то, что должна выводить таблица появляются вместе с кодом, который будет создавать временную таблицу.

Любые рекомендации о том, как я могу разработать этот запрос и какие функции использовать?

ИСТОЧНИК:

EMP     DATE        LOCATION
-----------------------------------
Pinal   2020-01-01  Bangalore
Pinal   2020-01-02  Bangalore
Pinal   2020-01-04  Uttar Pradesh
Pinal   2020-01-06  Uttar Pradesh
Pinal   2020-01-20  Mumbai
Pinal   2020-01-22  Bangalore

Требуемый вывод запроса:

EMP     DATE_FROM   DATE_TO     LOCATION
----------------------------------------------
Pinal   2020-01-01  2020-01-03  Bangalore
Pinal   2020-01-04  2020-01-19  Uttar Pradesh
Pinal   2020-01-20  2020-01-21  Mumbai
Pinal   2020-01-22  2099-01-01  Bangalore


CREATE TABLE #EMP 
(
    EMP VARCHAR(30) NOT NULL  ,
    DATE_WORKED DATE NOT NULL ,
    CITY VARCHAR(30) NOT NULL
);

INSERT INTO #EMP (EMP, DATE_WORKED, CITY) 
VALUES 
('Pinal','2020-01-01','Bangalore'),
('Pinal','2020-01-02','Bangalore'),
('Pinal','2020-01-04','Uttar Pradesh'),
('Pinal','2020-01-06','Uttar Pradesh'),
('Pinal','2020-01-20','Mumbai'),
('Pinal','2020-01-22','Bangalore')

Ответы [ 3 ]

1 голос
/ 09 марта 2020

Вы можете использовать функции windows следующим образом:

with cte as
(

select distinct EMP, 
case when city = lag(CITY)over(partition by EMP order by DATE_WORKED) 
then lag(DATE_WORKED)over(partition by EMP order by DATE_WORKED) else 
 DATE_WORKED end as DATE_FROM, CITY 
 from 
#EMP a --order by DATE_WORKED
)
select EMP, CITY as LOCATION, DATE_FROM, 
case when dateadd(d,-1,lead(DATE_FROM)over(order by DATE_FROM)) is null 
then '2099-01-01' else dateadd(d,-1,lead(DATE_FROM)over(order by DATE_FROM)) 
 end as DATE_TO 
 from cte a 

Вывод:

EMP LOCATION            DATE_FROM   DATE_TO
Pinal   Bangalore       2020-01-01  2020-01-03
Pinal   Uttar Pradesh   2020-01-04  2020-01-19
Pinal   Mumbai          2020-01-20  2020-01-21
Pinal   Bangalore       2020-01-22  2099-01-01
1 голос
/ 09 марта 2020

Это класс c Gaps-and-Islands.

Здесь мы используем CROSS APPLY B для получения диапазона дат, а затем CROSS APPLY C в качестве ad-ho c таблицы подсчета

Пример

 Select Emp
      ,FromDate = min(D)
      ,ToDate   = max(D)
      ,City
 From (
Select *
      ,Grp = datediff(day,'1900-01-01',d) - row_number() over (partition by Emp,City Order By D)
 From  #Emp A
 Cross Apply (
               Select NextDate = IsNull(min(DateAdd(DAY,-1,Date_Worked)),'2025-01-01')
                From  #Emp 
                Where Emp=A.Emp and Date_Worked>A.Date_Worked
             ) B
 Cross Apply (
               Select Top (DateDiff(DAY,Date_Worked,NextDate)+1) 
                      D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),Date_Worked) 
                From  master..spt_values n1,master..spt_values n2
             ) C
 --Where EMP ='Victor'
) A
Group By Emp,City,Grp
Order by Emp,FromDate

Возвращает

Emp     FromDate    ToDate      City
Pinal   2020-01-01  2020-01-03  Bangalore
Pinal   2020-01-04  2020-01-19  Uttar Pradesh
Pinal   2020-01-20  2020-01-21  Mumbai
Pinal   2020-01-22  2025-01-01  Banga
Victor  2020-01-01  2020-01-18  NYC
Victor  2020-01-19  2020-01-24  San Fran
Victor  2020-01-25  2025-01-01  NYC
0 голосов
/ 09 марта 2020

Вы можете использовать рекурсивный cte для группировки последовательных строк вместе, а затем использовать функцию опережения для получения следующей даты.

Добавление row_number к данным:

select a.*,
       row_number() over (Partition by Emp Order by date_worked) as rownum
INTO #Emp1
From #Emp a

Рекурсивный cte для группы:

IF object_ID ('tempdb.dbo.#Temp1') is not null DROP TABLE #Temp1

DECLARE @int int=0

;WITH CTE as
(
SELECT *,1 as grp 
FROM #Emp1
WHERE rownum=1 

UNION ALL

SELECT t.*,
        CASE WHEN c.City=t.City then grp 
        else grp+1 end as grp
FROM #Emp1 t 
INNER JOIN CTE c
            ON t.rownum=c.rownum+1 and t.Emp=c.Emp
)

SELECT * 
INTO #temp1 
FROM CTE
OPTION (maxrecursion 0);

Ведущая функция для получения даты окончания:

SELECT emp,
       min(date_worked) as date_from,
       ISNULL(DATEADD(d,-1,lead(min(date_worked)) Over (Partition by emp Order by min(date_worked))),'01Jan2099') as date_to,
       City
from #temp1
Group by grp,emp,City

Надеюсь, это даст вам желаемый результат.

...