วิธีการสุ่มเลือกแถวใน SQL?


226

ฉันใช้ MSSQL Server 2005 ใน db ของฉันฉันมีตาราง "customerNames" ซึ่งมีสองคอลัมน์ "Id" และ "ชื่อ" และประมาณ 1,000 ผลลัพธ์

ฉันกำลังสร้างฟังก์ชั่นที่ฉันต้องเลือก 5 ลูกค้าแบบสุ่มทุกครั้ง ทุกคนสามารถบอกวิธีสร้างแบบสอบถามซึ่งจะได้รับสุ่ม 5 แถว (Id และชื่อ) ทุกครั้งเมื่อมีการดำเนินการค้นหา


สุ่มไม่ได้เป็นความต้องการทั่วไปสำหรับฐานข้อมูลที่ผมรู้สึกประหลาดใจที่จะหาการเชื่อมโยงสำหรับบาง SQL
Paxic

2
ขึ้นอยู่กับว่าคุณต้องการสุ่มเลือกมากแค่ไหน ดู: msdn.microsoft.com/en-us/library/aa175776(SQL.80).aspxสำหรับการเปรียบเทียบ NEW_ID กับ RAND ()
Shannon Severance

คำตอบ:


639
SELECT TOP 5 Id, Name FROM customerNames
ORDER BY NEWID()

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

การเลือกแถวสุ่มใน SQL

เลือกแถวสุ่มด้วย 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

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

SELECT column FROM table 
ORDER BY RANDOM() LIMIT 1

3
+1 สำหรับการโพสต์คำตอบโดยตรงบน SO แทนที่จะเชื่อมโยงไปยังไซต์ภายนอก (เช่นคำตอบที่ยอมรับได้) ซึ่งอาจล้มเหลวเมื่อผู้ใช้ในอนาคตดูคำถามนี้
เรย์โจว

17
สิ่งนี้มีราคาแพงมากในตารางขนาดใหญ่หรือไม่ซึ่งแต่ละแถวจะได้รับหมายเลขสุ่มจากนั้นจะมีการเรียงลำดับชุดตัวเลขสุ่มขนาดใหญ่ที่ไม่ได้ทำดัชนีหรือไม่?
Andrey

นี่อาจจะชัดเจนสำหรับคนส่วนใหญ่ แต่ไม่ชัดเจนสำหรับฉัน ... ข้อความค้นหาต่อไปนี้จะไม่ได้รับค่าสุ่มใหม่สำหรับแต่ละแถว: update tbl_vouchers set tbl_UsersID = (select top(1) id from tbl_Users order by NEWID()) - แก้ไข: ฉันไม่สามารถจัดรูปแบบการทำงานในความคิดเห็น :(
Mir

คุณอัจฉริยะ! ฉันเกลียดคุณมากเพราะฉันไม่เห็นสิ่งนี้จนกว่าฉันจะไปแล้วและเขียนแบบสอบถามยาว ๆ อย่างเมามันพร้อมคิวรีย่อยและหมายเลขแถว
greenkode

5
คำเตือน: สำหรับฐานข้อมูลขนาดใหญ่วิธีนี้จะมีประสิทธิภาพไม่ดี คุณสามารถจินตนาการถึงเวลาที่ต้องใช้ในการสร้างค่าสุ่มสำหรับแต่ละแถวได้หรือไม่หากฐานข้อมูลมีรายการนับล้านรายการ คุณสามารถมีข้อมูลเพิ่มเติมเกี่ยวกับและทางเลือกที่ดีกว่าที่นี่
Francis Ngueukam


11

ในกรณีที่มีคนต้องการโซลูชัน PostgreSQL:

select id, name
from customer
order by random()
limit 5;

คำตอบนี้ดีสำหรับ PostgreSQL ไม่จำเป็นต้องมีขีด จำกัด
aliasbody

9

บางทีเว็บไซต์นี้อาจได้รับความช่วยเหลือ

สำหรับผู้ที่ไม่ต้องการคลิก:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

2
ควรจะได้อย่างน้อย 1 แทนที่ด้วย 5 :)
เมตรโรมัน

