1 teratas dengan gabungan kiri

95

Diberikan kueri di bawah ini mungkin ada beberapa baris di dps_markers dengan kunci penanda yang sama tetapi kami hanya ingin menggabungkan yang pertama. Jika saya mengambil kueri ini dan menghapus 1 teratas dan ORDER BY saya mendapatkan nilai untuk mbg.marker_value tetapi jalankan seperti itu selalu mengembalikan null

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'
dstarh
sumber

Jawaban:

204

Gunakan OUTER APPLY sebagai ganti LEFT JOIN:

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';

Tidak seperti JOIN, APPLY memungkinkan Anda untuk mereferensikan u.id di dalam query dalam.

Remus Rusanu
sumber
Terima kasih @Remus, Ini membantu saya.
Sarthak Shah
3

Kunci untuk men-debug situasi seperti ini adalah dengan menjalankan subquery / inline view sendiri untuk melihat keluarannya:

  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

Dengan menjalankan itu, Anda akan melihat bahwa profile_idnilainya tidak cocok dengan u.idnilai dari u162231993, yang akan menjelaskan mengapa mbgreferensi akan dikembalikannull (berkat left join; Anda tidak akan mendapatkan apa pun jika itu adalah inner join).

Anda telah mengkodekan diri Anda sendiri ke sudut menggunakan TOP, karena sekarang Anda harus mengubah kueri jika Anda ingin menjalankannya untuk pengguna lain. Pendekatan yang lebih baik adalah:

   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'

Dengan itu, Anda dapat mengubah idnilai dalam whereklausa untuk memeriksa catatan untuk setiap pengguna di sistem.

OMG Ponies
sumber
2

Karena TOP 1dari sub-kueri yang diurutkan tidak memiliki profile_id = 'u162231993' Hapuswhere u.id = 'u162231993' dan lihat hasilnya.

Jalankan sub-kueri secara terpisah untuk memahami apa yang terjadi.

Damir Sudarevic
sumber
ok saya pikir saya mengerti apa yang Anda maksud sekarang. masih harus bisa membuat ini bekerja. Pada dasarnya saya tabel dps_markers tabel mungkin memiliki lebih dari satu baris yang menyebabkan dupes di query luar yang perlu kita cegah.
dstarh
1

Damir benar,

Subkueri Anda perlu memastikan bahwa dps_user.id sama dengan um.profile_id, jika tidak maka akan mengambil baris teratas yang mungkin, tapi mungkin tidak sama dengan id Anda dari 'u162231993'

Kueri Anda akan terlihat seperti ini:

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'
Nathan Koop
sumber
1
ya saya baru saja mencobanya tetapi u.id tidak terlihat di sub-pilih Pengidentifikasi multi-bagian "u.id" tidak dapat terikat.
dstarh
1
Anda bisa memasukkan WHERE um.profile_id = 'u162231993'sub-query dan WHERE mbg.marker_value IS NOT NULLdi luar.
Damir Sudarevic
1
saya tidak akan tahu profile_id, itu akan dari join lain. Ini ditarik dari pertanyaan yang jauh lebih besar
dstarh
1
baik, gunakan variabel @SearchFor = 'u162231993'dan kemudian gunakan di WHERE, atau posting beberapa data dan struktur tabel sehingga orang lain dapat membantu dan mencobanya.
Damir Sudarevic