MySQL: เพิ่มประสิทธิภาพยูเนี่ยนด้วย“ ORDER BY” ในการค้นหาภายใน


9

ฉันเพิ่งตั้งค่าระบบบันทึกซึ่งประกอบด้วยหลายตารางที่มีเค้าโครงเดียวกัน

มีตารางหนึ่งตารางสำหรับแหล่งข้อมูลแต่ละแหล่ง

สำหรับโปรแกรมดูบันทึกฉันต้องการ

  • ยูเนี่ยนทุกตารางบันทึก ,
  • กรองพวกเขาโดยการบัญชี ,
  • เพิ่มคอลัมน์หลอกสำหรับการระบุแหล่งที่มา
  • เรียงพวกเขาโดยเวลา ,
  • และจำกัด พวกเขาสำหรับการแบ่งหน้า

ตารางทั้งหมดมีเขตข้อมูลที่เรียกzeitpunktว่าเป็นคอลัมน์วันที่ / เวลาที่จัดทำดัชนี

ความพยายามครั้งแรกของฉันคือ:

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730)

UNION

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730)

ORDER BY zeit DESC LIMIT 10;

เพิ่มประสิทธิภาพไม่สามารถใช้ดัชนีที่นี่เพราะแถวทั้งหมดจากตารางทั้งสองจะถูกส่งกลับโดย subqueries UNIONและเรียงหลังจากที่

วิธีแก้ปัญหาของฉันคือต่อไปนี้:

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)

UNION

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)

ORDER BY zeit DESC LIMIT 10;

ฉันคาดหวังว่าเคียวรีเอ็นจินจะใช้ดัชนีที่นี่เนื่องจากเคียวรีย่อยทั้งคู่ควรเรียงลำดับและ จำกัด ไว้ก่อนหน้าUNIONจากนั้นจึงผสานและเรียงลำดับแถว

ฉันคิดว่ามันจะเป็นอย่างนั้น แต่การเรียกใช้EXPLAINแบบสอบถามจะบอกฉันว่าแบบสอบถามย่อยยังคงค้นหาทั้งสองตาราง

EXPLAINingแบบสอบถามย่อยเองแสดงให้ฉันเห็นการเพิ่มประสิทธิภาพที่ต้องการ แต่UNIONingพวกเขาร่วมกันมันไม่ได้

ฉันพลาดอะไรไปหรือเปล่า?

ฉันรู้ว่าORDER BYคำสั่งย่อยภายในUNIONเคียวรีย่อยถูกละเว้นโดยไม่มีLIMITแต่มีข้อ จำกัด

แก้ไข: ที่
จริงแล้วอาจมีข้อความค้นหาโดยไม่มีaccount_idเงื่อนไข

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

ฉันต้องเก็บเลเยอร์ไว้ระหว่างตัวอ่านบันทึกและตารางจริง

ต่อไปนี้เป็นแผนการดำเนินการสำหรับเคียวรีทั้งหมดและเคียวรีย่อยแรกรวมถึงโครงร่างตารางโดยละเอียด:

https://gist.github.com/ca8fc1093cd95b1c6fc0


1
(account_id, zeitpunkt)ดัชนีดีที่สุดสำหรับการนี้จะเป็นสารประกอบ คุณมีดัชนีเช่นนี้หรือไม่? ที่ดีที่สุดที่สองจะเป็น (ฉันคิดว่า) เดียว(zeitpunkt)- แต่ประสิทธิภาพหากมีการใช้ขึ้นอยู่กับความถี่ที่account_id=730ปรากฏขึ้น
ypercubeᵀᴹ

2
แล้วทำไมUNION DISTINCTล่ะ? ไม่จำเป็นต้องบังคับให้มีการเรียงลำดับและแตกต่างกันเนื่องจากผลลัพธ์จะแตกต่างกันไปตามเคียวรีย่อยเนื่องจากคอลัมน์การระบุพิเศษ UNION ALLใช้
ypercubeᵀᴹ

