สั่ง MySQL โดยก่อนจัดกลุ่มตาม


243

มีคำถามที่คล้ายกันมากมายที่จะพบได้ที่นี่ แต่ฉันไม่คิดว่าจะตอบคำถามอย่างเพียงพอ

ฉันจะดำเนินการต่อจากคำถามยอดนิยมในปัจจุบันและใช้ตัวอย่างของพวกเขาหากไม่เป็นไร

ภารกิจในอินสแตนซ์นี้คือรับโพสต์ล่าสุดสำหรับผู้แต่งแต่ละคนในฐานข้อมูล

แบบสอบถามตัวอย่างสร้างผลลัพธ์ที่ใช้ไม่ได้เนื่องจากไม่ใช่โพสต์ล่าสุดที่ส่งคืนเสมอ

SELECT wp_posts.* FROM wp_posts
    WHERE wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
    GROUP BY wp_posts.post_author           
    ORDER BY wp_posts.post_date DESC

คำตอบที่ยอมรับในปัจจุบันคือ

SELECT
    wp_posts.*
FROM wp_posts
WHERE
    wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
HAVING wp_posts.post_date = MAX(wp_posts.post_date) <- ONLY THE LAST POST FOR EACH AUTHOR
ORDER BY wp_posts.post_date DESC

น่าเสียดายที่คำตอบนี้ธรรมดาและเรียบง่ายผิดและในหลายกรณีให้ผลลัพธ์ที่มีเสถียรภาพน้อยกว่าแบบสอบถามแบบเดิม

ทางออกที่ดีที่สุดของฉันคือการใช้แบบสอบถามย่อยของแบบฟอร์ม

SELECT wp_posts.* FROM 
(
    SELECT * 
    FROM wp_posts
    ORDER BY wp_posts.post_date DESC
) AS wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author 

คำถามของฉันเป็นคำถามธรรมดาแล้ว: มีการสั่งซื้อแถวก่อนจัดกลุ่มโดยไม่หันไปใช้แบบสอบถามย่อยหรือไม่?

แก้ไข : คำถามนี้เป็นคำถามต่อเนื่องจากคำถามอื่นและสถานการณ์เฉพาะของฉันแตกต่างกันเล็กน้อย คุณสามารถ (และควร) สมมติว่ามี wp_posts.id ซึ่งเป็นตัวระบุเฉพาะสำหรับโพสต์นั้น


2
ดังที่คุณพูดถึงในความคิดเห็นต่อคำตอบที่ให้มาอาจเป็นไปได้ที่จะมีบางโพสต์ที่มีการประทับเวลาเดียวกัน ถ้าเป็นเช่นนั้นโปรดยกตัวอย่างกับข้อมูลและผลลัพธ์ที่คาดหวัง และโปรดอธิบายว่าทำไมคุณถึงคาดหวังผลลัพธ์นี้ post_authorและpost_dateไม่เพียงพอที่จะได้รับแถวที่ไม่ซ้ำกันดังนั้นจะต้องมีแถวที่ไม่ซ้ำกันต่อไปpost_author
Sir Rufo

@SirRufo คุณพูดถูกฉันได้เพิ่มการแก้ไขสำหรับคุณแล้ว
Rob Forrest

There are plenty of similar questions to be found on here but I don't think that any answer the question adequately.นั่นคือสิ่งที่เป็นรางวัลสำหรับ
การแข่งขัน Lightness ใน Orbit

@LightnessRacesinOrbit หากคำถามปัจจุบันมีคำตอบที่ยอมรับแล้วว่าในความคิดของฉันผิดคุณแนะนำให้ทำอะไร
Rob Forrest

1
สงสัยว่าทำไมคุณยอมรับคำตอบที่ใช้แบบสอบถามย่อย - เมื่อคำถามของคุณถามชัดเจน ... "" มีการสั่งซื้อแถวก่อนที่จะจัดกลุ่มโดยไม่หันไปใช้แบบสอบถามย่อยหรือไม่? "
TV-C-15

คำตอบ:


373

การใช้งานORDER BYในแบบสอบถามย่อยไม่ใช่ทางออกที่ดีที่สุดสำหรับปัญหานี้

ทางออกที่ดีที่สุดที่จะได้รับmax(post_date)โดยผู้เขียนคือการใช้แบบสอบถามย่อยเพื่อส่งกลับวันที่สูงสุดแล้วเข้าร่วมที่ตารางของคุณทั้งในpost_authorและวันที่สูงสุด

