สุ่มบันทึกจากตารางฐานข้อมูล (T-SQL)


85

มีวิธีที่รวบรัดในการดึงข้อมูลแบบสุ่มจากตารางเซิร์ฟเวอร์ sql หรือไม่?

ฉันต้องการสุ่มข้อมูลการทดสอบหน่วยของฉันดังนั้นกำลังมองหาวิธีง่ายๆในการเลือกรหัสสุ่มจากตาราง ในภาษาอังกฤษตัวเลือกจะเป็น "Select one id จากตารางโดยที่ id เป็นตัวเลขสุ่มระหว่าง id ต่ำสุดในตารางและ id สูงสุดในตาราง"

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

ไอเดีย?


มีสองวิธีที่นี่brettb.com/SQL_Help_Random_Numbers.asp
ตาข่าย

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

ลิงก์ด้านบนจาก @Mesh ไม่สามารถใช้งานได้อีกต่อไป
Robert Sievers

คำตอบ:


146

มีวิธีที่รวบรัดในการดึงข้อมูลแบบสุ่มจากตารางเซิร์ฟเวอร์ sql หรือไม่?

ใช่

SELECT TOP 1 * FROM table ORDER BY NEWID()

คำอธิบาย

A NEWID()ถูกสร้างขึ้นสำหรับแต่ละแถวจากนั้นตารางจะถูกจัดเรียงตามนั้น ระเบียนแรกจะถูกส่งกลับ (เช่นระเบียนที่มี GUID "ต่ำสุด")

หมายเหตุ

  1. GUID ถูกสร้างขึ้นเป็นตัวเลขสุ่มหลอกตั้งแต่เวอร์ชันสี่:

    UUID เวอร์ชัน 4 มีไว้สำหรับการสร้าง UUID จากตัวเลขสุ่มจริงหรือสุ่มหลอก

    อัลกอริทึมมีดังนี้:

    • ตั้งค่าบิตที่สำคัญที่สุดสองบิต (บิต 6 และ 7) ของ clock_seq_hi_and_reserved เป็นศูนย์และหนึ่งตามลำดับ
    • ตั้งค่าบิตที่สำคัญที่สุดสี่บิต (บิต 12 ถึง 15) ของฟิลด์ time_hi_and_version เป็นหมายเลขเวอร์ชัน 4 บิตจากส่วน 4.1.3
    • ตั้งค่าบิตอื่น ๆ ทั้งหมดเป็นค่าที่เลือกแบบสุ่ม (หรือสุ่มหลอก)

    - เนมสเปซ URN IDentifier (UUID) สากล - RFC 4122

  2. ทางเลือกSELECT TOP 1 * FROM table ORDER BY RAND()จะไม่ทำงานอย่างที่คิด RAND()ส่งคืนค่าเดียวต่อหนึ่งแบบสอบถามดังนั้นทุกแถวจะแชร์ค่าเดียวกัน

  3. แม้ว่าค่า GUID จะสุ่มหลอก แต่คุณจะต้องมี PRNG ที่ดีกว่าสำหรับแอปพลิเคชันที่มีความต้องการมากขึ้น

  4. ประสิทธิภาพโดยทั่วไปน้อยกว่า 10 วินาทีสำหรับประมาณ 1,000,000 แถว - แน่นอนขึ้นอยู่กับระบบ โปรดทราบว่าไม่สามารถเข้าถึงดัชนีได้ดังนั้นประสิทธิภาพจึงค่อนข้าง จำกัด


สิ่งที่ฉันกำลังมองหา ฉันรู้สึกว่ามันง่ายกว่าที่ฉันทำ
Jeremy

1
คุณกำลังสมมติว่า NEWID สร้างค่าเทียม มีโอกาสดีที่จะสร้างค่าตามลำดับ NEWID เพียงสร้างค่าที่ไม่ซ้ำกัน อย่างไรก็ตาม RAND สร้างค่าสุ่มหลอก
Skizz

