Làm cách nào tôi có thể gán các giá trị ngẫu nhiên khác nhau cho mỗi hàng trong câu lệnh CHỌN?


11

Vui lòng xem mã này:

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');

Bây giờ, bất cứ khi nào bạn thực hiện điều này

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

bạn sẽ nhận được kết quả với tất cả các hàng có cùng giá trị ngẫu nhiên. ví dụ

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

Tôi biết một cách sử dụng một con trỏ để lặp các hàng và nhận các giá trị ngẫu nhiên khác nhau, nhưng đó không phải là hiệu suất.

Một giải pháp thông minh cho vấn đề này là

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 

Nhưng tôi đã đơn giản hóa truy vấn. Các truy vấn thực sự trông giống như

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

và giải pháp đơn giản không phù hợp. Tôi đang tìm cách để buộc đánh giá lặp đi lặp lại

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

không sử dụng con trỏ

Chỉnh sửa: Đầu ra mong muốn:

có lẽ 1 cuộc gọi

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

và một cuộc gọi thứ hai

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

Giá trị cho mỗi hàng chỉ là một giá trị ngẫu nhiên độc lập với các hàng khác

Đây là phiên bản con trỏ của mã:

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

Điều gì sẽ là đầu ra hoàn hảo của bạn xin vui lòng? có lẽ tôi đang thiếu một cái gì đó
gbn

Tôi đang chuẩn bị một phiên bản con trỏ để làm cho nó rõ ràng
bernd_k

Vì vậy, rnd và val luôn khác nhau ở mỗi hàng? Nếu đó là "ngẫu nhiên", thì đôi khi chúng sẽ giống nhau. Ngoài ra, trong 2 cuộc gọi của bạn được đề cập, vấn đề là rnd không có tất cả các giá trị trên cột?
gbn

Nó được sử dụng để tạo ra một trình diễn ngẫu nhiên nhỏ đến trung bình từ một nhóm lớn dữ liệu thực. Có sửa chữa được cho phép.
bernd_k

Câu trả lời:


11

Một truy vấn con được đánh giá một lần nếu có thể. Tôi không thể nhớ lại "tính năng" được gọi là gì (gấp?) Xin lỗi.

Điều tương tự cũng áp dụng cho các hàm GETDATE và RAND. NEWID được đánh giá từng hàng bởi vì về bản chất nó là một giá trị ngẫu nhiên và không bao giờ nên tạo ra cùng một giá trị hai lần.

Các kỹ thuật thông thường là sử dụng NEWID làm đầu vào CHECKSUM hoặc làm hạt giống cho RAND

Đối với các giá trị ngẫu nhiên trên mỗi hàng:

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

Nếu bạn muốn đặt hàng ngẫu nhiên:

SELECT
   co1l, col2
FROM
   MyTable
ORDER BY
   NEWID()

Nếu bạn muốn thứ tự ngẫu nhiên với một thứ tự hàng quá. Thứ tự thực tế ở đây được giữ nguyên bất kể thứ tự của tập kết quả

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

Biên tập:

Trong trường hợp này, chúng tôi có thể nêu yêu cầu như:

  1. trả về bất kỳ giá trị ngẫu nhiên nào từ tập hợp cho mỗi hàng trong tập hợp
  2. giá trị ngẫu nhiên sẽ khác với giá trị thực tế ở bất kỳ hàng nào

Điều này khác với những gì tôi đã cung cấp ở trên, đơn giản là sắp xếp lại các hàng theo nhiều cách khác nhau

Vì vậy, tôi sẽ xem xét CROSS ỨNG DỤNG. Mệnh đề WHERE buộc hàng theo đánh giá hàng và tránh vấn đề "gấp" đảm bảo rằng val và rnd luôn khác nhau. ỨNG DỤNG CROSS cũng có thể mở rộng khá tốt

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

ÁP DỤNG là SQL Server 2005 và trên
bernd_k

1
@bernd_k: vâng, nhưng thực tế là bỏ qua người dùng SQL Server 2000 vào năm 2011 ...
gbn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.