ทางออกควรเป็น:

SELECT p1.* 
FROM wp_posts p1
INNER JOIN
(
    SELECT max(post_date) MaxPostDate, post_author
    FROM wp_posts
    WHERE post_status='publish'
       AND post_type='post'
    GROUP BY post_author
) p2
  ON p1.post_author = p2.post_author
  AND p1.post_date = p2.MaxPostDate
WHERE p1.post_status='publish'
  AND p1.post_type='post'
order by p1.post_date desc

หากคุณมีข้อมูลตัวอย่างต่อไปนี้:

CREATE TABLE wp_posts
    (`id` int, `title` varchar(6), `post_date` datetime, `post_author` varchar(3))
;

INSERT INTO wp_posts
    (`id`, `title`, `post_date`, `post_author`)
VALUES
    (1, 'Title1', '2013-01-01 00:00:00', 'Jim'),
    (2, 'Title2', '2013-02-01 00:00:00', 'Jim')
;

แบบสอบถามย่อยจะส่งคืนวันที่สูงสุดและผู้แต่ง:

MaxPostDate | Author
2/1/2013    | Jim

จากนั้นเมื่อคุณเข้าร่วมที่กลับไปที่ตารางทั้งสองค่าคุณจะส่งกลับรายละเอียดทั้งหมดของโพสต์นั้น

ดูซอ SQL กับการสาธิต

หากต้องการขยายความคิดเห็นของฉันเกี่ยวกับการใช้แบบสอบถามย่อยเพื่อส่งคืนข้อมูลนี้อย่างแม่นยำ

MySQL ไม่ได้บังคับให้คุณGROUP BYทุกคอลัมน์ที่คุณรวมไว้ในSELECTรายการ ด้วยเหตุนี้หากคุณมีเพียงGROUP BYหนึ่งคอลัมน์ แต่ให้ผลตอบแทนรวม 10 คอลัมน์จะไม่รับประกันว่าค่าคอลัมน์อื่น ๆ ที่เป็นของจะpost_authorถูกส่งคืน หากคอลัมน์ไม่ได้อยู่ในGROUP BYMySQL จะเลือกว่าควรส่งคืนค่าใด

การใช้เคียวรีย่อยด้วยฟังก์ชันการรวมจะรับประกันว่าผู้เขียนและโพสต์ที่ถูกต้องจะถูกส่งกลับทุกครั้ง

เป็นหมายเหตุด้านในขณะที่ MySQL ช่วยให้คุณสามารถใช้ORDER BYในแบบสอบถามย่อยและช่วยให้คุณสามารถใช้GROUP BYกับทุกคอลัมน์ในSELECTรายการพฤติกรรมนี้ไม่ได้รับอนุญาตในฐานข้อมูลอื่น ๆ รวมถึง SQL Server


4
ฉันเห็นสิ่งที่คุณทำไปแล้ว แต่เพียงแค่ส่งคืนวันที่ที่โพสต์ล่าสุดไม่ใช่โพสต์ล่าสุดทั้งแถว
Rob Forrest

1
@ RobForrest นั่นคือสิ่งที่เข้าร่วมทำ คุณส่งคืนวันที่โพสต์ล่าสุดในแบบสอบถามย่อยตามผู้แต่งแล้วกลับไปwp_postsที่ทั้งสองคอลัมน์เพื่อรับแถวเต็ม
Taryn

7
@RobForrest สำหรับหนึ่งเมื่อคุณใช้GROUP BYกับคอลัมน์เดียวเท่านั้นไม่มีการรับประกันว่าค่าในคอลัมน์อื่น ๆ จะถูกต้องอย่างต่อเนื่อง น่าเสียดายที่ MySQL อนุญาตให้ SELECT / GROUPing ประเภทนี้เกิดขึ้นกับผลิตภัณฑ์อื่นไม่ได้ ประการที่สองไวยากรณ์ของการใช้ORDER BYในแบบสอบถามย่อยในขณะที่ได้รับอนุญาตใน MySQL ไม่ได้รับอนุญาตในผลิตภัณฑ์ฐานข้อมูลอื่น ๆ รวมถึง SQL Server คุณควรใช้โซลูชันที่จะให้ผลลัพธ์ที่เหมาะสมในแต่ละครั้งที่มีการดำเนินการ
Taryn

