จะขอแถวสุ่มใน SQL ได้อย่างไร


510

ฉันจะขอสุ่มแถว (หรือใกล้เคียงกับการสุ่มอย่างแท้จริงเท่าที่จะทำได้) ใน SQL บริสุทธิ์ได้อย่างไร


ผมเคยทำแบบนี้เสมอใน PHP หลังจากที่ผลการค้นหาจาก SQL ... นี่อาจจะเป็นเร็วมากสำหรับการประมวลผลตามวิธีการแก้ปัญหาของวงเงิน 1 รยางค์
CheeseConQueso


2
ดูเหมือนว่าไม่มีโซลูชัน "pure SQL" ที่ทำงานบน dbms ทุกตัว ... มีวิธีแก้ปัญหาสำหรับแต่ละรายการ
มนู

คำตอบ:


735

ดูโพสต์นี้: SQL เพื่อเลือกแถวสุ่มจากตารางฐานข้อมูล มันจะผ่านวิธีการในการทำสิ่งนี้ใน MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 และ Oracle (ต่อไปนี้ถูกคัดลอกมาจากลิงค์นั้น):

เลือกแถวสุ่มด้วย MySQL:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

เลือกแถวสุ่มด้วย PostgreSQL:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

เลือกแถวสุ่มด้วย Microsoft SQL Server:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

เลือกแถวสุ่มด้วย IBM DB2

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

เลือกบันทึกแบบสุ่มด้วย Oracle:

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1

30
-1 สำหรับการพึ่งพาorder by rand()หรือเทียบเท่าในทุก dbs: | นอกจากนี้ยังกล่าวถึงที่นี่
AD7six

20
สิบปีที่ผ่านมามีชายคนหนึ่งพูดว่าการใช้ORDER BY RAND()ผิด ...
trejder

สั่งซื้อโดย NEWID () ดูเหมือนว่าจะช้ากว่าอย่างชัดเจนใน SQL Server ข้อความค้นหาของฉันดูเหมือนว่า: เลือกด้านบน 1,000 C.CustomerId, CL.LoginName จากการเข้าร่วมภายในลูกค้า C LinkedAccount LA บน C.CustomerId = LA.CustomerId ภายในการเข้าร่วม CustomerLogin CL บน C.CustomerId = CL.CustomerId, CL ชื่อล็อกอินที่มีจำนวน (*)> 1 คำสั่งโดย NEWID () การลบบรรทัด "คำสั่งซื้อโดย NEWID ()" จะให้ผลลัพธ์ที่เร็วกว่ามาก
เบ็นพาวเวอร์

3
สำหรับ SQLite ให้ใช้ฟังก์ชัน RANDOM ()
สแล

10
โซลูชั่นเหล่านี้ไม่ได้ปรับขนาด พวกเขาอยู่O(n)กับnจำนวนระเบียนในตาราง ลองนึกภาพคุณมี 1 ล้านบันทึกคุณต้องการสร้างตัวเลขสุ่ม 1 ล้านตัวหรือรหัสเฉพาะหรือไม่? ฉันควรใช้COUNT()และเกี่ยวข้องกับมันในการLIMITแสดงออกใหม่ด้วยตัวเลขสุ่มเดียว
Christian Hujer

174

โซลูชันอย่าง Jeremies:

SELECT * FROM table ORDER BY RAND() LIMIT 1

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

SELECT * FROM table WHERE num_value >= RAND() * 
    ( SELECT MAX (num_value ) FROM table ) 
ORDER BY num_value LIMIT 1

สิ่งนี้ใช้ได้ในเวลาลอการิทึมโดยไม่คำนึงถึงขนาดของตารางหากnum_valueถูกทำดัชนี คำเตือน: นี้สันนิษฐานว่ามีการกระจายอย่างเท่าเทียมกันในช่วงnum_value 0..MAX(num_value)หากชุดข้อมูลของคุณเบี่ยงเบนไปจากสมมติฐานนี้คุณจะได้รับผลลัพธ์ที่เบ้ (บางแถวจะปรากฏบ่อยกว่าชุดอื่น ๆ )


