SQL Server: СЛЕВА ВНЕШНЕЕ СОЕДИНЕНИЕ с TOP 1, чтобы выбрать не более одной строки - PullRequest
18 голосов
/ 07 марта 2012

Мне нужно сделать левое внешнее объединение для двух таблиц (CarePlan и Referrals), проблема в том, что мне нужен самый новый реферал Если он существует , то нормально, если нет.

Iвыполните эти 2 запроса: 1. присоединяется к таблицам CarePlan / Referral - создает дубликаты планов обслуживания, если для плана обслуживания есть несколько рефералов, или вообще не имеет реферальной информации (левое внешнее объединение) 2. выберите первого реферала на основе даты, учитываяCarePlanId

Я хотел бы объединить эти 2, поэтому я беру все планы ухода и его рефералов, если он существует, если он существует - беру только самых новых рефералов

select * from CarePlan c //query 1
left outer join Referral r on 
r.CarePlanId = c.CarePlanId


select top 1 * from Referral r //query 2
where r.CarePlanId = '1'
order by ReferralDate desc

РЕДАКТИРОВАТЬ:

Первый запрос дает мне что-то вроде этого:

CarePlanID    ReferralId     ReferralDate
----------    ----------     ------------
1             1              05/15/12
2             NULL           NULL
1             2              05/10/12  //Old date, dont want this careplan

Второй запрос даст мне реферала с самой новой датой

ReferralId    ReferralDate
----------    ------------
1             05/15/12

Данные рефералов могут иметь 0или более рефералов, принадлежащих Careplan

ReferralID  CarePlanId    Date
----------  ----------    ----
1           1             05/15/12
2           1             05/10/12

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

вот так:

CarePlanId   ReferralId    ReferralDate
----------   ----------    ------------
1            1             05/15/12
2            NULL          NULL

Спасибо - надеюсь, это имеет смысл

Ответы [ 3 ]

43 голосов
/ 07 марта 2012
select *
from CarePlan c
outer apply (
    select top 1 * --top N rows
    from Referral r
    where r.CarePlanId = c.CarePlanId --join condition
    order by /*fill this in!*/
) x

Имейте в виду, что это вызывает соединение цикла из-за недостатков оптимизатора вплоть до версии 2014 включительно.

8 голосов
/ 13 ноября 2014

Я знаю, что этот вопрос старше, но есть другой подход, который, как мне кажется, недостаточно используется:

Вы можете объединять таблицы обратно в себя и использовать оператор для поиска «самой последней» записи.

Ответ

SELECT CP.CarePlanId, R.ReferralId, R.ReferralDate
FROM CarePlan CP
LEFT OUTER JOIN Referral R ON R.CarePlanId = CP.CarePlanId
LEFT OUTER JOIN Referral R_NEWER ON R.CarePlanId = R_NEWER.CarePlanId AND R.ReferralDate < R_NEWER.ReferralDate
WHERE R_NEWER.ReferralId IS NULL

Результаты:

CP.CarePlanId   R.ReferralId    R.ReferralDate
----------      ----------      ------------
1               1               05/15/12
2               NULL            NULL

Объяснение

Давайте разберемся с этим. По сути, вы говорите, что для каждой записи реферала (слева) присоединяются к каждой другой записи реферала, которая связана с тем же CarePlanId, но ТОЛЬКО там, где имеется более новая ReferralDate.

Вот запрос без предложения where (вместе с дополнительной информацией из таблицы R_NEWER):

SELECT CP.CarePlanId, R.ReferralId, R.ReferralDate, R_NEWER.ReferralId, R.NEWER.ReferralDate
FROM CarePlan CP
LEFT OUTER JOIN Referral R ON R.CarePlanId = CP.CarePlanId
LEFT OUTER JOIN Referral R_NEWER ON R.CarePlanId = R_NEWER.CarePlanId AND R.ReferralDate < R_NEWER.ReferralDate

Вот результат этого запроса:

CP.CarePlanId   R.ReferralId    R.ReferralDate  R_NEWER.ReferralId  R_NEWER.ReferralDate
----------      ----------      ------------    ------------        ------------    
1               1               05/15/12        NULL                NULL
2               NULL            NULL            NULL                NULL
1               2               05/10/12        1                   05/15/12

Как видите, только реферальный Id 2 (3-я запись выше) нашел "более новую" запись для присоединения в реферальной таблице (т.е. реферальный Id 1). Идентификатор реферала 1 (1-я запись выше) НЕ нашел «более нового» реферала (для того же CarePlanId).

Итак, имея это в виду, теперь мы просто добавляем обратно выражение where:

SELECT CP.CarePlanId, R.ReferralId, R.ReferralDate, R_NEWER.ReferralId, R.NEWER.ReferralDate
FROM CarePlan CP
LEFT OUTER JOIN Referral R ON R.CarePlanId = CP.CarePlanId
LEFT OUTER JOIN Referral R_NEWER ON R.CarePlanId = R_NEWER.CarePlanId AND R.ReferralDate < R_NEWER.ReferralDate
WHERE R_NEWER.ReferralId IS NULL

и получите:

CP.CarePlanId   R.ReferralId    R.ReferralDate  R_NEWER.ReferralId  R_NEWER.ReferralDate
----------      ----------      ------------    ------------        ------------    
1               1               05/15/12        NULL                NULL
2               NULL            NULL            NULL                NULL

На этом этапе просто удалите столбцы R_NEWER из вашего SELECT, так как они больше не нужны, и у вас есть ответ.

Важно помнить, что «где» применяется ПОСЛЕ того, как объединения произошли, но оператор ON происходит во время соединения. Чтобы сделать это более понятным для меня, я всегда стараюсь писать SELECTs и JOINs и возвращать столбцы из каждой таблицы, к которой я присоединяюсь, а затем добавлять свои предложения WHERE, когда у меня есть четкое представление о том, что возвращается.

Протест Этот подход прекрасно работает в большинстве случаев, но возможно иметь дублирующиеся строки, если у вас было 2 реферала (для одного и того же CarePlanId) с датой 15.05.12, и эта дата была «самой последней». Чтобы обойти это, вы можете расширить ваши объединения (ы) до ограничения, основанного на «самом высоком» ReferralId, если возникает такой сценарий.

1 голос
/ 07 марта 2012

Просто предположение. Я не уверен, что у EF возникнут проблемы с синтаксисом CTE - можете ли вы заставить EF вызывать хранимую процедуру, чтобы на вас не надевали наручники подмножество функций, поддерживаемых EF?

;WITH r AS 
(
  SELECT CarePlanId, MAX(ReferralDate)
    FROM dbo.Referrals GROUP BY CarePlanId
)
SELECT * FROM dbo.CarePlan AS c
LEFT OUTER JOIN r 
  ON r.CarePlanId = c.CarePlanId;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...