2
สำหรับการปรับสเกลสารประกอบINDEX(post_author, post_date)มีความสำคัญ
Rick James

1
@ jtcotton63 จริง แต่ถ้าคุณใส่post_idในการสืบค้นภายในแล้วในทางเทคนิคคุณควรจัดกลุ่มด้วยเช่นกันซึ่งน่าจะทำให้ผลลัพธ์ของคุณเอียงไป
Taryn

20

โซลูชันของคุณใช้ประโยชน์จากส่วนขยายของ GROUP BY clause ที่อนุญาตให้จัดกลุ่มตามฟิลด์บางฟิลด์ (ในกรณีนี้เพียงpost_author):

GROUP BY wp_posts.post_author

และเลือกคอลัมน์ที่ไม่รวม:

SELECT wp_posts.*

ที่ไม่ได้ระบุไว้ในกลุ่มตามข้อหรือที่ไม่ได้ใช้ในฟังก์ชั่นรวม (MIN, MAX, COUNT, ฯลฯ )

ใช้ส่วนขยายที่ถูกต้องไปยัง GROUP BY clause

สิ่งนี้มีประโยชน์เมื่อทุกค่าของคอลัมน์ที่ไม่รวมมีค่าเท่ากันทุกแถว

ตัวอย่างเช่นสมมติว่าคุณมีโต๊ะGardensFlowers( nameของสวนflowerที่เติบโตในสวน):

INSERT INTO GardensFlowers VALUES
('Central Park',       'Magnolia'),
('Hyde Park',          'Tulip'),
('Gardens By The Bay', 'Peony'),
('Gardens By The Bay', 'Cherry Blossom');

และคุณต้องการที่จะสกัดดอกไม้ทั้งหมดที่เติบโตในสวนที่ดอกไม้หลายดอกเติบโต จากนั้นคุณต้องใช้แบบสอบถามย่อยตัวอย่างเช่นคุณสามารถใช้สิ่งนี้:

SELECT GardensFlowers.*
FROM   GardensFlowers
WHERE  name IN (SELECT   name
                FROM     GardensFlowers
                GROUP BY name
                HAVING   COUNT(DISTINCT flower)>1);

หากคุณต้องการแยกดอกไม้ทั้งหมดที่เป็นดอกไม้เดียวในสวนแทนคุณสามารถเปลี่ยนเงื่อนไขการมีHAVING COUNT(DISTINCT flower)=1ได้ แต่ MySql ยังอนุญาตให้คุณใช้สิ่งนี้:

SELECT   GardensFlowers.*
FROM     GardensFlowers
GROUP BY name
HAVING   COUNT(DISTINCT flower)=1;

ไม่มีแบบสอบถามย่อยไม่ใช่ SQL มาตรฐาน แต่ง่ายกว่า

การใช้ส่วนขยายไม่ถูกต้องไปยัง GROUP BY clause

แต่จะเกิดอะไรขึ้นหากคุณเลือกคอลัมน์ที่ไม่รวมคอลัมน์ที่ไม่เท่ากันสำหรับทุกแถว ค่าใดที่ MySql เลือกสำหรับคอลัมน์นั้น

ดูเหมือนว่า MySql จะเลือกค่าแรกที่พบเสมอ

เพื่อให้แน่ใจว่าค่าแรกที่พบนั้นเป็นค่าที่คุณต้องการคุณต้องใช้ a GROUP BYกับคิวรีที่สั่งซื้อดังนั้นจึงจำเป็นต้องใช้คิวรีย่อย คุณไม่สามารถทำได้

เมื่อทำการสันนิษฐานว่า MySql เลือกแถวแรกที่พบเสมอคุณกำลังเรียงลำดับแถวก่อนกลุ่มอย่างถูกต้อง แต่น่าเสียดายที่ถ้าคุณอ่านเอกสารอย่างละเอียดคุณจะสังเกตเห็นว่าข้อสันนิษฐานนี้ไม่เป็นความจริง

เมื่อเลือกคอลัมน์ที่ไม่รวมที่ไม่เหมือนกันเสมอMySql มีอิสระในการเลือกค่าใด ๆ ดังนั้นค่าผลลัพธ์ที่แสดงจริงจะไม่สามารถระบุได้