1
นอกเหนือจากข้อเสนอแนะของ @ ypercube ฉันมีคำถาม: จะดีกว่าไหมถ้ามีบันทึกเหล่านั้นในตารางเดียวกันพร้อมกับเพิ่มsourceคอลัมน์? วิธีนี้คุณสามารถหลีกเลี่ยงUNIONและใช้ดัชนี (s) ในข้อมูลทั้งหมดของคุณ
dezso

1
@ypercube ที่จริงแล้วอาจมีข้อความค้นหาที่ไม่มีเงื่อนไขaccount_id การตั้งค่าสถานะDISTINCTเป็นการบอกเล่าของการลองก่อนหน้านี้และไร้ประโยชน์จริง ๆ เนื่องจากผลลัพธ์จะแตกต่างกันเสมอและเนื่องจากDISTINCTเป็นพฤติกรรมของ dafualt ตารางมีอยู่แล้วและเต็มไปด้วยข้อมูล อย่างไรก็ตามอาจมีการเปลี่ยนแปลงในเค้าโครงขึ้นอยู่กับแหล่งที่มาดังนั้นฉันต้องการให้พวกเขาแบ่งออก นอกจากนี้ไคลเอ็นต์การบันทึกใช้ข้อมูลรับรองที่แตกต่างกันด้วยเหตุผล ฉันต้องเก็บเลเยอร์ไว้ระหว่างตัวอ่านบันทึกและตารางจริง
ลูคัส

ตกลง แต่ตรวจสอบว่าการเปลี่ยนแปลงเพื่อUNION ALLให้ได้แผนการปฏิบัติที่แตกต่างกันหรือไม่
ypercubeᵀᴹ

คำตอบ:


8

เพิ่งอยากรู้อยากเห็นคุณลองรุ่นนี้ได้ไหม มันอาจหลอกให้เครื่องมือเพิ่มประสิทธิภาพใช้ดัชนีเดียวกันกับที่เคียวรีย่อยใช้:

SELECT *
FROM
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10) 
    AS a

UNION ALL

SELECT *
FROM
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)
    AS b

ORDER BY zeit DESC LIMIT 10;

(account_id, zeitpunkt)ผมยังคิดว่าดัชนีดีที่สุดที่คุณจะได้เป็นสารประกอบ มันให้ผลเร็ว 10 แถวและไม่จำเป็นต้องใช้ลูกเล่น


การดัดแปลงของคุณกลายเป็นผลลัพธ์ที่ต้องการ ขอบคุณ! เช่นเดียวกับหมายเหตุด้านล่าง: ตอนนี้ฉันไม่แน่ใจว่าดัชนีใดจะดีกว่า ฉันสามารถใช้ทั้งคู่ได้ ฉันจะต้องตรวจสอบว่าจำนวนผู้ใช้และlog entries / userขนาดจะ
Lukas

หากคุณต้องการแบบสอบถามด้วยและไม่มีแบบสอบถามaccount_id=?ให้เก็บทั้งสองไว้
ypercubeᵀᴹ

@ypercube +1 นี้ฉลาดมากและทำงานในสถานการณ์ของฉัน (คล้ายกัน) ด้วย! คุณช่วยอธิบายได้ไหมว่าทำไมการห่อแบบสอบถามที่รวมเข้าด้วยกันเป็นกลSELECT * FROMอุบาย MySQL เพื่อใช้ดัชนี?
dkamins

@dkamins: เครื่องมือเพิ่มประสิทธิภาพ MySQL ไม่ฉลาดมากโดยปกติเมื่อมีตารางที่ได้มาเช่น(SELECT ...) AS aนี้มันพยายามประเมินและปรับตารางที่ได้รับแยกต่างหากจากตารางอื่น ๆ ที่ได้รับจากนั้นแบบสอบถามทั้งหมด
ypercubeᵀᴹ

@Lukas จริง ๆ แล้วเนื่องจากคุณต้องการให้แน่ใจว่ามีการใช้ดัชนีการใช้ / การเพิ่มforce indexจะช่วยให้คุณมีทางออกที่ดีขึ้น
Pacerier
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.