8
ข้อเสนอแนะที่สองไม่ได้สุ่ม คุณไม่สามารถคาดเดาแถวที่จะถูกเลือกได้ แต่ถ้าคุณต้องเดิมพันคุณจะเดิมพันในแถวที่สอง และคุณไม่เคยเดิมพันในแถวสุดท้ายมันมีโอกาสน้อยที่จะเลือกสิ่งที่เป็นการกระจายของ num_value ของคุณและโต๊ะของคุณใหญ่แค่ไหน
Etienne Racine

1
ฉันรู้ว่าโดยทั่วไปแล้วฟังก์ชั่น RAND () ไม่ได้มีคุณภาพสูงมาก แต่นอกจากนี้คุณสามารถโปรดอธิบายเพิ่มเติมได้อย่างละเอียดว่าทำไมการเลือกจะไม่สุ่ม?
Panther สีเทา

13
คนแรกคือผิดใน SQL Server ฟังก์ชั่น RAND () ถูกเรียกเพียงครั้งเดียวต่อการค้นหาไม่ใช่ครั้งเดียวต่อแถว ดังนั้นจึงเลือกแถวแรกเสมอ (ลอง)
Jeff Walker Code Ranger

3
ส่วนที่สองสมมติว่าแถวทั้งหมดมีสัดส่วน: เป็นไปได้ที่จะเลือกแถวที่ถูกลบไปแล้ว
Sam Rueby

3
@ Sam.Rueby จริงแล้ว num_value> = RAND () ... ขีด จำกัด 1 ทำให้แน่ใจได้ว่าจะไม่มีการข้ามแถวที่ว่างเปล่าจนกว่าจะพบแถวที่มีอยู่
ghord

62

ฉันไม่รู้ว่ามันมีประสิทธิภาพเพียงใด แต่ฉันเคยใช้มาก่อน:

SELECT TOP 1 * FROM MyTable ORDER BY newid()

เนื่องจาก GUID นั้นเป็นแบบสุ่มการสั่งซื้อจึงหมายความว่าคุณจะได้แถวแบบสุ่ม


1
ฉันใช้เซิร์ฟเวอร์ MS SQL เลือก TOP 1 * จาก some_table_name สั่งซื้อโดย NEWID () ใช้งานได้ดีสำหรับฉันขอบคุณสำหรับคำแนะนำ!

นั่นเป็นสิ่งเดียวกับORDER BY RAND() LIMIT 1
Ken Bloom

6
นอกจากนี้ยังเป็นที่เฉพาะเจาะจงมากตั้งแต่ฐานข้อมูลจะใช้และTOP 1 newid()
สีเทา

12
นี่เป็นความคิดที่ไม่ดี วิธีนี้จะไม่ใช้ดัชนีเว้นแต่ว่าแต่ละคอลัมน์จะได้รับการจัดทำดัชนีแยกกัน ตารางที่มีระเบียน 100 ล้านรายการอาจใช้เวลานานมากในการรับหนึ่งระเบียน
เปลี่ยน

1
@ แม่มดและวิธีการแก้ปัญหาที่คุณจะเสนอ?
Akmal Salikhov

31
ORDER BY NEWID()

ใช้เวลา 7.4 milliseconds

WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table)

ใช้เวลา0.0065 milliseconds!

ฉันจะไปด้วยวิธีหลังแน่นอน


2
ตัวเลือกที่สองจะไม่เลือกแถวสุดท้าย ฉันไม่รู้ว่าทำไม - แค่ชี้มันออกมา
โวลเดอมอร์

7
@Voldemort: rand()ส่งกลับจำนวนจุดลอยตัวที่n 0 < n < 1สมมติว่าnum_valueเป็นจำนวนเต็มค่าส่งคืนของrand() * max(num_value)จะถูกบังคับให้เป็นจำนวนเต็มดังนั้นการตัดทอนอะไรก็ได้หลังจากจุดทศนิยม ดังนั้นrand() * max(num_value)จะน้อยกว่าเสมอmax(num_value)ซึ่งเป็นสาเหตุที่แถวสุดท้ายจะไม่ถูกเลือก
Ian Kemp

ฉันจะไม่มีประสิทธิภาพหากข้อมูลของฉันถูกลบบ่อยครั้ง - หากฉันพบช่องว่างฉันจะต้องทำการค้นหาใหม่ทั้งหมด
Loic Coenen

