Найти вторую дату встречи для каждого клиента - PullRequest
3 голосов
/ 03 января 2012

Это плохо сформулированный заголовок, но я не могу придумать ничего лучшего, извините!

У нас есть таблица, которая выглядит примерно так (обрезано для краткости):

create table Appointment (
    AppointmentId       int     not null identity(1, 1),
    CustomerId          int     not null,
    AppointmentDate     date    not null,

    constraint PK_Appointment primary key (AppointmentId),
    constraint FK_Appointment_Customer foreign key (CustomerId) references Customer(CustomerId)
)

Мы пытаемся написать запрос, в котором будут найдены сведения обо всех клиентах, у которых была ВТОРАЯ встреча в заданном диапазоне дат. Обратите внимание, что клиенты могли иметь две встречи в одну и ту же дату.

Мы можем сделать это, используя несколько CTE, но я уверен, что есть лучший способ (возможно, с использованием некоторой функции типа row_number?). Какие-либо предложения? Что мне действительно не нравится в нашем решении, так это то, что оно абсолютно негибкое (что происходит, когда они хотят увидеть встречу THIRD в заданном диапазоне дат и т. Д.).

Во всяком случае; вот что мы придумали:

declare @startDate date = '2011-12-01'
declare @endDate date = '2011-12-31'
;
-- Limit to appointments before the end date
with AppointmentDates as (
    select
        AppointmentId,
        CustomerId,
        AppointmentDate
    from
        Appointment
    where
        AppointmentDate < @endDate
),

-- Get first appointment date - careful to cater for customers who have had
-- two appointments on the same day
FirstAppointments as (
    select
        CustomerId,
        Min(AppointmentId) AppointmentId,
        Min(AppointmentDate) AppointmentDate
    from
        AppointmentDates
    group by
        CustomerId
),

-- Second appointment date
SecondAppointments as (
    select
        AppointmentDates.CustomerId,
        Min(AppointmentDates.AppointmentId) AppointmentId,
        Min(AppointmentDates.AppointmentDate) AppointmentDate
    from
        AppointmentDates
        inner join FirstAppointments on AppointmentDates.CustomerId = FirstAppointments.CustomerId
    where
        AppointmentDates.AppointmentId > FirstAppointments.AppointmentId
    group by 
        AppointmentDates.CustomerId
    having
        Min(AppointmentDates.AppointmentDate) > @startDate
)

-- Bulk of query goes here; trimmed for brevity
select * from SecondAppointments

Ответы [ 2 ]

2 голосов
/ 03 января 2012

Да, используя ROW_NUMBER(), вы сможете решить эту проблему намного проще:

;WITH ranked AS (
  SELECT
    CustomerId,
    AppointmentId,
    AppointmentDate,
    VisitNumber = ROW_NUMBER() OVER (PARTITION BY CustomerId
                                         ORDER BY AppointmentDate)
  FROM AppointmentDates
)
SELECT
  CustomerId,
  AppointmentId,
  AppointmentDate
FROM ranked
WHERE VisitNumber     =  @visitNumber
  AND AppointmentDate >= @startDate
  AND AppointmentDate <  @endDate
0 голосов
/ 03 января 2012

Вложенные использования TOP должны это. Обратите внимание, что порядок сортировки во внутреннем цикле по убыванию. Так что внешний цикл получает первую запись в порядке убывания. Это должно работать для поиска любой n-й позиции.

SELECT TOP 1 * 
FROM (
    SELECT TOP 2 * 
    FROM employee
    GROUP BY CustomerID
    ORDER BY AppointmentDate ASC) a
GROUP BY CustomerID
ORDER BY AppointmentDate DESC

Обратите внимание, что я НЕ проверял этот SQL.

Исправлено, чтобы отражать второе приложение, а не второе самое последнее приложение.

Надеюсь, это поможет.

...