ฉันเห็นว่าเคล็ดลับนี้เพื่อให้ได้มาซึ่งค่าแรกของคอลัมน์ที่ไม่รวมถูกใช้บ่อยครั้งและมักใช้งานได้ / เกือบทุกครั้งฉันก็ใช้มันเช่นกันในบางครั้ง (ด้วยความเสี่ยงของตัวเอง) แต่เนื่องจากมันไม่ได้บันทึกไว้คุณจึงไม่สามารถไว้วางใจพฤติกรรมนี้ได้

ลิงค์นี้ (ขอบคุณ ypercube!) GROUP BY trick ได้รับการปรับปรุงให้ดีที่สุดแสดงสถานการณ์ที่แบบสอบถามเดียวกันส่งคืนผลลัพธ์ที่แตกต่างระหว่าง MySql และ MariaDB อาจเป็นเพราะเครื่องมือเพิ่มประสิทธิภาพที่แตกต่างกัน

ดังนั้นหากเคล็ดลับนี้ใช้งานได้มันเป็นเรื่องของโชค

คำตอบที่ได้รับการยอมรับในคำถามอื่น ๆ ที่มีลักษณะผิดกับฉัน:

HAVING wp_posts.post_date = MAX(wp_posts.post_date)

wp_posts.post_dateเป็นคอลัมน์ที่ไม่รวมตัวกันและค่าของมันจะถูกบึกบึนอย่างเป็นทางการ แต่มันน่าจะเป็นคอลัมน์แรกที่post_dateพบ แต่เนื่องจากเคล็ดลับ GROUP BY ถูกนำไปใช้กับตารางที่ไม่มีการเรียงลำดับจึงไม่แน่ใจว่าสิ่งใดที่post_dateพบครั้งแรก

มันอาจจะส่งคืนโพสต์ที่เป็นโพสต์เดียวของผู้เขียนคนเดียว แต่ก็ไม่แน่ใจเสมอไป

ทางออกที่เป็นไปได้

ฉันคิดว่านี่อาจเป็นทางออกที่เป็นไปได้:

SELECT wp_posts.*
FROM   wp_posts
WHERE  id IN (
  SELECT max(id)
  FROM wp_posts
  WHERE (post_author, post_date) = (
    SELECT   post_author, max(post_date)
    FROM     wp_posts
    WHERE    wp_posts.post_status='publish'
             AND wp_posts.post_type='post'
    GROUP BY post_author
  ) AND wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
  GROUP BY post_author
)

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

(หากคุณแน่ใจว่าIDมีการเพิ่มขึ้นเพียงอย่างเดียวและหากID1 > ID2หมายถึงpost_date1 > post_date2เช่นนั้นอาจทำให้การสืบค้นง่ายขึ้น แต่ฉันไม่แน่ใจว่าเป็นเช่นนั้นหรือไม่)


นั่นextension to GROUP Byคือการอ่านที่น่าสนใจขอบคุณสำหรับสิ่งนั้น
Rob Forrest


คอลัมน์ Nonaggregated ในการแสดงออกทางเลือกกับ GROUP BY ไม่มีผลงานอีกต่อไปโดยเริ่มต้นกับ MySQL 5.7: stackoverflow.com/questions/34115174/... IMHO ไหนที่ปลอดภัยกว่าและบังคับให้บางคนเขียนข้อความค้นหาที่มีประสิทธิภาพมากขึ้น
rink.attendant.6

คำตอบนี้ใช้แบบสอบถามย่อยหรือไม่? ผู้โพสต์ดั้งเดิมไม่ถามหาวิธีแก้ปัญหาที่ไม่ได้ใช้แบบสอบถามย่อยหรือไม่?
TV-C-15

1
@ TV-C-15 ปัญหาคือการใช้การค้นหาข้อความค้นหาย่อยและฉันกำลังอธิบายว่าเพราะเหตุใดการค้นหาข้อความค้นหาย่อยจึงไม่ทำงาน แม้คำตอบที่ได้รับการยอมรับจะใช้แบบสอบถามย่อย แต่ก็เริ่มอธิบายว่าทำไมการหันมาใช้ความคิดที่ไม่ดี ( การใช้คำสั่งซื้อโดยใช้แบบสอบถามย่อยไม่ใช่ทางออกที่ดีที่สุดสำหรับปัญหานี้ )
fthiella

9

สิ่งที่คุณกำลังอ่านจะค่อนข้างแฮ็กดังนั้นอย่าลองทำที่บ้าน!