1
@IanKemp คำถามโง่ดังนั้นทำไมไม่ใช้ SELECT MAX (num_value) + 1 ??? เนื่องจาก rand (หรือ RANDOM ในกรณีส่วนใหญ่) ส่งคืน [0,1) คุณจะได้รับค่าเต็มรูปแบบ นอกจากนี้ใช่คุณถูกต้องต้องแก้ไขแบบสอบถาม
tekHedd

13

คุณไม่ได้พูดว่าคุณใช้เซิร์ฟเวอร์ตัวใด ใน SQL Server เวอร์ชันเก่าคุณสามารถใช้สิ่งนี้:

select top 1 * from mytable order by newid()

ใน SQL Server 2005 และสูงกว่าคุณสามารถใช้TABLESAMPLEเพื่อรับตัวอย่างสุ่มที่ทำซ้ำได้:

SELECT FirstName, LastName
FROM Contact 
TABLESAMPLE (1 ROWS) ;

9
MSDN กล่าวว่า newid () เป็นที่ต้องการมากกว่า tablesample เพื่อให้ได้ผลลัพธ์แบบสุ่มอย่างแท้จริง: msdn.microsoft.com/en-us/library/ms189108.aspx
Andrew Hedges

7
@Andrew Hedges: การสั่งซื้อโดย NEWID () มีราคาแพงเกินไป
Andrei Rînea

10

สำหรับ SQL Server

newid () / order by จะทำงานได้ แต่จะมีราคาแพงมากสำหรับชุดผลลัพธ์ขนาดใหญ่เพราะต้องสร้างรหัสสำหรับทุกแถวแล้วเรียงลำดับ

TABLESAMPLE () เป็นสิ่งที่ดีจากจุดยืนด้านประสิทธิภาพ แต่คุณจะได้รับผลลัพธ์จำนวนมาก (แถวทั้งหมดในหน้าจะถูกส่งคืน)

สำหรับตัวอย่างสุ่มจริงที่มีประสิทธิภาพดีกว่าวิธีที่ดีที่สุดคือกรองแถวแบบสุ่ม ฉันพบตัวอย่างโค้ดต่อไปนี้ในบทความ SQL Server Books Online การจำกัด ชุดผลลัพธ์โดยใช้ TABLESAMPLE :

หากคุณต้องการสุ่มตัวอย่างแถวแต่ละแถวจริงๆให้แก้ไขแบบสอบถามเพื่อกรองแถวแบบสุ่มแทนที่จะใช้ TABLESAMPLE ตัวอย่างเช่นแบบสอบถามต่อไปนี้ใช้ฟังก์ชัน NEWID เพื่อส่งกลับประมาณหนึ่งเปอร์เซ็นต์ของแถวของตาราง Sales.SalesOrderDetail:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float)
              / CAST (0x7fffffff AS int)

คอลัมน์ SalesOrderID จะรวมอยู่ในนิพจน์ CHECKSUM เพื่อให้ NEWID () ประเมินหนึ่งครั้งต่อแถวเพื่อให้ได้การสุ่มตัวอย่างตามแต่ละแถว นิพจน์ CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) ประเมินค่าเป็นทศนิยมแบบสุ่มระหว่าง 0 และ 1

เมื่อทำงานกับตารางที่มี 1,000,000 แถวผลลัพธ์ของฉันคือ

SET STATISTICS TIME ON
SET STATISTICS IO ON

/* newid()
   rows returned: 10000
   logical reads: 3359
   CPU time: 3312 ms
   elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()

/* TABLESAMPLE
   rows returned: 9269 (varies)
   logical reads: 32
   CPU time: 0 ms
   elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)

/* Filter
   rows returned: 9994 (varies)
   logical reads: 3359
   CPU time: 641 ms
   elapsed time: 627 ms
*/    
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
              / CAST (0x7fffffff AS int)

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

หากคุณสามารถใช้งาน TABLESAMPLE ได้มันจะให้ประสิทธิภาพที่ดีที่สุดแก่คุณ มิฉะนั้นใช้วิธี newid () / filter newid () / คำสั่งซื้อควรเป็นทางเลือกสุดท้ายหากคุณมีชุดผลลัพธ์จำนวนมาก


4

หากเป็นไปได้ให้ใช้ข้อความสั่งที่เก็บไว้เพื่อหลีกเลี่ยงความไม่มีประสิทธิภาพของดัชนีทั้งสองใน RND () และสร้างฟิลด์หมายเลขบันทึก