ฉันเรียกใช้บนตารางที่มีการจัดทำดัชนีอย่างมากโดยมี 1,671,145 แถวและใช้เวลา 7 วินาทีในการส่งคืน ตารางก็ค่อนข้างเหมาะสมเช่นกันซึ่งแทบจะเป็นหัวใจของฐานข้อมูลของเราดังนั้นจึงได้รับการดูแล
Tom Ritter

@ ÂviewAnew. 1.6 ล้านแถวและ 7 วินาทีในการเลือกที่ไม่ (และไม่สามารถ) ตีดัชนีก็ไม่เลว
Sklivvz

7
@Skizz แรนด์ไม่ทำงานแบบนั้น ค่าสุ่มเดียวจะถูกสร้างขึ้นก่อนที่จะเลือก ดังนั้นหากคุณลอง "SELECT TOP 10 RAND () ... " คุณจะได้ค่าเท่าเดิมเสมอ
Sklivvz

27

บนโต๊ะขนาดใหญ่คุณยังสามารถใช้TABLESAMPLEเพื่อหลีกเลี่ยงการสแกนทั้งตาราง

SELECT  TOP 1 *
FROM YourTable
TABLESAMPLE (1000 ROWS)
ORDER BY NEWID()

ORDER BY NEWIDยังคงจำเป็นต้องหลีกเลี่ยงแถวเพียงกลับมาที่ปรากฏในหน้าแรกข้อมูล

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


ฉันพบสิ่งนี้ในเว็บไซต์ของ Microsoft: คุณสามารถใช้ TABLESAMPLE เพื่อส่งคืนตัวอย่างจากตารางขนาดใหญ่อย่างรวดเร็วเมื่อเงื่อนไขใด ๆ ต่อไปนี้เป็นจริง: ตัวอย่างไม่จำเป็นต้องเป็นตัวอย่างสุ่มอย่างแท้จริงในระดับของแต่ละแถว แถวในแต่ละหน้าของตารางจะไม่สัมพันธ์กับแถวอื่น ๆ ในหน้าเดียวกัน
Mark Entingh

1
@MarkEntingh - ในกรณีTOP 1นี้ไม่สำคัญว่าแถวในหน้าเดียวกันจะสัมพันธ์กันหรือไม่ คุณเลือกเพียงหนึ่งในนั้น
Martin Smith

9

ลองใช้วิธีของคุณเพื่อรับรหัสสุ่มระหว่าง MIN (Id) และ MAX (Id) แล้ว

SELECT TOP 1 * FROM table WHERE Id >= @yourrandomid

มันจะทำให้คุณได้หนึ่งแถวเสมอ


2
-1 สิ่งนี้จะใช้ได้เฉพาะเมื่อไม่มี ID ที่ขาดหายไประหว่าง min และ max หากมีการลบ ID เดียวกันนั้นจะถูกสร้างขึ้นโดยฟังก์ชันสุ่มคุณจะได้รับระเบียนกลับเป็นศูนย์
Neil N

6
@ นีลไม่จริง - มันจะทำให้คุณได้แถวแรกที่มี Id มากกว่าตัวเลขสุ่มหากไม่มี ID ปัญหาคือความน่าจะเป็นของแต่ละแถวที่ออกมานั้นไม่คงที่ แต่แล้วอีกครั้งก็เพียงพอแล้วในกรณีส่วนใหญ่
Sklivvz

1
+1. สำหรับการทดสอบหน่วยที่ควรตีค่าต่างๆที่ดีพอ - หากคุณต้องการการสุ่มจริงนี่เป็นอย่างอื่น แต่ในบริบทของ OP ควรจะดีพอ
TomTom

7

หากคุณต้องการเลือกข้อมูลขนาดใหญ่วิธีที่ดีที่สุดที่ฉันรู้คือ:

SELECT * FROM Table1
WHERE (ABS(CAST(
    (BINARY_CHECKSUM
    (keycol1, NEWID())) as int))
    % 100) < 10

ที่มา: MSDN


ฉันไม่แน่ใจ แต่ฉันคิดว่าการใช้ RAND () แทน NEWID () เพื่อสร้างตัวเลขสุ่มอย่างแท้จริงอาจดีกว่าเนื่องจากข้อเสียของการใช้ NEWID () ในกระบวนการเลือก
QMaster