ใน SQL โดยทั่วไปคำตอบสำหรับคำถามของคุณคือNOแต่เนื่องจากโหมดที่ผ่อนคลายของGROUP BY(กล่าวถึงโดย@bluefeet ) คำตอบคือYESใน MySQL

สมมติว่าคุณมีดัชนี BTREE ใน (post_status, post_type, post_author, post_date) ดัชนีมีลักษณะอย่างไรภายใต้ประทุน?

(post_status = 'เผยแพร่', post_type = 'โพสต์', post_author = 'ผู้ใช้ A', post_date = '2012-12-01') (post_status = 'เผยแพร่', post_type = 'โพสต์', post_author = 'ผู้ใช้ A', post_date = '2012-12-31') (post_status = 'เผยแพร่', post_type = 'โพสต์', post_author = 'ผู้ใช้ B', post_date = '2012-10-01') (post_status = 'เผยแพร่', post_type = ' โพสต์ ', post_author =' ผู้ใช้ B ', post_date =' 2012-12-01 ')

นั่นคือข้อมูลจะถูกจัดเรียงตามเขตข้อมูลทั้งหมดในลำดับจากน้อยไปหามาก

เมื่อคุณทำGROUP BYตามค่าเริ่มต้นมันจะเรียงลำดับข้อมูลตามเขตข้อมูลการจัดกลุ่ม ( post_authorในกรณีของเราคือ post_status, post_type เป็นข้อกำหนดตามWHEREข้อ) และหากมีดัชนีที่ตรงกันจะใช้ข้อมูลสำหรับแต่ละระเบียนแรกในลำดับจากน้อยไปหามาก นั่นคือแบบสอบถามจะดึงข้อมูลต่อไปนี้ (โพสต์แรกสำหรับผู้ใช้แต่ละคน):

(post_status = 'เผยแพร่', post_type = 'โพสต์', post_author = 'ผู้ใช้ A', post_date = '2012-12-01') (post_status = 'เผยแพร่', post_type = 'โพสต์', post_author = 'ผู้ใช้ B', post_date = '2012/10/01')

แต่GROUP BYใน MySQL อนุญาตให้คุณระบุคำสั่งอย่างชัดเจน และเมื่อคุณขอpost_userเรียงจากมากไปน้อยมันก็จะผ่านดัชนีของเราในลำดับตรงกันข้ามยังคงบันทึกแรกสำหรับแต่ละกลุ่มที่เป็นจริงสุดท้าย

นั่นคือ

...
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC

จะให้เรา

(post_status = 'เผยแพร่', post_type = 'โพสต์', post_author = 'ผู้ใช้ B', post_date = '2012-12-01') (post_status = 'เผยแพร่', post_type = 'โพสต์', post_author = 'ผู้ใช้ A', post_date = '2012-12-31')

ตอนนี้เมื่อคุณสั่งซื้อผลลัพธ์ของการจัดกลุ่มโดย post_date คุณจะได้รับข้อมูลที่คุณต้องการ

SELECT wp_posts.*
FROM wp_posts
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC
ORDER BY wp_posts.post_date DESC;

หมายเหตุ :

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

ข้อผิดพลาด : ข้อเสียของวิธีการคือ

  • ผลลัพธ์ของแบบสอบถามขึ้นอยู่กับดัชนีซึ่งขัดต่อเจตนารมณ์ของ SQL (ดัชนีควรเพิ่มความเร็วการสืบค้น);
  • ดัชนีไม่ทราบอะไรเกี่ยวกับอิทธิพลที่มีต่อคิวรี (คุณหรือคนอื่น ๆ ในอนาคตอาจพบว่าดัชนีใช้ทรัพยากรมากเกินไปและเปลี่ยนแปลงอย่างใดก็ตามทำให้การแบ่งผลลัพธ์คิวรีไม่เพียง แต่ประสิทธิภาพ)
  • หากคุณไม่เข้าใจวิธีการทำงานของแบบสอบถามส่วนใหญ่คุณจะลืมคำอธิบายในหนึ่งเดือนและแบบสอบถามจะทำให้คุณและเพื่อนร่วมงานสับสน

ข้อดีคือประสิทธิภาพในกรณียาก ในกรณีนี้ประสิทธิภาพของการสืบค้นควรเหมือนกับในการสืบค้นของ @ bluefeet เนื่องจากจำนวนข้อมูลที่เกี่ยวข้องในการเรียงลำดับ (ข้อมูลทั้งหมดถูกโหลดลงในตารางชั่วคราวแล้วเรียงลำดับแล้ว btw แบบสอบถามของเขาต้องการ(post_status, post_type, post_author, post_date)ดัชนีเช่นกัน) .

