18

SQL Server 2008 R2 を使用しています。次の構造とサンプルデータを持つ EmployeeHistory というテーブルがあります。

EmployeeID Date      DepartmentID SupervisorID
10001      20130101  001          10009
10001      20130909  001          10019
10001      20131201  002          10018
10001      20140501  002          10017
10001      20141001  001          10015
10001      20141201  001          10014

従業員 10001 は、時間の経過とともに 2 つの部門と複数の監督者を変更していることに注意してください。私がやろうとしているのは、この従業員の雇用の開始日と終了日を、日付フィールドで並べられた各部門でリストすることです。したがって、出力は次のようになります。

EmployeeID DateStart DateEnd  DepartmentID 
10001      20130101  20131201 001
10001      20131201  20141001 002
10001      20141001  NULL     001

次のクエリを使用してデータをパーティション分割するつもりでしたが、失敗しました。Department は 001 から 002 に変更され、次に 001 に戻ります。明らかに、DepartmentID でパーティション分割することはできません...明らかに見落としていると思います。何か助けはありますか?前もって感謝します。

SELECT * ,ROW_NUMBER() OVER (PARTITION BY EmployeeID, DepartmentID
ORDER BY [Date]) RN FROM EmployeeHistory
4

3 に答える 3

9

少し関与しています。最も簡単なのは、私が作成したこの SQL Fiddleを参照して、正確な結果を生成することです。パフォーマンスやその他の考慮事項のために改善する方法はありますが、これは少なくともいくつかの代替案よりも明確になるはずです。

要点は、最初にデータの正規ランキングを取得し、それを使用してデータをグループに分割し、次に各グループの終了日を見つけて、中間の行を削除することです。ROW_NUMBER() と CROSS APPLY は、読みやすくするのに大いに役立ちます。


編集2019:

実際、SQL Fiddle は何らかの理由で壊れているように見えますが、SQL Fiddle サイトでは問題のようです。SQL Server 2016 で今テストした完全なバージョンを次に示します。

CREATE TABLE Source
(
  EmployeeID int,
  DateStarted date,
  DepartmentID int
)

INSERT INTO Source
VALUES
(10001,'2013-01-01',001),
(10001,'2013-09-09',001),
(10001,'2013-12-01',002),
(10001,'2014-05-01',002),
(10001,'2014-10-01',001),
(10001,'2014-12-01',001)


SELECT *, 
  ROW_NUMBER() OVER (PARTITION BY EmployeeID ORDER BY DateStarted) AS EntryRank,
  newid() as GroupKey,
  CAST(NULL AS date) AS EndDate
INTO #RankedData
FROM Source
;

UPDATE #RankedData
SET GroupKey = beginDate.GroupKey
FROM #RankedData sup
  CROSS APPLY 
  (
    SELECT TOP 1 GroupKey
    FROM #RankedData sub 
    WHERE sub.EmployeeID = sup.EmployeeID AND
      sub.DepartmentID = sup.DepartmentID AND
      NOT EXISTS 
        (
          SELECT * 
          FROM #RankedData bot 
          WHERE bot.EmployeeID = sup.EmployeeID AND
            bot.EntryRank BETWEEN sub.EntryRank AND sup.EntryRank AND
            bot.DepartmentID <> sup.DepartmentID
        )
      ORDER BY DateStarted ASC
    ) beginDate (GroupKey);

UPDATE #RankedData
SET EndDate = nextGroup.DateStarted
FROM #RankedData sup
  CROSS APPLY 
  (
    SELECT TOP 1 DateStarted
    FROM #RankedData sub
    WHERE sub.EmployeeID = sup.EmployeeID AND
      sub.DepartmentID <> sup.DepartmentID AND
      sub.EntryRank > sup.EntryRank
    ORDER BY EntryRank ASC
  ) nextGroup (DateStarted);

SELECT * FROM 
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY GroupKey ORDER BY EntryRank ASC) AS GroupRank FROM #RankedData
) FinalRanking
WHERE GroupRank = 1
ORDER BY EntryRank;

DROP TABLE #RankedData
DROP TABLE Source
于 2013-11-12T05:53:13.200 に答える