เตรียมการบันทึกแบบสุ่มจาก "SELECT * จากข้อ จำกัด ของตาราง?, 1";
SET @ n = ชั้น (RAND () * (เลือก COUNT (*) จากตาราง));
ดำเนินการ RandomRecord โดยใช้ @n;

โซลูชันนี้ยังดูแลการส่งคืนแถวแบบสุ่มเมื่อค่าตัวเลขที่จัดทำดัชนีใช้ในตำแหน่งที่ข้อด้านบนไม่กระจายเท่ากัน ดังนั้นแม้ว่าจะใช้เวลา (คงที่) เกือบเท่ากันกับการใช้โดยที่ id_value> = RAND () * MAX (id_value) จะดีกว่า
guido

เท่าที่ฉันสามารถบอกได้ว่านี่ไม่ได้ทำงานในเวลาคงที่มันจะทำงานในเวลาเชิงเส้น ในกรณีที่เลวร้ายที่สุด @n เท่ากับจำนวนแถวในตารางและ "SELECT * FROM ตาราง LIMIT? 1" จะประเมิน @n - 1 แถวจนกว่าจะถึงแถวสุดท้าย
Andres Riofrio

3

วิธีที่ดีที่สุดคือการใส่ค่าแบบสุ่มในคอลัมน์ใหม่เพื่อจุดประสงค์นั้นและใช้บางอย่างเช่นนี้ (รหัสเทียม + SQL):

randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")

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

โซลูชั่น newid () อาจต้องใช้การสแกนแบบเต็มตารางเพื่อให้แต่ละแถวสามารถกำหนด guid ใหม่ซึ่งจะมีประสิทธิภาพน้อยกว่ามาก

rand () solution อาจไม่ทำงานเลย (เช่นกับ MSSQL) เพราะฟังก์ชั่นจะได้รับการประเมินเพียงครั้งเดียวและทุกแถวจะได้รับหมายเลข "สุ่ม" เดียวกัน


1
ล้อมรอบเมื่อคุณได้รับผลลัพธ์ 0 ให้ตัวอย่างสุ่มพิสูจน์ได้ (ไม่ใช่แค่ "ดีพอ") โซลูชันนี้เกือบจะปรับให้เป็นการค้นหาหลายแถว (คิดว่า "ปาร์ตี้สลับ") ปัญหาคือผลลัพธ์มักจะถูกเลือกในกลุ่มเดียวกันซ้ำ ๆ ในการหลีกเลี่ยงปัญหานี้คุณจะต้องกระจายหมายเลขสุ่มที่คุณเพิ่งใช้ไปอีกครั้ง คุณสามารถโกงโดยการติดตาม randomNo และตั้งค่าให้เป็น max (randomness) จากผลลัพธ์ แต่จากนั้น p (แถวที่ฉันในแบบสอบถาม 1 และแถวที่ฉันกับแบบสอบถาม 2) == 0 ซึ่งไม่ยุติธรรม ให้ฉันทำคณิตศาสตร์และฉันจะกลับไปหาคุณด้วยรูปแบบที่ยุติธรรมอย่างแท้จริง
alsuren

3

สำหรับ SQL Server 2005 และ 2008 ถ้าเราต้องการตัวอย่างสุ่มของแต่ละแถว (จากBooks Online ):

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)

3

กรณีที่ใช้ RAND () เนื่องจากไม่ได้รับการสนับสนุนคุณอาจได้รับ ID สูงสุด (= สูงสุด)

SELECT MAX(ID) FROM TABLE;

รับการสุ่มระหว่าง 1..Max (= My_Generated_Random)

My_Generated_Random = rand_in_your_programming_lang_function(1..Max);

จากนั้นเรียกใช้ SQL นี้:

SELECT ID FROM TABLE WHERE ID >= My_Generated_Random ORDER BY ID LIMIT 1

โปรดทราบว่ามันจะตรวจสอบแถวใด ๆ ที่รหัสเท่ากับหรือสูงกว่าค่าที่เลือก นอกจากนี้ยังเป็นไปได้ที่จะค้นหาแถวในตารางและรับ ID ที่เท่ากันหรือต่ำกว่า My_Generated_Random จากนั้นปรับเปลี่ยนแบบสอบถามเช่นนี้:

SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1

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