สิ่งที่ฉันอยากจะแนะนำ :

ดังที่ฉันได้กล่าวแบบสอบถามเหล่านั้นทำให้ MySQL เสียเวลาในการเรียงลำดับข้อมูลจำนวนมหาศาลในตารางชั่วคราว ในกรณีที่คุณต้องการเพจ (ที่เกี่ยวข้องกับ LIMIT) ข้อมูลส่วนใหญ่จะถูกโยนทิ้ง สิ่งที่ฉันจะทำคือลดจำนวนข้อมูลที่เรียงลำดับ: นั่นคือการเรียงลำดับและ จำกัด ข้อมูลขั้นต่ำในเคียวรีย่อยจากนั้นเข้าร่วมกลับไปที่ตารางทั้งหมด

SELECT * 
FROM wp_posts
INNER JOIN
(
  SELECT max(post_date) post_date, post_author
  FROM wp_posts
  WHERE post_status='publish' AND post_type='post'
  GROUP BY post_author
  ORDER BY post_date DESC
  -- LIMIT GOES HERE
) p2 USING (post_author, post_date)
WHERE post_status='publish' AND post_type='post';

แบบสอบถามเดียวกันโดยใช้วิธีการที่อธิบายข้างต้น:

SELECT *
FROM (
  SELECT post_id
  FROM wp_posts
  WHERE post_status='publish' AND post_type='post'
  GROUP BY post_author DESC
  ORDER BY post_date DESC
  -- LIMIT GOES HERE
) as ids
JOIN wp_posts USING (post_id);

ทุกคำสั่งผู้ที่มีแผนดำเนินการของพวกเขาในSQLFiddle


นั่นเป็นเทคนิคที่น่าสนใจที่คุณได้ไปที่นั่น สองสิ่ง: คุณบอกว่าอย่าลองทำที่บ้านสิ่งที่เป็นอันตรายที่อาจเกิดขึ้น? ประการที่สองคุณพูดถึงคำตอบของ Bluefeet รุ่นที่แก้ไขเล็กน้อยสิ่งที่จะเป็นอย่างไร
Rob Forrest

ขอบคุณสำหรับสิ่งนั้นมันน่าสนใจมากที่เห็นใครบางคนโจมตีปัญหาด้วยวิธีที่แตกต่างกัน เนื่องจากชุดข้อมูลของฉันอยู่ใกล้แถว 18 ล้าน + ของคุณฉันไม่คิดว่าประสิทธิภาพเป็นสิ่งสำคัญเท่ากับการบำรุงรักษาดังนั้นฉันคิดว่าตัวเลือกในภายหลังของคุณอาจเหมาะสมกว่า ฉันชอบความคิดของข้อ จำกัด ด้านในของแบบสอบถามย่อย
Rob Forrest

8

ลองอันนี้. เพียงแค่ได้รับรายชื่อของโพสต์ล่าสุดวันจากแต่ละผู้เขียน แค่นั้นแหละ

SELECT wp_posts.* FROM wp_posts WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post' AND wp_posts.post_date IN(SELECT MAX(wp_posts.post_date) FROM wp_posts GROUP BY wp_posts.post_author) 

@ Rob Forrest ตรวจสอบทางออกของฉัน มันช่วยแก้ไขคำถามของคุณหวังว่า!
sanchitkhanna26

1
ฉันขอโทษฉันไม่คิดว่ามันจะได้ผล ตัวอย่างเช่นหากทั้งผู้แต่ง 1 และผู้แต่ง 2 เผยแพร่บางอย่างใน 01/02/13 จากนั้นผู้เขียน 2 โพสต์สิ่งใหม่ใน 08/02/13 โพสต์ทั้ง 3 จะถูกส่งกลับ ใช่ฟิลด์ datetime มีเวลาดังนั้นสถานการณ์จึงมีโอกาสน้อยลง แต่ไม่รับประกันว่าจะมีชุดข้อมูลขนาดใหญ่เพียงพอ
Rob Forrest

+1 post_date IN (select max(...) ...)สำหรับใช้ สิ่งนี้มีประสิทธิภาพมากกว่าการทำกลุ่มด้วยการเลือกย่อยดูที่dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html
Seaux