7

มีโซลูชันเฉพาะของ Microsoft SQL Server 2005 ที่ดีที่นี่ จัดการกับปัญหาที่คุณกำลังทำงานกับชุดผลลัพธ์ขนาดใหญ่ (ไม่ใช่คำถามที่ฉันรู้)

การเลือกแถวแบบสุ่มจากตารางขนาดใหญ่ http://msdn.microsoft.com/en-us/library/cc441928.aspx


5

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

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

https://msdn.microsoft.com/en-us/library/cc441928.aspx


โปรดทราบว่าสิ่งนี้จะเลือกประมาณ 10% ของแถวในตาราง หากคุณต้องการเลือกจำนวนแถวที่แน่นอนหรืออย่างน้อย N แถววิธีนี้จะใช้ไม่ได้
LarsH

4

นี่เป็นคำถามเก่า แต่การพยายามใช้ฟิลด์ใหม่ (ทั้ง NEWID () หรือ ORDER BY แรนด์ ()) กับตารางที่มีแถวจำนวนมากจะมีราคาแพง หากคุณมี ID ที่เพิ่มขึ้นเฉพาะ (และไม่มีรูใด ๆ ) จะมีประสิทธิภาพมากกว่าในการคำนวณ X # ของ ID ที่เลือกแทนการใช้ GUID หรือคล้ายกับแถวเดี่ยวทุกแถวจากนั้นจึงนำ X # อันดับแรกของ

DECLARE @minValue int;
DECLARE @maxValue int;
SELECT @minValue = min(id), @maxValue = max(id) from [TABLE];

DECLARE @randomId1 int, @randomId2 int, @randomId3 int, @randomId4 int, @randomId5 int
SET @randomId1 = ((@maxValue + 1) - @minValue) * Rand() + @minValue
SET @randomId2 = ((@maxValue + 1) - @minValue) * Rand() + @minValue
SET @randomId3 = ((@maxValue + 1) - @minValue) * Rand() + @minValue
SET @randomId4 = ((@maxValue + 1) - @minValue) * Rand() + @minValue
SET @randomId5 = ((@maxValue + 1) - @minValue) * Rand() + @minValue

--select @maxValue as MaxValue, @minValue as MinValue
--  , @randomId1 as SelectedId1
--  , @randomId2 as SelectedId2
--  , @randomId3 as SelectedId3
--  , @randomId4 as SelectedId4
--  , @randomId5 as SelectedId5

select * from [TABLE] el
where el.id in (@randomId1, @randomId2, @randomId3, @randomId4, @randomId5)

หากคุณต้องการเลือกแถวอื่น ๆ อีกมากมายฉันจะดูการเติม #tempTable ด้วย ID และค่า rand () จำนวนหนึ่งจากนั้นใช้ค่า rand () แต่ละค่าเพื่อปรับมาตราส่วนเป็นค่า min-max ด้วยวิธีนี้คุณไม่จำเป็นต้องกำหนดพารามิเตอร์ @ randomId1 ... n ทั้งหมด ฉันได้รวมตัวอย่างด้านล่างโดยใช้ CTE เพื่อเติมข้อมูลตารางเริ่มต้น

DECLARE @NumItems int = 100;

DECLARE @minValue int;
DECLARE @maxValue int;
SELECT @minValue = min(id), @maxValue = max(id) from [TABLE];
DECLARE @range int = @maxValue+1 - @minValue;

with cte (n) as (
   select 1 union all
   select n+1 from cte
   where n < @NumItems
)
select cast( @range * rand(cast(newid() as varbinary(100))) + @minValue as int) tp
into #Nt
from cte;

select * from #Nt ntt
inner join [TABLE] i on i.id = ntt.tp;

drop table #Nt;