ไม่มีอะไร คุณได้รับหมายเลขประจำตัวที่ไม่ถูกต้อง หากคุณพิจารณาว่าจะลบ id = 1 ให้แลกเปลี่ยน 1 ด้วยค่าต่ำสุด
forsberg

2

ตามที่ระบุไว้ในความคิดเห็นของ @ BillKarwin ในคำตอบของ @ cnu ...

เมื่อรวมกับ LIMIT ฉันพบว่ามันทำงานได้ดีขึ้นมาก (อย่างน้อยกับ PostgreSQL 9.1) เพื่อเข้าร่วมกับการสั่งซื้อแบบสุ่มแทนที่จะสั่งซื้อแถวจริงโดยตรง: เช่น

SELECT * FROM tbl_post AS t
JOIN ...
JOIN ( SELECT id, CAST(-2147483648 * RANDOM() AS integer) AS rand
       FROM tbl_post
       WHERE create_time >= 1349928000
     ) r ON r.id = t.id
WHERE create_time >= 1349928000 AND ...
ORDER BY r.rand
LIMIT 100

ตรวจสอบให้แน่ใจว่า 'r' สร้างค่า 'rand' สำหรับทุกค่าคีย์ที่เป็นไปได้ในเคียวรีที่ซับซ้อนซึ่งรวมอยู่ด้วย แต่ยังคง จำกัด จำนวนแถวของ 'r' เท่าที่เป็นไปได้

CAST as Integer มีประโยชน์อย่างยิ่งสำหรับ PostgreSQL 9.2 ซึ่งมีการเพิ่มประสิทธิภาพการเรียงลำดับเฉพาะสำหรับประเภทจำนวนเต็มและจำนวนทศนิยมที่แม่นยำ


1

โซลูชันส่วนใหญ่ที่นี่มีเป้าหมายเพื่อหลีกเลี่ยงการเรียงลำดับ แต่พวกเขายังคงต้องทำการสแกนตามลำดับบนตาราง

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

วิธีแก้ปัญหาต่อไปนี้ใช้กับ PostgreSQL 8.4:

explain analyze select * from cms_refs where rec_id in 
  (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint 
   from generate_series(1,10))
  limit 1;

ฉันแก้ปัญหาข้างต้นคุณเดา 10 ค่าดัชนีสุ่มต่าง ๆ จากช่วง 0 .. [ค่าสุดท้ายของ id]

หมายเลข 10 นั้นเป็นกฎเกณฑ์ - คุณอาจใช้ 100 หรือ 1,000 ก็ได้ (น่าอัศจรรย์) ไม่มีผลกระทบต่อเวลาตอบสนอง

นอกจากนี้ยังมีปัญหาอย่างใดอย่างหนึ่ง - ถ้าคุณมีรหัสเบาบางคุณอาจจะพลาด วิธีแก้ปัญหาคือมีแผนสำรองข้อมูล :) ในกรณีนี้การสั่งซื้อแบบเก่าโดยการสุ่ม () แบบสอบถาม เมื่อ id ที่รวมกันมีลักษณะเช่นนี้:

explain analyze select * from cms_refs where rec_id in 
    (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint 
     from generate_series(1,10))
    union all (select * from cms_refs order by random() limit 1)
    limit 1;

ไม่ใช่สหภาพ ข้อทั้งหมด ในกรณีนี้ถ้าส่วนแรกส่งคืนข้อมูลใด ๆ ส่วนที่สองจะไม่ถูกประหาร!


1

ช่วงปลายปี แต่มาถึงที่นี่ผ่าน Google ดังนั้นเพื่อลูกหลานฉันจะเพิ่มทางเลือกอื่น

อีกวิธีคือใช้ท็อปสองครั้งโดยสลับคำสั่งซื้อ ฉันไม่รู้ว่ามันเป็น "pure SQL" หรือไม่เพราะใช้ตัวแปรใน TOP แต่ทำงานได้ใน SQL Server 2008 นี่คือตัวอย่างที่ฉันใช้เทียบกับคำในพจนานุกรมถ้าฉันต้องการคำแบบสุ่ม

SELECT TOP 1
  word
FROM (
  SELECT TOP(@idx)
    word 
  FROM
    dbo.DictionaryAbridged WITH(NOLOCK)
  ORDER BY
    word DESC
) AS D
ORDER BY
  word ASC