เพียงชี้แจงให้ชัดเจนซึ่งจะเหมาะสมกว่าหากคุณมีการจัดทำดัชนี post_author
Seaux

1
IN ( SELECT ... )มีประสิทธิภาพน้อยกว่าการเข้าร่วมที่เทียบเท่า
Rick James

3

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


ถึงอย่างไรคุณจะตอบความคิดเห็นของ Bluefeet ได้อย่างไรว่าแบบสอบถามชนิดนี้ไม่ถูกต้องไวยากรณ์ SQL และดังนั้นจึงไม่สามารถเคลื่อนย้ายข้ามแพลตฟอร์มฐานข้อมูลได้ นอกจากนี้ยังมีข้อกังวลว่าไม่มีการรับประกันว่าสิ่งนี้จะให้ผลลัพธ์ที่ถูกต้องทุกครั้ง
Rob Forrest

2

เพียงแค่ใช้ฟังก์ชั่นสูงสุดและฟังก์ชั่นกลุ่ม

    select max(taskhistory.id) as id from taskhistory
            group by taskhistory.taskid
            order by taskhistory.datum desc

3
จะเกิดอะไรขึ้นถ้ารหัสที่มีรหัสสูงสุดไม่ใช่โพสต์ล่าสุด ตัวอย่างนี้อาจเป็นได้ว่าผู้เขียนถือโพสต์ของเขาในร่างเป็นเวลานานก่อนที่จะโพสต์
Rob Forrest

0

เพียงเพื่อสรุปวิธีการแก้ปัญหามาตรฐานใช้แบบสอบถามย่อย uncorrelated และมีลักษณะเช่นนี้:

SELECT x.*
  FROM my_table x
  JOIN (SELECT grouping_criteria,MAX(ranking_criterion) max_n FROM my_table GROUP BY grouping_criteria) y
    ON y.grouping_criteria = x.grouping_criteria
   AND y.max_n = x.ranking_criterion;

หากคุณใช้ MySQL รุ่นเก่าหรือชุดข้อมูลขนาดเล็กคุณสามารถใช้วิธีการต่อไปนี้:

SELECT x.*
  FROM my_table x
  LEFT
  JOIN my_table y
    ON y.joining_criteria = x.joining_criteria
   AND y.ranking_criteria < x.ranking_criteria
 WHERE y.some_non_null_column IS NULL;  

เมื่อคุณพูดรุ่นโบราณ MySQL รุ่นนี้จะทำงานต่อไปอย่างไร และขออภัยไม่ชุดข้อมูลของฉันมีขนาดค่อนข้างใหญ่
Rob Forrest

มันจะทำงานได้ช้าลงในทุกรุ่น เวอร์ชันที่เก่ากว่าไม่สามารถใช้เคียวรีย่อยได้
สตรอเบอร์รี่

ใช่วิธี # 2 (รุ่นที่ฉันลองมาจากที่นี่ ) จะไม่ทำงานบนชุดข้อมูลขนาดใหญ่ (ล้านแถว) ทำให้เกิดข้อผิดพลาดในการเชื่อมต่อที่หายไป วิธีที่ # 1 ใช้เวลา ~ 15 วินาทีในการดำเนินการค้นหา ตอนแรกฉันต้องการหลีกเลี่ยงการใช้ข้อความค้นหาซ้อน แต่สิ่งนี้ทำให้ฉันพิจารณาใหม่ ขอบคุณ!
aexl

@TheSexiestManinJamaica ใช่ มีการเปลี่ยนแปลงไม่มากใน 3.5 ปี สมมติว่าแบบสอบถามมีประสิทธิภาพในตัวเองดังนั้นเวลาที่ใช้ในการดำเนินการแบบสอบถามนั้นขึ้นอยู่กับขนาดของชุดข้อมูลการจัดเรียงดัชนีและฮาร์ดแวร์ที่มีอยู่
สตรอเบอร์รี่

-1

** ข้อความค้นหาย่อยอาจส่งผลเสียต่อประสิทธิภาพเมื่อใช้กับชุดข้อมูลขนาดใหญ่ **

ข้อความค้นหาดั้งเดิม

SELECT wp_posts.*
FROM   wp_posts
WHERE  wp_posts.post_status = 'publish'
       AND wp_posts.post_type = 'post'
GROUP  BY wp_posts.post_author
ORDER  BY wp_posts.post_date DESC; 

