[SQL]組合具有連續性的資料~如員工經歷
最近看到了 獨行俠 大大的「利用流水號(row_number)以及累加語法,組合具有連續性的資料~如員工經歷」,感覺應該還可以改用其他的方式。
所以就沿用他的範例來練習 ^_^。
--準備測試資料 WK1
SELECT '001' As EmpId,'王小明' As Name,'HR' As Dep,'PG1' As Title,convert(DATETIME,'2001/01/01') As HirDate INTO WK1
UNION
SELECT '001' As EmpId,'王小明' As Name,'HR' As Dep,'PG2' As Title,'2002/01/01' As HirDate
Union
SELECT '001' As EmpId,'王小明' As Name,'HR' As Dep,'SD1' As Title,'2003/07/01' As HirDate
Union
SELECT '001' As EmpId,'王小明' As Name,'GL' As Dep,'SD1' As Title,'2005/02/01' As HirDate
Union
SELECT '001' As EmpId,'王小明' As Name,'GL' As Dep,'SD2' As Title,'2007/01/01' As HirDate
Union
SELECT '002' As EmpId,'李小寶' As Name,'KM' As Dep,'SA1' As Title,'2001/01/01' As HirDate
UNION
SELECT '002' As EmpId,'李小寶' As Name,'KM' As Dep,'SA1' As Title,'2002/01/01' As HirDate
Union
SELECT '002' As EmpId,'李小寶' As Name,'TR' As Dep,'SA2' As Title,'2003/07/01' As HirDate
Union
SELECT '002' As EmpId,'李小寶' As Name,'TR' As Dep,'PM2' As Title,'2005/02/01' As HirDate
要組出連續性的資料,馬上就想到使用SQL 2012新提供的函數(LEAD or LAG)來做,如下,
SELECT EmpId ,
Name ,
Dep ,
HirDate ,
ISNULL(LEAD(HirDate) OVER ( PARTITION BY EmpId ORDER BY HirDate ),
CAST('2990/10/10' AS DATETIME)) AS NextHirDate
FROM WK1
但如果是非SQL 2012呢? 那就使用SUBQUERY來做。如下
SELECT EmpId ,
Name ,
Dep ,
HirDate ,
( SELECT ISNULL(MIN(HirDate), CAST('2990/10/10' AS DATETIME))
FROM dbo.WK1 t2
WHERE t1.EmpId = t2.EmpId
AND t2.HirDate > t1.HirDate
) AS NextHirDate
FROM WK1 t1
ORDER BY t1.EmpId, t1.HirDate
以日期(HirDate)組出連續資料後,要再來看各員工在各部門的最小及最大日期,來組出「員工經歷」。如下,
SELECT EmpId ,
Name ,
Dep ,
CONVERT(VARCHAR, MIN(HirDate), 111) AS HirDate ,
MAX(CASE NextHirDate
WHEN CAST('2990/10/10' AS DATETIME) THEN 'UNTIL NOW'
ELSE CONVERT(VARCHAR, DATEADD(DAY, -1, NextHirDate), 111)
END) NextHirDate
FROM ( SELECT EmpId ,
Name ,
Dep ,
HirDate ,
( SELECT ISNULL(MIN(HirDate),
CAST('2990/10/10' AS DATETIME))
FROM dbo.WK1 t2
WHERE t1.EmpId = t2.EmpId
AND t2.HirDate > t1.HirDate
) AS NextHirDate
FROM WK1 t1
) T3
GROUP BY EmpId ,
Name ,
Dep
ORDER BY EmpId ,
HirDate
看似OK的結果,但 獨行俠 大大說:
在下面的狀況可能會失真喔
1~3月:A部門
4~8月:B部門
9~12月:又回A部門,
取Min Max 生效日,Group By部門可能會有問題ㄟ ^^
嗯,的確會有獨行俠 大大說的那種狀況,再把資料加入以上狀況的資料,如下。
--準備測試資料 WK1
DROP TABLE WK1
SELECT '001' As EmpId,'王小明' As Name,'HR' As Dep,'PG1' As Title,convert(DATETIME,'2001/01/01') As HirDate INTO WK1
UNION
SELECT '001' As EmpId,'王小明' As Name,'HR' As Dep,'PG2' As Title,'2002/01/01' As HirDate
Union
SELECT '001' As EmpId,'王小明' As Name,'HR' As Dep,'SD1' As Title,'2003/07/01' As HirDate
Union
SELECT '001' As EmpId,'王小明' As Name,'GL' As Dep,'SD1' As Title,'2005/02/01' As HirDate
Union
SELECT '001' As EmpId,'王小明' As Name,'GL' As Dep,'SD2' As Title,'2007/01/01' As HirDate
Union
SELECT '001' As EmpId,'王小明' As Name,'HR' As Dep,'SD3' As Title,'2009/01/01' As HirDate
Union
SELECT '001' As EmpId,'王小明' As Name,'HR' As Dep,'SWA1' As Title,'2010/08/01' As HirDate
Union
SELECT '002' As EmpId,'李小寶' As Name,'KM' As Dep,'SA1' As Title,'2001/01/01' As HirDate
UNION
SELECT '002' As EmpId,'李小寶' As Name,'KM' As Dep,'SA1' As Title,'2002/01/01' As HirDate
Union
SELECT '002' As EmpId,'李小寶' As Name,'TR' As Dep,'SA2' As Title,'2003/07/01' As HirDate
Union
SELECT '002' As EmpId,'李小寶' As Name,'TR' As Dep,'PM2' As Title,'2005/02/01' As HirDate
Union
SELECT '002' As EmpId,'李小寶' As Name,'TR' As Dep,'PM3' As Title,'2007/02/01' As HirDate
Union
SELECT '003' As EmpId,'郭小玉' As Name,'MIS' As Dep,'MG1' As Title,'2009/01/01' As HirDate
Union
SELECT '003' As EmpId,'郭小玉' As Name,'TO' As Dep,'MG2' As Title,'2010/01/01' As HirDate
所以單以部門及員工來GROUP是不行的.....
SELECT EmpId ,
Name ,
Dep ,
CONVERT(VARCHAR, MIN(HirDate), 111) AS HirDate ,
MAX(CASE NextHirDate
WHEN CAST('2990/10/10' AS DATETIME) THEN 'UNTIL NOW'
ELSE CONVERT(VARCHAR, DATEADD(DAY, -1, NextHirDate), 111)
END) NextHirDate
FROM ( SELECT EmpId ,
Name ,
Dep ,
HirDate ,
( SELECT ISNULL(MIN(HirDate),
CAST('2990/10/10' AS DATETIME))
FROM dbo.WK1 t2
WHERE t1.EmpId = t2.EmpId
AND t2.HirDate > t1.HirDate
) AS NextHirDate
FROM WK1 t1
) T3
GROUP BY EmpId ,
Name ,
Dep
ORDER BY EmpId ,
HirDate
那就再加一個區分的FLAG,一樣是用SUBQUERY來算出要區分的FLAG(請注意SQL中DiffFlag),再加入區分的FLAG來Group應該就可以了吧,如下,
SELECT EmpId ,
Name ,
Dep ,
CONVERT(VARCHAR, MIN(HirDate), 111) AS HirDate ,
MAX(CASE NextHirDate
WHEN CAST('2990/10/10' AS DATETIME) THEN 'UNTIL NOW'
ELSE CONVERT(VARCHAR, DATEADD(DAY, -1, NextHirDate), 111)
END) NextHirDate
FROM ( SELECT EmpId ,
Name ,
Dep ,
HirDate ,
( SELECT ISNULL(MIN(HirDate),
CAST('2990/10/10' AS DATETIME))
FROM dbo.WK1 t2
WHERE t1.EmpId = t2.EmpId
AND t2.HirDate > t1.HirDate
) AS NextHirDate ,
( SELECT COUNT(t2.Dep)
FROM dbo.WK1 t2
WHERE t1.EmpId = t2.EmpId
AND t2.HirDate > t1.HirDate
AND t2.Dep <> t1.Dep
) AS DiffFlag
FROM WK1 t1
) T3
GROUP BY EmpId ,
Name ,
Dep ,
DiffFlag
ORDER BY EmpId ,
HirDate
...…….
那除了看員工進出部門的時間外,如果要再看員工Title的變換呢???
一樣,修改區分的FLAG,加入Title的條件即可,如下,
SELECT EmpId ,
Name ,
Dep ,
Title ,
CONVERT(VARCHAR, MIN(HirDate), 111) AS HirDate ,
MAX(CASE NextHirDate
WHEN CAST('2990/10/10' AS DATETIME) THEN 'UNTIL NOW'
ELSE CONVERT(VARCHAR, DATEADD(DAY, -1, NextHirDate), 111)
END) NextHirDate
FROM ( SELECT EmpId ,
Name ,
Dep ,
Title ,
HirDate ,
( SELECT ISNULL(MIN(HirDate),
CAST('2990/10/10' AS DATETIME))
FROM dbo.WK1 t2
WHERE t1.EmpId = t2.EmpId
AND t2.HirDate > t1.HirDate
) AS NextHirDate ,
( SELECT COUNT(t2.Dep)
FROM dbo.WK1 t2
WHERE t1.EmpId = t2.EmpId
AND t2.HirDate > t1.HirDate
AND t2.Dep <> t1.Dep
AND t2.Title <> t1.Title
) AS DiffFlag
FROM WK1 t1
) T3
GROUP BY EmpId ,
Name ,
Dep ,
Title ,
DiffFlag
ORDER BY EmpId ,
HirDate
所以,以上的這種狀況,要記得加入「區分的FLAG」來處理哦。
如果大家有其他的方式,請告訴我哦。
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^