อันดับ 1 ด้วยการเข้าร่วมด้านซ้าย


96

จากคำค้นหาด้านล่างอาจมีหลายแถวใน dps_markers ที่มีคีย์เครื่องหมายเดียวกัน แต่เราต้องการเข้าร่วมกับแถวแรกเท่านั้น ถ้าฉันใช้แบบสอบถามนี้และลบ 1 อันดับแรกและ ORDER BY ฉันจะได้รับค่าสำหรับ mbg.marker_value แต่รันตามที่มันส่งกลับค่า 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'

คำตอบ:


204

ใช้ OUTER APPLY แทน 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';

ไม่เหมือนกับการเข้าร่วม APPLY ช่วยให้คุณอ้างอิง u.id ภายในแบบสอบถามภายใน


ขอบคุณ @Remus มันช่วยฉัน
Sarthak Shah

3

กุญแจสำคัญในการดีบักสถานการณ์เช่นนี้คือการรัน subquery / inline view ด้วยตัวของมันเองเพื่อดูว่าผลลัพธ์คืออะไร:

  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' Remove where u.id = 'u162231993'และดูผลลัพธ์แล้ว

เรียกใช้แบบสอบถามย่อยแยกกันเพื่อทำความเข้าใจว่าเกิดอะไรขึ้น


โอเคฉันคิดว่าตอนนี้ฉันเข้าใจว่าคุณหมายถึงอะไร ยังต้องสามารถใช้งานได้ โดยทั่วไปฉันตาราง dps_markers ตารางอาจมีมากกว่าหนึ่งแถวซึ่งทำให้เกิด dupes ในแบบสอบถามด้านนอกซึ่งเราจำเป็นต้องป้องกัน
dstarh

1

Damir ถูกต้อง

แบบสอบถามย่อยของคุณต้องแน่ใจว่า dps_user.id เท่ากับ um.profile_id มิฉะนั้นจะจับแถวบนสุดซึ่งอาจ แต่อาจไม่เท่ากับ 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ด้านนอกได้
Damir Sudarevic

1
ฉันจะไม่เอา profile_id มันมาจากการเข้าร่วมอื่น สิ่งนี้ถูกดึงออกมาจากแบบสอบถามที่ใหญ่กว่ามาก
dstarh

1
ใช้ตัวแปร@SearchFor = 'u162231993'แล้วใช้สิ่งนั้นในWHEREหรือโพสต์ข้อมูลและโครงสร้างตารางเพื่อให้คนอื่นช่วยและทดลองใช้
ดาเมียร์ซูดาเรวิช
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.