ข้อความค้นหาที่แก้ไข

SELECT p.post_status,
       p.post_type,
       Max(p.post_date),
       p.post_author
FROM   wp_posts P
WHERE  p.post_status = "publish"
       AND p.post_type = "post"
GROUP  BY p.post_author
ORDER  BY p.post_date; 

เนื่องจากฉันกำลังใช้maxในselect clause==> max(p.post_date)มันเป็นไปได้ที่จะหลีกเลี่ยงคิวรีย่อยเลือกและเรียงตามคอลัมน์สูงสุดหลังจากกลุ่มโดย


1
สิ่งนี้จะส่งคืน post_date ล่าสุดต่อผู้เขียน แต่ไม่รับประกันว่าข้อมูลที่เหลือที่ถูกส่งคืนจะเกี่ยวข้องกับการโพสต์ด้วย post_date ล่าสุด
Rob Forrest

@RobForrest -> ฉันไม่เข้าใจว่าทำไม เป็นความคิดที่ดีที่จะอธิบายคำตอบของคุณให้ละเอียดและเพียงแค่ทำการเคลม เท่าที่ฉันเข้าใจว่าข้อมูลรับประกันว่าจะเกี่ยวข้องกันตามที่ฉันใช้ในส่วนที่ข้อเพื่อกรองข้อมูลที่เกี่ยวข้อง
guykaplan

1
คุณจะถูกต้องทั้งหมด 4 ฟิลด์ที่คุณเลือกจะเกี่ยวข้องกับ post_date สูงสุดนั้น แต่สิ่งนี้ไม่ตอบคำถามที่ถูกถาม ตัวอย่างเช่นหากคุณเพิ่ม post_id หรือเนื้อหาของโพสต์คอลัมน์เหล่านั้นจะไม่สามารถมั่นใจได้ว่ามาจากระเบียนเดียวกันกับวันที่สูงสุด ในการรับแบบสอบถามของคุณด้านบนเพื่อส่งกลับรายละเอียดส่วนที่เหลือของโพสต์คุณจะต้องเรียกใช้แบบสอบถามที่สอง หากคำถามเกี่ยวกับการค้นหาวันที่โพสต์ล่าสุดใช่แล้วคุณจะตอบคำถามได้ดี
Rob Forrest

@guykaplan คำถามย่อยไม่ช้า ขนาดของชุดข้อมูลไม่สำคัญ ขึ้นอยู่กับว่าคุณใช้มันอย่างไร ดูpercona.com/blog/2010/03/18/when-the-subselect-runs-faster
Pacerier

@Pacerier: บทความนี้แสดงให้เห็นว่าคุณจะได้รับประโยชน์จากประสิทธิภาพการค้นหาย่อยได้อย่างไร แต่ฉันชอบที่จะเห็นคุณแปลงสถานการณ์ที่กำหนดให้ทำงานได้ดีขึ้น และขนาดข้อมูลมีความสำคัญอีกครั้งในบทความที่คุณโพสต์คุณได้รับสมมติว่ามีเพียงหนึ่งตารางที่จะทำงานกับ ขนาดข้อมูลไม่ได้ตามขนาดแถว แต่โดยขนาดความซับซ้อน ต้องบอกว่าถ้าคุณกำลังทำงานกับตารางย่อยที่มีขนาดใหญ่มาก (ไม่เกี่ยวข้องกับหลายตาราง) แบบสอบถามย่อยอาจทำงานได้ดีกว่ามาก
guykaplan

-4

ก่อนอื่นอย่าใช้ * ในรายการที่เลือกมีผลต่อประสิทธิภาพและขัดขวางการใช้กลุ่มโดยและเรียงลำดับตาม ลองใช้แบบสอบถามนี้:

SELECT wp_posts.post_author, wp_posts.post_date as pdate FROM wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author           
ORDER BY pdate DESC

เมื่อคุณไม่ระบุตารางใน ORDER BY เพียงนามแฝงพวกเขาจะเรียงลำดับผลลัพธ์ของการเลือก


ละเว้นการเลือก * พวกมันมีความกะทัดรัดในตัวอย่างนี้ คำตอบของคุณตรงกับตัวอย่างแรกที่ฉันให้
Rob Forrest

นามแฝงไม่มีผลกับแถวที่ได้รับคืนหรือการเรียงลำดับของผลลัพธ์
Rob Forrest
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.