4

Person と PersonEvents の間など、同じテーブルで複数の結合を行う必要があります。1 人につき複数のイベントがあります (0 以上)。最新のイベントからの特定の列と、次に新しいイベントからの列を含む各人物を選択する VIEW を作成する必要があります。

個人データ:

Id    Name
1     Iain
2     Fred
3     Mary
4     Foo
5     Bar

PersonEvents データ:

PersonId    DateStarted                ReasonForLeaving
1           2011-03-12 00:00:00.000    sick
1           2013-02-12 00:00:00.000    NULL
1           2012-04-12 00:00:00.000    holiday
2           2011-05-12 00:00:00.000    new baby
2           2013-06-12 00:00:00.000    NULL
2           2012-07-12 00:00:00.000    had enough
3           2011-08-12 00:00:00.000    pregnant
3           2013-09-12 00:00:00.000    NULL
4           2012-10-12 00:00:00.000    NULL

出力サンプルは次のようになります。

Id   Name    MemberSince                ReasonForChange
1    Iain    2011-03-12 00:00:00.000    holiday
4    Foo     2012-10-12 00:00:00.000    NULL
...

「古い方法」では、上位 1 結合またはサブ選択ステートメントが使用されました。

SELECT p.*,
    (
        SELECT TOP 1 DateStarted
        FROM PersonEvents e
        WHERE e.PersonId = p.Id
        ORDER BY DateFoo DESC
    ) As MemberSince
FROM Person p
....

ただし、この Join から複数の列が必要な場合 (たとえば、Date、Comment、および場合によってはその他の ID)、複数の sub-select ステートメントを実行する必要があり、コストがかかります。

問題は、最新および以前のイベントの行番号を使用して結合から複数の列を取得するにはどうすればよいかということです。

4

2 に答える 2

4

私が思いついた最も簡単な(つまり、読み取り可能なSQL)答えは、WITHとROW_NUMBERを使用しています。

最初に、イベントを並べ替え、その PersonId に固有の各イベントに番号を与える ROW_NUMBER クエリを作成します。

SELECT *,
    ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY DateStarted DESC) AS EventOrder
FROM PersonEvents

結果:

PersonId    DateStarted              ReasonForLeaving    EventOrder
1           2013-02-12 00:00:00.000  NULL                1
1           2012-04-12 00:00:00.000  holiday             2
1           2011-03-12 00:00:00.000  sick                3
2           2013-06-12 00:00:00.000  NULL                1
2           2012-07-12 00:00:00.000  had enough          2
2           2011-05-12 00:00:00.000  new baby            3
3           2013-09-12 00:00:00.000  NULL                1
3           2011-08-12 00:00:00.000  pregnant            2
4           2012-10-12 00:00:00.000  NULL                1

現在、すべての人の「最初の」イベント(私の場合は最新)には、変更が行われた日付が含まれています(実際の例:これは、複数の学校にまたがる学生の登録履歴データであり、学校 ID やその他の多くの情報が含まれています)。すべての人の「2 番目の」イベントには、前のイベントと離職理由が含まれています。一緒に追加するには:

WITH SortedEvents AS (
     SELECT *,
         ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY ReasonForLeaving DESC) AS EventOrder
     FROM PersonEvents
)
SELECT p.*, MostRecent.DateStarted AS MemberSince, NextRecent.ReasonForLeaving AS ReasonForChange
FROM Person p
     LEFT OUTER JOIN SortedEvents AS MostRecent ON p.Id = MostRecent.PersonId AND MostRecent.EventOrder = 1
     LEFT OUTER JOIN SortedEvents AS NextRecent ON p.Id = NextRecent.PersonId AND NextRecent.EventOrder = 2

きれいにフォーマットされた出力を提供します:

Id          Name   MemberSince              ReasonForChange
1           Iain   2013-02-12 00:00:00.000  holiday
2           Fred   2013-06-12 00:00:00.000  had enough
3           Mary   2013-09-12 00:00:00.000  pregnant
4           Foo    2012-10-12 00:00:00.000  NULL
5           Bar    NULL                     NULL

実際には、任意の行番号から複数の列を選択できます。実際の例 (ここでも、学生の登録履歴) は次のとおりです。

  1. マスター学生テーブルから:
    • 学生証
    • 名前
    • 生年月日など
  2. 登録履歴テーブルから「現在の登録」として
    • 学校ID
    • 各種在籍状況情報
    • 開始日
  3. 登録履歴テーブルから「前の登録」として
    • 辞めるわけ