@Protiguous การแก้ไขที่คุณเสนอได้ทำให้การเลือกแบบสุ่ม การใช้ min () และ max () นำไปใช้กับตาราง dbo.Tally64k จะไม่อนุญาตให้ผู้ใช้เลือกแถวที่มี pk id> 65556
RIanGillis

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

@Protiguous Ah ฉันเห็นว่าตอนนี้ฉันสับสนเพราะคุณใช้ 0-65k เมื่อทำ min-max แต่ไม่ช้า หลังจากการแก้ไขครั้งล่าสุดของคุณฉันต้องการถามคุณเกี่ยวกับผลกระทบของการเปลี่ยนแปลงที่เกิดขึ้นเนื่องจากการปรับประสิทธิภาพเป็นหนึ่งในความสนใจของฉันและการตัดสินใจที่ไม่มีความหมายเหมือนด้านใดของเครื่องหมายเท่ากับที่คุณวางบางสิ่งบางอย่าง - สิ่งเดียวกันนี้จะมีผลกับการเรียก 5 SET @ randomId ## หรือไม่ หรือแตกต่างกันเพราะมันไม่ได้เลือกจากตารางจริง
RIanGillis

ฉันไม่แน่ใจว่าฉันเข้าใจคำถามของคุณ คุณถามว่าทำไมมี 5 SET แทน 1 SELECT @ id1 = rand (), @ id2 = rand () .. ? เป็นเพราะการเรียก rand () หลายรายการใน 1 คำสั่งจะให้ผลลัพธ์เหมือนกันดังนั้น SET ที่แยกออกมา (rand () บน SQL Server เป็นฟังก์ชั่นที่กำหนดขึ้นมาฉันเชื่อว่า) ฉันเดาว่า 1 select vs 5 set อยู่ในช่วงประสิทธิภาพระดับนาโนวินาที
คลุมเครือ

4
SELECT * FROM TABLENAME ORDER BY random() LIMIT 5; 

คำถามเก่า แต่คำตอบนี้ไม่ได้ทำงานสำหรับฉันใน Oracle
Bear

SELECT * FROM (SELECT * FROM เรียงตามตารางตาม DBMS_RANDOM.VALUE) WHERE rownum <number; @Bear ลองสิ่งนี้
Narendra

3

ฉันพบว่าสิ่งนี้ทำงานได้ดีที่สุดสำหรับข้อมูลขนาดใหญ่

SELECT TOP 1 Column_Name FROM dbo.Table TABLESAMPLE(1 PERCENT);

TABLESAMPLE(n ROWS) or TABLESAMPLE(n PERCENT)เป็นแบบสุ่ม แต่ต้องเพิ่มTOP nเพื่อให้ได้ขนาดตัวอย่างที่ถูกต้อง

การใช้NEWID()ช้ามากในตารางขนาดใหญ่


0

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

โปรดทราบว่าการเรียงชุดผลลัพธ์ขนาดใหญ่โดยใช้ฟังก์ชั่น RANDOM อาจกลายเป็นช้ามากดังนั้นโปรดตรวจสอบให้แน่ใจว่าคุณใช้ชุดผลลัพธ์ขนาดเล็ก

หากคุณต้องสับเปลี่ยนชุดผลลัพธ์ขนาดใหญ่และ จำกัด ในภายหลังจากนั้นควรใช้บางอย่างเช่นOracleSAMPLE(N)หรือTABLESAMPLEในSQL ServerหรือPostgreSQLแทนฟังก์ชันแบบสุ่มในข้อ ORDER BY

ดังนั้นสมมติว่าเรามีตารางฐานข้อมูลต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

และแถวต่อไปนี้ในsongตาราง:

| id | artist                          | title                              |
|----|---------------------------------|------------------------------------|
| 1  | Miyagi & Эндшпиль ft. Рем Дигга | I Got Love                         |
| 2  | HAIM                            | Don't Save Me (Cyril Hahn Remix)   |
| 3  | 2Pac ft. DMX                    | Rise Of A Champion (GalilHD Remix) |
| 4  | Ed Sheeran & Passenger          | No Diggity (Kygo Remix)            |
| 5  | JP Cooper ft. Mali-Koa          | All This Love                      |