ฉันลองใช้วิธีนี้กับจำนวนระเบียนที่แน่นอนแทนที่จะเป็นฐานเปอร์เซ็นต์ฉันทำด้วยการขยายช่วงที่เลือกและ จำกัด ด้วย TOP n มีข้อเสนอแนะหรือไม่
QMaster

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

0

ฉันต้องการปรับปรุงวิธีการที่ฉันได้ลองและเจอโพสต์นี้ ฉันรู้ว่ามันเก่า แต่วิธีนี้ไม่อยู่ในรายการ ฉันกำลังสร้างและใช้ข้อมูลการทดสอบ แสดงวิธีการสำหรับ "ที่อยู่" ใน SP ที่เรียกด้วย @st (สองสถานะถ่าน)

Create Table ##TmpAddress (id Int Identity(1,1), street VarChar(50), city VarChar(50), st VarChar(2), zip VarChar(5))
Insert Into ##TmpAddress(street, city, st, zip)
Select street, city, st, zip 
From tbl_Address (NOLOCK)
Where st = @st


-- unseeded RAND() will return the same number when called in rapid succession so
-- here, I seed it with a guaranteed different number each time. @@ROWCOUNT is the count from the most recent table operation.

Set @csr = Ceiling(RAND(convert(varbinary, newid())) * @@ROWCOUNT)

Select street, city, st, Right(('00000' + ltrim(zip)),5) As zip
From ##tmpAddress (NOLOCK)
Where id = @csr

0

หากคุณต้องการสุ่มตัวอย่างของแต่ละแถวให้แก้ไขคำค้นหาเพื่อกรองแถวแบบสุ่มแทนที่จะใช้ 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) จะประเมินเป็นค่า float แบบสุ่มระหว่าง 0 ถึง 1 "

ที่มา: http://technet.microsoft.com/en-us/library/ms189108(v=sql.105).aspx

มีคำอธิบายเพิ่มเติมด้านล่าง:

วิธีนี้ทำงานอย่างไร? มาแยกคำสั่ง WHERE ออกและอธิบาย

ฟังก์ชัน CHECKSUM กำลังคำนวณการตรวจสอบรายการในรายการ เป็นที่ถกเถียงกันอยู่ว่าจำเป็นต้องใช้ SalesOrderID หรือไม่เนื่องจาก NEWID () เป็นฟังก์ชันที่ส่งคืน GUID แบบสุ่มใหม่ดังนั้นการคูณตัวเลขสุ่มด้วยค่าคงที่ควรทำให้เกิดการสุ่มในทุกกรณี อันที่จริงการยกเว้น SalesOrderID ดูเหมือนจะไม่แตกต่างกัน หากคุณเป็นนักสถิติที่กระตือรือร้นและสามารถพิสูจน์ได้ว่าสิ่งนี้รวมอยู่ด้วยโปรดใช้ส่วนความคิดเห็นด้านล่างและแจ้งให้เราทราบว่าทำไมฉันถึงคิดผิด!

ฟังก์ชัน CHECKSUM จะส่งกลับค่า VARBINARY การดำเนินการแบบบิตและการดำเนินการด้วย 0x7fffffff ซึ่งเทียบเท่ากับ (111111111 ... ) ในไบนารีจะให้ค่าทศนิยมที่เป็นตัวแทนของสตริงสุ่ม 0 และ 1s การหารด้วย 0x7fffffff แบบสัมประสิทธิ์แบบสัมบูรณ์จะทำให้ตัวเลขทศนิยมนี้เป็นตัวเลขระหว่าง 0 ถึง 1 อย่างมีประสิทธิภาพจากนั้นเพื่อตัดสินใจว่าแต่ละแถวจะรวมเข้าในชุดผลลัพธ์สุดท้ายหรือไม่จะใช้เกณฑ์ 1 / x (ในกรณีนี้คือ 0.01) โดยที่ x คือเปอร์เซ็นต์ของข้อมูลที่จะดึงมาเป็นตัวอย่าง

ที่มา: https://www.mssqltips.com/sqlservertip/3157/different-ways-to-get-random-data-for-sql-server-data-sampling

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