ฉันจะกำหนดค่าสุ่มที่แตกต่างให้กับแต่ละแถวในคำสั่ง SELECT ได้อย่างไร


11

โปรดดูรหัสนี้:

create table #t1(
  id int identity (1,1),
  val varchar(10)
);


insert into #t1 values ('a');
insert into #t1 values ('b');
insert into #t1 values ('c');
insert into #t1 values ('d');

ตอนนี้เมื่อใดก็ตามที่คุณดำเนินการนี้

select *, 
    ( select top 1 val from #t1 order by NEWID()) rnd 
from #t1 order by 1;

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

id          val        rnd
----------- ---------- ----------
1           a          b
2           b          b
3           c          b
4           d          b

ฉันรู้วิธีใช้เคอร์เซอร์เพื่อวนรอบแถวและรับค่าสุ่มที่แตกต่างกัน แต่นั่นไม่ใช่การแสดง

ทางออกที่ฉลาดสำหรับเรื่องนี้คือ

select t1.id, t1.val, t2.val
from #t1 t1
    join (select *, ROW_NUMBER() over( order by NEWID()) lfd from #t1) as t2 on  t1.id = t2.lfd 

แต่ฉันทำให้การสืบค้นง่ายขึ้น แบบสอบถามจริงดูเหมือนว่ามากขึ้น

select *, 
    ( select top 1 val from t2 where t2.x <> t1.y order by NEWID()) rnd 
from t1 order by 1;

และวิธีแก้ปัญหาง่ายๆนั้นไม่เหมาะสม ฉันกำลังมองหาวิธีที่จะบังคับให้ประเมินซ้ำ ๆ

( select top 1 val from #t1 order by NEWID()) rnd 

โดยไม่ต้องใช้เคอร์เซอร์

แก้ไข: เอาท์พุทที่ต้องการ:

อาจจะ 1 สาย

id          val        rnd
----------- ---------- ----------
1           a          c
2           b          c
3           c          b
4           d          a

และสายที่สอง

id          val        rnd
----------- ---------- ----------
1           a          a
2           b          d
3           c          d
4           d          b

ค่าสำหรับแต่ละแถวควรเป็นค่าสุ่มที่เป็นอิสระจากแถวอื่น

นี่คือรหัสเคอร์เซอร์เวอร์ชัน:

CREATE TABLE #res ( id INT, val VARCHAR(10), rnd VARCHAR(10));

DECLARE @id INT
DECLARE @val VARCHAR(10)
DECLARE c CURSOR FOR
SELECT id, val
FROM #t1
OPEN c
FETCH NEXT FROM c INTO @id, @val
WHILE @@FETCH_STATUS = 0
BEGIN
    INSERT INTO #res
    SELECT @id, @val, ( SELECT TOP 1 val FROM #t1 ORDER BY NEWID()) rnd 
    FETCH NEXT FROM c INTO @id, @val
END
CLOSE c
DEALLOCATE c

SELECT * FROM #res

อะไรคือผลลัพธ์ที่สมบูรณ์แบบของคุณโปรด บางทีฉันหายไปบางอย่าง
GBN

ฉันกำลังเตรียมเวอร์ชันเคอร์เซอร์เพื่อให้ชัดเจน
bernd_k

rnd และ val ต่างกันเสมอในทุกแถวหรือไม่? ถ้ามันเป็น "สุ่ม" แล้วบางครั้งพวกเขาก็จะเหมือนกัน นอกจากนี้ในการโทร 2 สายที่คุณพูดถึงมันสำคัญว่า rnd ไม่มีค่าทั้งหมดในคอลัมน์หรือไม่?
GBN

มันถูกใช้เพื่อสร้างการสาธิตแบบสุ่มขนาดเล็กถึงขนาดกลางจากแหล่งข้อมูลขนาดใหญ่ อนุญาตให้เผยแพร่ซ้ำได้
bernd_k

คำตอบ:


11

แบบสอบถามย่อยจะถูกประเมินหนึ่งครั้งถ้าเป็นไปได้ ฉันจำไม่ได้ว่า "ฟีเจอร์" เรียกว่าอะไร (พับได้) ขออภัย

เช่นเดียวกับฟังก์ชั่น GETDATE และ RAND NEWID จะถูกประเมินผลทีละแถวเพราะมันอยู่ในค่าสุ่มภายในและไม่ควรสร้างค่าเดียวกันสองครั้ง

เทคนิคปกติคือการใช้ NEWID เป็นอินพุทไปยัง CHECKSUM หรือเป็นเมล็ดพันธุ์ให้กับ RAND

สำหรับค่าสุ่มต่อแถว:

SELECT
   co1l, col2,
   ABS(CHECKSUM(NEWID())) AS Random1,
   RAND(CHECKSUM(NEWID())) AS Random2
FROM
   MyTable

หากคุณต้องการคำสั่งสุ่ม:

SELECT
   co1l, col2
FROM
   MyTable
ORDER BY
   NEWID()

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

SELECT
   id, val,
   ROWNUMBER() OVER (ORDER BY id) AS id
FROM
   #t1
ORDER BY
   NEWID()

แก้ไข:

ในกรณีนี้เราสามารถระบุความต้องการเป็น:

  1. ส่งคืนค่าสุ่มใด ๆ จากชุดสำหรับแต่ละแถวในชุด
  2. ค่าสุ่มจะแตกต่างจากค่าจริงในแถวใด ๆ

สิ่งนี้แตกต่างจากที่ฉันเสนอไว้ด้านบนซึ่งเพียงสั่งซื้อแถวใหม่ในรูปแบบต่างๆ

ดังนั้นฉันจะพิจารณา CROSS ใช้ WHERE clause บังคับให้แต่ละแถวประเมินและหลีกเลี่ยงปัญหา "การพับ" และทำให้แน่ใจว่า val และ rnd แตกต่างกันเสมอ CROSS APPLY สามารถปรับขนาดได้ค่อนข้างดีเช่นกัน

SELECT
   id, val, R.rnd
FROM
   #t1 t1
   CROSS APPLY
   (SELECT TOP 1 val as rnd FROM #t1 t2 WHERE t1.val <> t2.val ORDER BY NEWID()) R
ORDER BY
   id

ใช้เป็นSQL Server 2005และบน
bernd_k

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