2013-03-12 3 views
3

Мне нужно сделать несколько соединений с одной и той же таблицей между (например) Person и PersonEvents. Для каждого человека существует несколько событий (0 или более). Мне нужно создать VIEW, который выбирает каждого человека с определенными столбцами из своего последнего события, а также столбцы из следующего самого последнего события.SQL с несколькими ROW_NUMBER или RANK

Person данные:

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 
.... 

Однако, если вам нужно несколько столбцов из этого Объединения (например, дата, комментарий и, возможно, дальнейшие идентификаторы), вам нужно сделать несколько инструкций подвыбора, что дорого.

Итак, вопрос: Как вы получаете несколько столбцов из соединения, используя номер строки для последних и предыдущих событий?

+0

почему -1 на этом? – thinkOfaNumber

ответ

4

Самый прямой (то есть читаемый SQL) ответ, который я использовал с использованием WITH и ROW_NUMBER.

Во-первых, сделать запрос row_number, что упорядочивает события и дает номер каждому событию уникальный для этой PersonId:

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 

Теперь, "первое" событие (в моем случае самое последнее) для каждого человека содержит дату внесения изменения (пример реальной жизни: это данные истории регистрации учащихся в нескольких школах, содержащие идентификатор школы и множество других guff). «Второе» событие для каждого человека содержит предыдущее событие и причину ухода. Для того, чтобы добавить его вместе:

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. От главного студенческого стола:
    • студент ID
    • имя
    • DOB и т.д.
  2. От Зачисление Таблица истории как «текущая регистрация»
    • Номер школы
    • различные регистрации Информация Статус
    • дата начала
  3. Из таблицы истории Зачисление как «предыдущий регистрации»
    • причина выхода

Этот метод является весьма эффективным с о 150 тыс. Студентов и их история.

полный SQL для моих тестов:

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 
+0

Возможно, я не указал, но мне потребовалось, чтобы все лица были возвращены независимо от событий. Кроме того, поскольку это VIEW, я не могу разделить его на отдельные команды выбора - в одной строке должны быть: id, name, MostRecentDate, Reason (со второй самой последней даты). – thinkOfaNumber

Смежные вопросы