แน่นอน @idx เป็นจำนวนเต็มที่สร้างแบบสุ่มซึ่งมีช่วงตั้งแต่ 1 ถึง COUNT (*) บนตารางเป้าหมายโดยรวม หากคอลัมน์ของคุณได้รับการจัดทำดัชนีคุณจะได้รับประโยชน์เช่นกัน ข้อดีอีกอย่างคือคุณสามารถใช้ในฟังก์ชั่นได้เนื่องจาก NEWID () ไม่ได้รับอนุญาต

สุดท้ายแบบสอบถามข้างต้นจะทำงานในเวลาประมาณ 1/10 ของเวลา exec ของ NEWID () - ประเภทของแบบสอบถามในตารางเดียวกัน YYMV


1

คุณอาจลองใช้new id()ฟังก์ชั่น

เพียงแค่เขียนแบบสอบถามของคุณและใช้การเรียงลำดับตามnew id()ฟังก์ชั่น มันค่อนข้างสุ่ม


1

สำหรับ MySQL ที่จะได้รับการบันทึกแบบสุ่ม

 SELECT name
  FROM random AS r1 JOIN
       (SELECT (RAND() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1

รายละเอียดเพิ่มเติมhttp://jan.kneschke.de/projects/mysql/order-by-rand/


หลังจากทดสอบคำตอบมากมายฉันเชื่อว่านี่เป็นคำตอบที่ดีที่สุด ดูเหมือนว่าจะเร็วและเลือกหมายเลขสุ่มที่ดีในแต่ละครั้ง ดูเหมือนว่าคล้ายกับข้อเสนอแนะที่สองของ @GreyPanther ด้านบน แต่คำตอบนี้เลือกตัวเลขสุ่มมากขึ้น
Jeff Baker

1

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

สำหรับ MS SQL:

ตัวอย่างขั้นต่ำ:

select top 10 percent *
from table_name
order by rand(checksum(*))

เวลาดำเนินการปกติ: 1.00

ตัวอย่าง NewId ():

select top 10 percent *
from table_name
order by newid()

เวลาดำเนินการปกติ: 1.02

NewId()ช้ากว่าไม่มีนัยสำคัญrand(checksum(*))ดังนั้นคุณอาจไม่ต้องการใช้กับชุดบันทึกขนาดใหญ่

การคัดเลือกด้วยเมล็ดเริ่มต้น:

declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */

select top 10 percent *
from table_name
order by rand(checksum(*) % seed) /* any other math function here */

หากคุณต้องการเลือกชุดเดียวกันที่ได้รับเมล็ดดูเหมือนว่าจะทำงาน


1

ใน MSSQL (ทดสอบบน 11.0.5569) โดยใช้

SELECT TOP 100 * FROM employee ORDER BY CRYPT_GEN_RANDOM(10)

เร็วกว่า

SELECT TOP 100 * FROM employee ORDER BY NEWID()

1

ใน SQL Server คุณสามารถรวม TABLESAMPLE กับ NEWID () เพื่อรับการสุ่มที่ดีและยังมีความเร็ว สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณต้องการเพียง 1 หรือจำนวนน้อย ๆ ของแถว

SELECT TOP 1 * FROM [table] 
TABLESAMPLE (500 ROWS) 
ORDER BY NEWID()

1

ด้วย SQL Server 2012+ คุณสามารถใช้ แบบสอบถาม OFFSET FETCHเพื่อทำสิ่งนี้สำหรับแถวสุ่มหนึ่งแถว

select  * from MyTable ORDER BY id OFFSET n ROW FETCH NEXT 1 ROWS ONLY

โดยที่ id คือคอลัมน์ข้อมูลประจำตัวและ n คือแถวที่คุณต้องการ - คำนวณเป็นตัวเลขสุ่มระหว่าง 0 และ count () - 1 ของตาราง (offset 0 คือแถวแรกหลังจากทั้งหมด)

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



0

ฉันต้องเห็นด้วยกับ CD-MaN: การใช้ "ORDER BY RAND ()" จะทำงานได้ดีสำหรับโต๊ะเล็ก ๆ หรือเมื่อคุณเลือก SELECT เพียงไม่กี่ครั้ง

ฉันยังใช้เทคนิค "num_value> = RAND () * ... " และถ้าฉันต้องการผลลัพธ์แบบสุ่มฉันมีคอลัมน์ "สุ่ม" พิเศษในตารางที่ฉันอัปเดตวันละครั้ง การอัปเดตครั้งเดียวนั้นจะใช้เวลาสักครู่ (โดยเฉพาะอย่างยิ่งเพราะคุณจะต้องมีดัชนีในคอลัมน์นั้น) แต่จะเร็วกว่าการสร้างตัวเลขสุ่มสำหรับทุกแถวในแต่ละครั้งที่เลือกทำงาน


0

ระวังเพราะ TableSample ไม่ส่งคืนสุ่มแถวตัวอย่าง มันนำทางแบบสอบถามของคุณเพื่อดูตัวอย่างสุ่มของหน้า 8KB ที่ประกอบขึ้นเป็นแถวของคุณ จากนั้นแบบสอบถามของคุณจะถูกดำเนินการกับข้อมูลที่มีอยู่ในหน้าเหล่านี้ เนื่องจากวิธีการจัดกลุ่มข้อมูลในหน้าเหล่านี้ (ลำดับการแทรก ฯลฯ ) สิ่งนี้อาจนำไปสู่ข้อมูลที่ไม่ใช่ตัวอย่างแบบสุ่ม

ดู: http://www.mssqltips.com/tip.asp?tip=1308

หน้า MSDN นี้สำหรับ TableSample รวมถึงตัวอย่างของวิธีการสร้างตัวอย่างข้อมูลจริงแบบสุ่ม

http://msdn.microsoft.com/en-us/library/ms189108.aspx


0

ดูเหมือนว่าแนวคิดจำนวนมากที่แสดงยังคงใช้การสั่งซื้อ

อย่างไรก็ตามหากคุณใช้ตารางชั่วคราวคุณสามารถกำหนดดัชนีแบบสุ่ม (เช่นวิธีแก้ปัญหาที่แนะนำ) และจากนั้นคว้าตารางแรกที่มากกว่าเลขสุ่มระหว่าง 0 ถึง 1

ตัวอย่างเช่น (สำหรับ DB2):

WITH TEMP AS (
SELECT COMLUMN, RAND() AS IDX FROM TABLE)
SELECT COLUMN FROM TABLE WHERE IDX > .5
FETCH FIRST 1 ROW ONLY

2
หลังจากพิจารณาวิธีนี้ฉันพบข้อบกพร่องพื้นฐานในตรรกะของฉัน สิ่งนี้จะคืนค่าการตั้งค่าขนาดเล็กที่เหมือนกันใกล้กับจุดเริ่มต้นของตารางเพราะฉันสมมติว่าถ้ามีการแจกแจงแบบหนึ่งระหว่าง 0 ถึง 1 มีโอกาส 50% ที่แถวแรกจะตรงกับเกณฑ์นั้น
DAVID


0

มีวิธีแก้ปัญหาที่ดีกว่าสำหรับ Oracle แทนที่จะใช้ dbms_random.value ในขณะที่ต้องการสแกนแบบเต็มเพื่อเรียงลำดับแถวโดย dbms_random.value และค่อนข้างช้าสำหรับตารางขนาดใหญ่

ใช้สิ่งนี้แทน:

SELECT *
FROM employee sample(1)
WHERE rownum=1


0

สำหรับ SQL Server 2005 ขึ้นไปให้ขยาย @ GreyPanther ให้คำตอบสำหรับเคสที่num_valueไม่มีค่าต่อเนื่อง สิ่งนี้ใช้ได้สำหรับกรณีที่เมื่อเราไม่ได้กระจายชุดข้อมูลอย่างสม่ำเสมอและเมื่อnum_valueไม่ได้เป็นตัวเลข แต่เป็นตัวระบุที่ไม่ซ้ำกัน

WITH CTE_Table (SelRow, num_value) 
AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ID) AS SelRow, num_value FROM table
) 

SELECT * FROM table Where num_value = ( 
    SELECT TOP 1 num_value FROM CTE_Table  WHERE SelRow >= RAND() * (SELECT MAX(SelRow) FROM CTE_Table)
)

-1

ฟังก์ชั่นแบบสุ่มจาก sql สามารถช่วยได้ นอกจากนี้หากคุณต้องการ จำกัด เพียงหนึ่งแถวให้เพิ่มเข้าไปในท้ายที่สุด

SELECT column FROM table
ORDER BY RAND()
LIMIT 1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.