LEFT JOIN เฉพาะแถวแรก


145

ฉันอ่านกระทู้มากมายเกี่ยวกับการรับเฉพาะแถวแรกของการเข้าร่วมด้านซ้าย แต่ด้วยเหตุผลบางประการสิ่งนี้ไม่ได้ผลสำหรับฉัน

นี่คือโครงสร้างของฉัน (ทำให้ง่ายขึ้นแน่นอน)

ฟีด

id |  title | content
----------------------
1  | Feed 1 | ...

ศิลปิน

artist_id | artist_name
-----------------------
1         | Artist 1
2         | Artist 2

feeds_artists

rel_id | artist_id | feed_id
----------------------------
1      |     1     |    1 
2      |     2     |    1 
...

ตอนนี้ฉันต้องการรับบทความและเข้าร่วมเฉพาะศิลปินคนแรกเท่านั้นและฉันก็คิดถึงสิ่งนี้:

SELECT *
    FROM feeds 
    LEFT JOIN feeds_artists ON wp_feeds.id = (
        SELECT feeds_artists.feed_id FROM feeds_artists
        WHERE feeds_artists.feed_id = feeds.id 
    LIMIT 1
    )
WHERE feeds.id = '13815'

เพียงเพื่อรับเฉพาะแถวแรกของ feeds_artists แต่ยังไม่ได้ผล

ฉันไม่สามารถใช้TOPเนื่องจากฐานข้อมูลของฉันและฉันไม่สามารถจัดกลุ่มผลลัพธ์ตามfeeds_artists.artist_idที่ฉันต้องการจัดเรียงตามวันที่ (ฉันได้ผลลัพธ์จากการจัดกลุ่มด้วยวิธีนี้ แต่ผลลัพธ์ที่ไม่ใหม่ที่สุด)

พยายามบางอย่างกับ OUTER APPLY เช่นกัน - ไม่ประสบความสำเร็จเช่นกัน พูดตามตรงฉันนึกไม่ออกจริงๆว่าเกิดอะไรขึ้นในแถวเหล่านั้น - อาจเป็นเหตุผลใหญ่ที่สุดว่าทำไมฉันถึงไม่สามารถใช้งานได้

วิธีการแก้:

SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
    SELECT artist_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.id
    LIMIT 1
)
WHERE f.id = '13815'


นี่คือโซลูชันstackoverflow.com/a/7588442/612987
Wasim A.

คุณควรใส่วิธีแก้ปัญหาเป็นคำตอบเพื่อให้เราสามารถโหวตได้และสามารถมองเห็นได้มากขึ้นสำหรับผู้เยี่ยมชมในอนาคต
fguillen

ฉันเดาว่าคุณพูดถูก - ฉันยังเพิ่มเป็นคำตอบ
KddC

คำตอบ:


99

หากคุณสามารถสันนิษฐานได้ว่ารหัสศิลปินเพิ่มขึ้นเมื่อเวลาผ่านไปรหัสนั้นMIN(artist_id)จะเร็วที่สุด

ลองอะไรแบบนี้ (ยังไม่ทดลอง ... )

SELECT *
  FROM feeds f
  LEFT JOIN artists a ON a.artist_id = (
    SELECT
      MIN(fa.artist_id) a_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.feed_id
  ) a

ขอบคุณสำหรับการตอบกลับที่รวดเร็ว นี่ไม่ใช่คำตอบที่แน่นอน แต่ทำให้ฉันมาถูกทาง ฉันพยายามที่จะเข้าร่วมทั้งสองในระดับเดียวกันเสมอแทนที่จะทำให้ระดับหนึ่งต้องพึ่งพาอีกฝ่าย ขอบคุณมากที่ทำให้ฉันไปถูกทาง แก้ไขโพสต์แรก
KddC

5
ณ จุดนี้แบบสอบถามย่อยธรรมดาจะไม่ดีกว่าหรือ? ทำให้ตอนนี้คุณมีการเข้าร่วมและแบบสอบถามย่อย แค่ถามสาเหตุฉันกำลังมองหาวิธีแก้ปัญหาเดียวกัน :)
galdikas

2
แบบสอบถามย่อยช้าเกินไป
Sinux

1
@Sinux กำหนด "ช้าเกินไป" ขึ้นอยู่กับจำนวนบันทึกและข้อกำหนดที่กำหนด
ผู้สังเกตการณ์

1
ไม่ได้ผล! แบบสอบถามย่อยไม่อนุญาตให้ส่งผ่านฟิลด์จากแบบสอบถามหลัก !!!
Thư Sinh

52

เวอร์ชันที่ไม่มีการเลือกย่อย:

   SELECT f.title,
          f.content,
          MIN(a.artist_name) artist_name
     FROM feeds f
LEFT JOIN feeds_artists fa ON fa.feed_id = f.id
LEFT JOIN artists a ON fa.artist_id = a.artist_id
 GROUP BY f.id

2
ฉันไม่คิดว่านี่จะเป็นแถวแรก (id แรกหรือก่อนอื่น) มันเป็นเพียงแค่สุ่มเลือกหนึ่งในแถวการรวมด้านซ้าย
Sinux

28
คำค้นหานี้ไม่ถูกต้อง จะเลือกชื่อศิลปิน "ต่ำสุด" ไม่ใช่ชื่อศิลปินแรกในชุด
Glapa

5
ผิดสำหรับคำถาม… แต่สิ่งที่ฉันต้องการ ในกรณีของฉันฉันแค่ต้องการ ID แรกจากตารางที่ฉันเข้าร่วม
Drew Stephens