この方法は、約 150,000 人の学生とそれぞれの履歴に対して非常に効率的です。

テスト用の完全な SQL:

CREATE TABLE Person
(
     Id INT NOT NULL,
     Name VARCHAR(50)
)
GO
CREATE TABLE PersonEvents
(
     PersonId INT NOT NULL,
     DateStarted DATETIME NOT NULL,
     ReasonForLeaving VARCHAR(50)
)
GO
INSERT INTO Person
     SELECT 1, 'Iain' UNION ALL
     SELECT 2, 'Fred' UNION ALL
     SELECT 3, 'Mary' UNION ALL
     SELECT 4, 'Foo'  UNION ALL
     SELECT 5, 'Bar'
GO
INSERT INTO PersonEvents
     SELECT 1, '20110312', 'sick'       UNION ALL
     SELECT 1, '20130212', NULL         UNION ALL
     SELECT 1, '20120412', 'holiday'    UNION ALL
     SELECT 2, '20110512', 'new baby'   UNION ALL
     SELECT 2, '20130612', NULL         UNION ALL
     SELECT 2, '20120712', 'had enough' UNION ALL
     SELECT 3, '20110812', 'pregnant'   UNION ALL
     SELECT 3, '20130912', NULL         UNION ALL
     SELECT 4, '20121012', NULL
GO

--SELECT *
--FROM Person
--SELECT *
--FROM PersonEvents
--GO
WITH SortedEvents AS (
    SELECT *,
        ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY DateStarted DESC) AS EventOrder
    FROM PersonEvents
)
SELECT p.*, MostRecent.DateStarted AS MemberSince, NextRecent.ReasonForLeaving AS ReasonForChange
FROM Person p
    LEFT OUTER JOIN SortedEvents AS MostRecent ON p.Id = MostRecent.PersonId AND MostRecent.EventOrder = 1
    LEFT OUTER JOIN SortedEvents AS NextRecent ON p.Id = NextRecent.PersonId AND NextRecent.EventOrder = 2
GO

SELECT p.*,
    (
        SELECT TOP 1 DateStarted
        FROM PersonEvents pe
        WHERE pe.PersonId = p.Id
        ORDER BY DateStarted DESC
    ) AS MemberSince,
    'unknown' AS ReasonForChange
FROM Person p
GO

DROP TABLE Person
DROP TABLE PersonEvents
GO
于 2013-03-12T01:22:36.413 に答える
0

最後のイベントと前のイベントの日付:

SELECT ID,NAME,NextToMostEventDate,ReasonForLeaving
FROM PersonEvents pe
INNER JOIN(
    SELECT pe1.PersonId,TheMostEventDate,NextToMostEventDate=MAX(pe1.DateStarted)
    FROM PersonEvents pe1
    INNER JOIN(
        SELECT PersonId,TheMostEventDate=MAX(DateStarted)
        FROM PersonEvents
        GROUP BY PersonId 
    ) pe2 
    ON pe2.PersonId=pe1.PersonId
    WHERE DateStarted<TheMostEventDate
    GROUP BY pe1.PersonId,TheMostEventDate
) pe12 ON pe12.PersonId=pe.PersonId
INNER JOIN Person ON Id=pe.PersonId
WHERE pe.DateStarted=TheMostEventDate

最後のイベントの日付と前のイベントの場合:

SELECT ID,NAME,TheMostEventDate,ReasonForLeaving
FROM PersonEvents pe
INNER JOIN(
    SELECT pe1.PersonId,TheMostEventDate,NextToMostEventDate=MAX(pe1.DateStarted)
    FROM PersonEvents pe1
    INNER JOIN(
        SELECT PersonId,TheMostEventDate=MAX(DateStarted)
        FROM PersonEvents
        GROUP BY PersonId 
    ) pe2 
    ON pe2.PersonId=pe1.PersonId
    WHERE DateStarted<TheMostEventDate
    GROUP BY pe1.PersonId,TheMostEventDate
) pe12 ON pe12.PersonId=pe.PersonId
INNER JOIN Person ON Id=pe.PersonId
WHERE pe.DateStarted=NextToMostEventDate
于 2013-03-12T03:23:21.297 に答える