Топ 1 з лівим з'єднанням


94

Враховуючи запит нижче, у dps_markers може бути кілька рядків з однаковим ключем маркера, але ми хочемо приєднатись лише до першого. Якщо я беру цей запит і видаляю перші 1 і ЗАМОВЛЯЮ, я отримую значення mbg.marker_value, але запускаю як є, завжди повертає

SELECT u.id, mbg.marker_value 
FROM dps_user u
LEFT JOIN 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     ORDER BY m.creation_date
    ) MBG ON MBG.profile_id=u.id 
WHERE u.id = 'u162231993'

Відповіді:


202

Використовуйте ВНЕШНЄ ЗАСТОСУВАННЯ замість ЛІВОГО ПРИЄДНАННЯ:

SELECT u.id, mbg.marker_value 
FROM dps_user u
OUTER APPLY 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     WHERE um.profile_id=u.id 
     ORDER BY m.creation_date
    ) AS MBG
WHERE u.id = 'u162231993';

На відміну від JOIN, APPLY дозволяє посилатися на u.id всередині внутрішнього запиту.


Дякую @Remus, це мені допомогло.
Сартак Шах

3

Ключем до налагодження таких ситуацій є запуск підзапиту / вбудованого подання самостійно, щоб побачити, який результат:

  SELECT TOP 1 
         dm.marker_value, 
         dum.profile_id
    FROM DPS_USR_MARKERS dum (NOLOCK)
    JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                AND dm.marker_key = 'moneyBackGuaranteeLength'
ORDER BY dm.creation_date

Запустивши це, ви побачите, що profile_idзначення не відповідає u.idзначенню u162231993, що пояснює, чому mbgповертаються будь-які посилання null(завдяки лівому об’єднанню; ви не отримаєте нічого, якби це було внутрішнє об’єднання).

Ви закодували себе в кут, використовуючи TOP, тому що тепер вам потрібно налаштувати запит, якщо ви хочете запустити його для інших користувачів. Кращим підходом було б:

   SELECT u.id, 
          x.marker_value 
     FROM DPS_USER u
LEFT JOIN (SELECT dum.profile_id,
                  dm.marker_value,
                  dm.creation_date
             FROM DPS_USR_MARKERS dum (NOLOCK)
             JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                         AND dm.marker_key = 'moneyBackGuaranteeLength'
           ) x ON x.profile_id = u.id
     JOIN (SELECT dum.profile_id,
                  MAX(dm.creation_date) 'max_create_date'
             FROM DPS_USR_MARKERS dum (NOLOCK)
             JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                         AND dm.marker_key = 'moneyBackGuaranteeLength'
         GROUP BY dum.profile_id) y ON y.profile_id = x.profile_id
                                   AND y.max_create_date = x.creation_date
    WHERE u.id = 'u162231993'

Завдяки цьому ви можете змінити idзначення в whereпункті, щоб перевірити записи для будь-якого користувача в системі.


2

Оскільки у TOP 1впорядкованому підзапиті немає функції profile_id = 'u162231993' Видалити where u.id = 'u162231993'та переглянути результати тоді.

Запустіть підзапит окремо, щоб зрозуміти, що відбувається.


добре, я думаю, я бачу, що ви маєте на увазі зараз. все ще потрібно мати можливість змусити це працювати. В основному я в таблиці dps_markers таблиця може мати більше одного рядка, що викликає дублювання у зовнішньому запиті, який нам потрібно запобігти.
dstarh

1

Дамір прав,

Ваш підзапит повинен переконатися, що dps_user.id дорівнює um.profile_id, інакше він захопить верхній рядок, який може, але, можливо, не дорівнює вашому ідентифікатору 'u162231993'

Ваш запит повинен виглядати так:

SELECT u.id, mbg.marker_value 
FROM dps_user u
LEFT JOIN 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     WHERE u.id = um.profile_id
     ORDER BY m.creation_date
    ) MBG ON MBG.profile_id=u.id 
WHERE u.id = 'u162231993'

1
так, я просто спробував це, але u.id не видно в підвибірці. Ідентифікатор декількох частин "u.id" неможливо прив'язати.
dstarh

1
Ви можете розмістити WHERE um.profile_id = 'u162231993'підзапит та WHERE mbg.marker_value IS NOT NULLзовні.
Дамір Сударевич

1
я не буду знати profile_id, це буде з іншого приєднання. Це було вилучено з набагато більшого запиту
dstarh

1
ну, використовуйте змінну, @SearchFor = 'u162231993'а потім використовуйте її в WHEREабо розмістіть деякі дані та структури таблиць, щоб інші люди могли допомогти та спробувати.
Дамір Сударевич
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.