2
ปัญหาคือถ้าคุณตัดสินใจที่จะทำ COUNT หรือ SUM คุณจะทำให้ข้อมูลเสียหาย ปัญหาเกี่ยวกับ Subselect คือการรับข้อมูลจะหนักกว่าเนื่องจากสร้างตารางชั่วคราว ... ฉันหวังว่า MySql จะมี LIMIT ในระดับ LEFT JOIN
Shadoweb

โซลูชันนี้มีปัญหาด้านประสิทธิภาพ ใช้วิธีแก้ปัญหา Min / Max แทน
Sadegh PM

30

คำตอบ @ Matt Dodges ทำให้ฉันมาถูกทาง ขอขอบคุณอีกครั้งสำหรับคำตอบทั้งหมดซึ่งช่วยได้มากในช่วงเวลานั้น ทำให้มันทำงานได้ดังนี้:

SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
    SELECT artist_id
    FROM feeds_artists fa 
    WHERE fa.feed_id = f.id
    LIMIT 1
)
WHERE f.id = '13815'

1
นี่คือคำตอบที่ใช้ได้กับแถวแรกเท่านั้น แตกเมื่อไม่มีf.idเงื่อนไข.
Tajni

3

จากคำตอบหลายข้อที่นี่ฉันพบบางสิ่งที่ใช้ได้ผลสำหรับฉันและฉันต้องการสรุปและอธิบายสิ่งที่เกิดขึ้น

แปลง:

LEFT JOIN table2 t2 ON (t2.thing = t1.thing)

ถึง:

LEFT JOIN table2 t2 ON (t2.p_key = (SELECT MIN(t2_.p_key) 
    FROM table2 t2_ WHERE (t2_.thing = t1.thing) LIMIT 1))

เงื่อนไขที่ว่าเชื่อมต่อ T1 และ T2 จะถูกย้ายจากและเข้าไปในแบบสอบถามภายในON หรือทำให้แน่ใจว่ามีเพียง 1 แถวถูกส่งกลับโดยแบบสอบถามภายในWHEREMIN(primary key)LIMIT 1

หลังจากเลือกแถวที่ต้องการแล้วเราต้องบอกONว่าแถวนั้นคือแถวใด นั่นคือสาเหตุที่ONเปรียบเทียบคีย์หลักของ tabled ที่เข้าร่วม

คุณสามารถเล่นกับข้อความค้นหาภายใน (เช่น order + limit) แต่ต้องส่งคืนคีย์หลักหนึ่งคีย์ของแถวที่ต้องการซึ่งจะบอกONแถวที่ต้องการเข้าร่วม


บันทึก: ก็ยังจะทำงานร่วมกับเท่านั้นหรือเฉพาะLIMIT เงื่อนไขจะต้องอยู่ในตารางคีย์หลักเข้าร่วมเพื่อหลีกเลี่ยงผลกระทบต่อประสิทธิภาพ MINON
oriadam

2

ฉันเคยใช้อย่างอื่น (ฉันคิดว่าดีกว่า ... ) และต้องการแบ่งปัน:

ฉันสร้าง VIEW ที่มีอนุประโยค "กลุ่ม"

CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code

SELECT * FROM client INNER JOIN vCountries on client_province = province_id

ฉันอยากจะบอกว่าฉันคิดว่าเราต้องทำวิธีนี้เพราะเราคิดผิดในการวิเคราะห์ ... อย่างน้อยก็ในกรณีของฉัน ... แต่บางครั้งก็ถูกกว่าที่จะทำเช่นนี้เพื่อออกแบบทุกอย่างใหม่ ...

ฉันหวังว่ามันจะช่วยได้!


สมมติว่ามีหลายแถว (จังหวัด?) สำหรับแต่ละcountry_codeแถวนั่นGROUP BYไม่เหมาะสม ดูONLY_FULL_GROUP_BY.
Rick James

คุณไม่จำเป็นต้องสร้างมุมมองเพื่อใช้สำหรับการเข้าร่วม LEFT / INNER เท่านั้น! สามารถใช้แบบสอบถามย่อยหรือWITH x AS (อนุประโยคได้หรือไม่?
kiradotee

1

ฉันต้องการให้คำตอบที่ครอบคลุมมากขึ้น หนึ่งที่จะจัดการกับกรณีใด ๆ เมื่อคุณต้องการที่จะเลือกเฉพาะรายการแรกใน LEFT JOIN

คุณสามารถใช้แบบสอบถามย่อยที่ GROUP_CONCATS สิ่งที่คุณต้องการ (เรียงลำดับด้วย!) จากนั้นแยกผลลัพธ์ GROUP_CONCAT และรับเฉพาะรายการแรกเช่นนั้น ...

LEFT JOIN Person ON Person.id = (
    SELECT SUBSTRING_INDEX(
        GROUP_CONCAT(FirstName ORDER BY FirstName DESC SEPARATOR "_" ), '_', 1)
    ) FROM Person
);

เนื่องจากเรามีDESCเป็นตัวเลือกORDER BYสิ่งนี้จะส่งคืน Person id สำหรับคนอย่าง "Zack" ถ้าเราต้องการใครสักคนที่มีชื่อเช่น "แอนดี้" ที่เราจะเปลี่ยนORDER BY FirstName DESCเพื่อORDER BY FirstName ASC

นี่เป็นเรื่องที่ว่องไวเนื่องจากทำให้อำนาจในการสั่งซื้อทั้งหมดอยู่ในมือของคุณ แต่หลังจากการทดสอบหลายครั้งจะไม่สามารถปรับขนาดได้ดีในสถานการณ์ที่มีผู้ใช้จำนวนมากและมีข้อมูลจำนวนมาก

อย่างไรก็ตามมีประโยชน์ในการเรียกใช้รายงานที่เน้นข้อมูลสำหรับผู้ดูแลระบบ


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