คำพยากรณ์

บน Oracle คุณต้องใช้DBMS_RANDOM.VALUEฟังก์ชันดังแสดงในตัวอย่างต่อไปนี้:

SELECT
    artist||' - '||title AS song
FROM song
ORDER BY DBMS_RANDOM.VALUE

เมื่อเรียกใช้แบบสอบถาม SQL ดังกล่าวบน Oracle เราจะได้รับชุดผลลัพธ์ต่อไปนี้:

| song                                              |
|---------------------------------------------------|
| JP Cooper ft. Mali-Koa - All This Love            |
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |

ขอให้สังเกตว่าเพลงนั้นอยู่ในลำดับแบบสุ่มต้องขอบคุณDBMS_RANDOM.VALUEฟังก์ชันการโทรที่ใช้โดยส่วนคำสั่ง ORDER BY

เซิร์ฟเวอร์ SQL

บน SQL Server คุณต้องใช้NEWIDฟังก์ชันดังแสดงในตัวอย่างต่อไปนี้:

SELECT
    CONCAT(CONCAT(artist, ' - '), title) AS song
FROM song
ORDER BY NEWID()

เมื่อเรียกใช้แบบสอบถาม SQL ดังกล่าวบน SQL Server เราจะได้รับชุดผลลัพธ์ต่อไปนี้:

| song                                              |
|---------------------------------------------------|
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |
| JP Cooper ft. Mali-Koa - All This Love            |
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |

ขอให้สังเกตว่าเพลงนั้นอยู่ในลำดับแบบสุ่มต้องขอบคุณNEWIDฟังก์ชันการโทรที่ใช้โดยส่วนคำสั่ง ORDER BY

PostgreSQL

บน PostgreSQL คุณต้องใช้randomฟังก์ชันดังแสดงในตัวอย่างต่อไปนี้:

SELECT
    artist||' - '||title AS song
FROM song
ORDER BY random()

เมื่อเรียกใช้คิวรี SQL ดังกล่าวข้างต้นบน PostgreSQL เราจะได้รับชุดผลลัพธ์ต่อไปนี้:

| song                                              |
|---------------------------------------------------|
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |
| JP Cooper ft. Mali-Koa - All This Love            |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |

ขอให้สังเกตว่าเพลงนั้นอยู่ในลำดับแบบสุ่มต้องขอบคุณrandomฟังก์ชันการโทรที่ใช้โดยส่วนคำสั่ง ORDER BY

MySQL

บน MySQL คุณจำเป็นต้องใช้RANDฟังก์ชั่นดังที่แสดงในตัวอย่างต่อไปนี้:

SELECT
  CONCAT(CONCAT(artist, ' - '), title) AS song
FROM song
ORDER BY RAND()

เมื่อรันเคียวรี SQL ดังกล่าวบน MySQL เราจะได้ชุดผลลัพธ์ต่อไปนี้:

| song                                              |
|---------------------------------------------------|
| HAIM - Don't Save Me (Cyril Hahn Remix)           |
| Ed Sheeran & Passenger - No Diggity (Kygo Remix)  |
| Miyagi & Эндшпиль ft. Рем Дигга - I Got Love      |
| 2Pac ft. DMX - Rise Of A Champion (GalilHD Remix) |
| JP Cooper ft. Mali-Koa - All This Love            |

ขอให้สังเกตว่าเพลงนั้นอยู่ในลำดับแบบสุ่มต้องขอบคุณRANDฟังก์ชันการโทรที่ใช้โดยส่วนคำสั่ง ORDER BY


0

หากคุณกำลังใช้ตารางขนาดใหญ่และต้องการเข้าถึง 10 เปอร์เซ็นต์ของข้อมูลให้เรียกใช้คำสั่งต่อไปนี้: SELECT TOP 10 PERCENT * FROM Table1 ORDER BY NEWID();

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