คำแนะนำแรกของ Pradeep Adiga ORDER BY NEWID()
นั้นใช้ได้และบางสิ่งบางอย่างที่ฉันเคยใช้ในอดีตด้วยเหตุผลนี้
ระวังการใช้RAND()
- ในบริบทจำนวนมากมันจะถูกดำเนินการเพียงครั้งเดียวต่อคำสั่งดังนั้นORDER BY RAND()
จะไม่มีผลกระทบ (ในขณะที่คุณได้รับผลลัพธ์เดียวกันจาก RAND () สำหรับแต่ละแถว)
ตัวอย่างเช่น
SELECT display_name, RAND() FROM tr_person
ส่งกลับแต่ละชื่อจากตารางบุคคลของเราและหมายเลข "สุ่ม" ซึ่งจะเหมือนกันสำหรับแต่ละแถว จำนวนจะแตกต่างกันไปในแต่ละครั้งที่คุณเรียกใช้แบบสอบถาม แต่จะเหมือนกันสำหรับแต่ละแถวในแต่ละครั้ง
เพื่อแสดงให้เห็นว่าเป็นกรณีเดียวกันกับที่RAND()
ใช้ในORDER BY
ประโยคฉันลอง:
SELECT display_name FROM tr_person ORDER BY RAND(), display_name
ผลลัพธ์ยังคงจัดเรียงตามชื่อที่ระบุว่าฟิลด์เรียงลำดับก่อนหน้า (อันที่คาดว่าจะสุ่ม) ไม่มีผลดังนั้นสันนิษฐานว่ามีค่าเดียวกันเสมอ
การสั่งซื้อโดยNEWID()
ใช้งานได้เพราะหาก NEWID () ไม่ได้ประเมินใหม่เสมอวัตถุประสงค์ของ UUID นั้นจะแตกเมื่อทำการแทรกแถวใหม่จำนวนมากในหนึ่ง statemnt ด้วยตัวระบุที่ไม่ซ้ำกันดังนั้น:
SELECT display_name FROM tr_person ORDER BY NEWID()
จะเรียงลำดับชื่อ "สุ่ม"
DBMS อื่น ๆ
ข้างต้นเป็นจริงสำหรับ MSSQL (2005 และ 2008 อย่างน้อยและถ้าฉันจำได้อย่างถูกต้อง 2,000 เช่นกัน) ฟังก์ชันที่ส่งคืน UUID ใหม่ควรได้รับการประเมินทุกครั้งใน DBMSs NEWID ทั้งหมด () อยู่ภายใต้ MSSQL แต่มันก็คุ้มค่าที่จะตรวจสอบเรื่องนี้ในเอกสารประกอบและ / หรือโดยการทดสอบของคุณเอง พฤติกรรมของฟังก์ชั่นผลลัพธ์อื่น ๆ โดยพลการเช่น RAND () มีแนวโน้มที่จะแตกต่างกันระหว่าง DBMS ดังนั้นตรวจสอบเอกสารอีกครั้ง
นอกจากนี้ฉันได้เห็นการสั่งซื้อโดยค่า UUID ที่ถูกละเว้นในบริบทบางอย่างเนื่องจากฐานข้อมูลสมมติว่าประเภทนั้นไม่มีการเรียงลำดับที่มีความหมาย หากคุณพบว่าสิ่งนี้เป็นกรณีดังกล่าวอย่างชัดเจนโยน UUID กับชนิดสตริงในส่วนคำสั่งหรือห่อฟังก์ชั่นอื่น ๆ รอบ ๆ เช่นCHECKSUM()
ใน SQL Server (อาจมีความแตกต่างของประสิทธิภาพเล็กน้อยจากนี้เช่นกันจะทำการสั่งซื้อ ค่า 32- บิตไม่ใช่ 128- บิตแม้ว่าประโยชน์ที่ได้นั้นจะมากกว่าค่าใช้จ่ายในการรันCHECKSUM()
ต่อค่าแรกหรือไม่ก่อนอื่นฉันจะให้คุณทดสอบ)
หมายเหตุด้านข้าง
หากคุณต้องการการสั่งซื้อแบบกำหนดเอง แต่ทำซ้ำได้บ้างสั่งซื้อโดยชุดย่อยบางส่วนที่ค่อนข้างไม่มีการควบคุมของข้อมูลในแถวเอง ยกตัวอย่างเช่นอย่างใดอย่างหนึ่งหรือเหล่านี้จะกลับชื่อในคำสั่งโดยพลการ แต่ทำซ้ำ:
SELECT display_name FROM tr_person ORDER BY CHECKSUM(display_name), display_name -- order by the checksum of some of the row's data
SELECT display_name FROM tr_person ORDER BY SUBSTRING(display_name, LEN(display_name)/2, 128) -- order by part of the name field, but not in any an obviously recognisable order)
การเรียงลำดับตามอำเภอใจ แต่ทำซ้ำได้มักจะไม่เป็นประโยชน์ในแอปพลิเคชันแม้ว่าจะมีประโยชน์ในการทดสอบถ้าคุณต้องการทดสอบโค้ดบางอย่างเกี่ยวกับผลลัพธ์ในคำสั่งซื้อที่หลากหลาย ผลลัพธ์ในการรันหลายครั้งหรือการทดสอบที่การแก้ไขที่คุณทำไว้กับโค้ดจะลบปัญหาหรือความไม่มีประสิทธิภาพที่เน้นไว้ก่อนหน้านี้โดยชุดผลลัพธ์ที่ป้อนเข้ามาอย่างใดอย่างหนึ่งหรือเพียงเพื่อทดสอบว่ารหัสของคุณ "เสถียร" หากส่งข้อมูลเดียวกันตามลำดับที่กำหนด)
เคล็ดลับนี้ยังสามารถใช้เพื่อให้ได้ผลลัพธ์ตามอำเภอใจมากขึ้นจากฟังก์ชั่นซึ่งไม่อนุญาตให้มีการโทรที่ไม่กำหนดเช่น NEWID () ภายในร่างกายของพวกเขา อีกครั้งนี่ไม่ใช่สิ่งที่มักจะมีประโยชน์ในโลกแห่งความเป็นจริง แต่อาจมีประโยชน์ถ้าคุณต้องการให้ฟังก์ชันส่งคืนบางสิ่งบางอย่างแบบสุ่มและ "random-ish" นั้นดีพอ (แต่ระวังให้จำกฎที่กำหนดไว้ เมื่อฟังก์ชั่นที่ผู้ใช้กำหนดได้รับการประเมินซึ่งโดยปกติแล้วจะมีเพียงหนึ่งครั้งต่อแถวมิฉะนั้นผลลัพธ์ของคุณอาจไม่ตรงตามที่คุณต้องการ / ต้องการ)
ประสิทธิภาพ
ตามที่ EBarr ชี้ให้เห็นอาจมีปัญหาเรื่องประสิทธิภาพกับข้อใดข้อหนึ่งข้างต้น สำหรับแถวมากกว่าสองสามแถวคุณเกือบจะรับประกันว่าจะเห็นเอาต์พุตสพูลออกไปยัง tempdb ก่อนที่จะอ่านจำนวนแถวที่ร้องขอตามลำดับที่ถูกต้องซึ่งหมายความว่าแม้ว่าคุณกำลังมองหา 10 อันดับแรกคุณอาจพบดัชนีเต็ม การสแกน (หรือแย่กว่านั้นคือการสแกนตาราง) เกิดขึ้นพร้อมกับบล็อกจำนวนมากในการเขียนไปยัง tempdb ดังนั้นมันจึงมีความสำคัญอย่างยิ่งเช่นเดียวกับสิ่งต่างๆส่วนใหญ่ในการเปรียบเทียบข้อมูลจริงก่อนที่จะใช้ในการผลิต