Bản ghi ngẫu nhiên từ một bảng cơ sở dữ liệu (T-SQL)


85

Có cách nào ngắn gọn để lấy bản ghi ngẫu nhiên từ bảng máy chủ sql không?

Tôi muốn ngẫu nhiên hóa dữ liệu thử nghiệm đơn vị của mình, vì vậy tôi đang tìm một cách đơn giản để chọn một id ngẫu nhiên từ một bảng. Trong tiếng Anh, lựa chọn sẽ là "Chọn một id từ bảng trong đó id là một số ngẫu nhiên giữa id thấp nhất trong bảng và id cao nhất trong bảng."

Tôi không thể tìm ra cách để làm điều đó mà không phải chạy truy vấn, kiểm tra giá trị null, sau đó chạy lại nếu null.

Ý tưởng?


có một vài phương pháp ở đây brettb.com/SQL_Help_Random_Numbers.asp
Mesh

2
Bạn có chắc chắn muốn thực hiện phương pháp này không? Dữ liệu kiểm tra đơn vị không được ngẫu nhiên - trên thực tế, bạn phải được đảm bảo nhận được cùng một kết quả bất kể bạn thực hiện kiểm thử đơn vị bao nhiêu lần. Có dữ liệu ngẫu nhiên có thể vi phạm nguyên tắc cơ bản này của thử nghiệm đơn vị.
kiềm chế

Liên kết ở trên từ @Mesh không còn hoạt động.
Robert Sievers

Câu trả lời:


145

Có cách nào ngắn gọn để lấy bản ghi ngẫu nhiên từ bảng máy chủ sql không?

Đúng

SELECT TOP 1 * FROM table ORDER BY NEWID()

Giải trình

A NEWID()được tạo cho mỗi hàng và bảng sau đó được sắp xếp theo nó. Bản ghi đầu tiên được trả về (tức là bản ghi có GUID "thấp nhất").

Ghi chú

  1. GUID được tạo dưới dạng số giả ngẫu nhiên kể từ phiên bản bốn:

    UUID phiên bản 4 được dùng để tạo UUID từ các số thực sự ngẫu nhiên hoặc giả ngẫu nhiên.

    Thuật toán như sau:

    • Đặt hai bit quan trọng nhất (bit 6 và 7) của clock_seq_hi_and_reserved lần lượt là 0 và một.
    • Đặt bốn bit quan trọng nhất (bit 12 đến 15) của trường time_hi_and_version thành số phiên bản 4 bit từ Phần 4.1.3.
    • Đặt tất cả các bit khác thành các giá trị được chọn ngẫu nhiên (hoặc giả ngẫu nhiên).

    - Không gian tên URN ID nhận dạng duy nhất (UUID) - RFC 4122

  2. Giải pháp thay thế SELECT TOP 1 * FROM table ORDER BY RAND()sẽ không hoạt động như người ta nghĩ. RAND()trả về một giá trị duy nhất cho mỗi truy vấn, do đó tất cả các hàng sẽ chia sẻ cùng một giá trị.

  3. Trong khi các giá trị GUID là giả ngẫu nhiên, bạn sẽ cần một PRNG tốt hơn cho các ứng dụng đòi hỏi nhiều hơn.

  4. Hiệu suất điển hình là dưới 10 giây cho khoảng 1.000.000 hàng - tất nhiên tùy thuộc vào hệ thống. Lưu ý rằng không thể đạt được một chỉ mục, do đó hiệu suất sẽ tương đối hạn chế.


Chính xác những gì tôi đang tìm kiếm. Tôi có cảm giác nó đơn giản hơn tôi đang làm.
Jeremy

1
Bạn đang giả định rằng NEWID tạo ra các giá trị giả ngẫu nhiên. Có một cơ hội tốt là nó sẽ tạo ra các giá trị tuần tự. NEWID chỉ tạo ra các giá trị duy nhất. Tuy nhiên, RAND tạo ra các giá trị giả ngẫu nhiên.
Skizz

Tôi đang chạy nó trên một bảng được lập chỉ mục nhiều với 1.671.145 hàng và mất 7 giây để trả lại. Bảng này cũng khá tối ưu - nó gần như là trung tâm của cơ sở dữ liệu của chúng tôi nên nó được chăm sóc cẩn thận.
Tom Ritter

@ ÂviewAnew. 1,6 triệu hàng và 7 giây trên một lựa chọn không (và không thể) đạt được chỉ mục không phải là xấu.
Sklivvz 10/10/08

7
@Skizz, rand không hoạt động như vậy. Một giá trị ngẫu nhiên DUY NHẤT được tạo trước khi CHỌN. Vì vậy, nếu bạn thử "CHỌN ĐẦU 10 RAND () ...", bạn luôn nhận được cùng một giá trị
Sklivvz 10/10/08

27

Trên các bảng lớn hơn, bạn cũng có thể sử dụng tính năng TABLESAMPLEnày để tránh quét toàn bộ bảng.

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

Các ORDER BY NEWIDvẫn còn cần thiết để tránh hàng chỉ trở lại xuất hiện đầu tiên trên trang dữ liệu.

Số để sử dụng cần được chọn cẩn thận cho kích thước và định nghĩa của bảng và bạn có thể cân nhắc thử lại logic nếu không có hàng nào được trả về. Các phép toán đằng sau điều này và tại sao kỹ thuật này không phù hợp với các bảng nhỏ được thảo luận ở đây


Tôi tìm thấy điều này trên trang web của Microsoft: Bạn có thể sử dụng TABLESAMPLE để nhanh chóng trả về một mẫu từ một bảng lớn khi một trong các điều kiện sau là đúng: Mẫu không nhất thiết phải là mẫu thực sự ngẫu nhiên ở cấp độ các hàng riêng lẻ. Các hàng trên các trang riêng lẻ của bảng không tương quan với các hàng khác trên cùng một trang.
Mark Entingh

1
@MarkEntingh - Trong trường hợp TOP 1này, không quan trọng nếu các hàng trên cùng một trang có tương quan hay không. Bạn chỉ chọn một trong số chúng.
Martin Smith

9

Ngoài ra, hãy thử phương pháp của bạn để nhận một Id ngẫu nhiên giữa MIN (Id) và MAX (Id) và sau đó

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

Nó sẽ luôn giúp bạn có một hàng.


2
-1, Điều này sẽ chỉ hoạt động khi không có ID bị thiếu giữa tối thiểu và tối đa. Nếu một ID bị xóa thì cùng một ID đó được tạo bởi hàm ngẫu nhiên, bạn sẽ không nhận lại được bản ghi nào.
Neil N

6
@Neil, không thực sự - nó sẽ đưa bạn đến hàng đầu tiên có Id lớn hơn số ngẫu nhiên nếu thiếu Id. Vấn đề ở đây là xác suất mỗi hàng ra không phải là hằng số. Nhưng sau đó một lần nữa điều này đủ trong hầu hết các trường hợp.
Sklivvz

1
+1. Đối với thử nghiệm đơn vị sẽ đạt được các giá trị khác nhau đủ tốt - nếu bạn yêu cầu một ngẫu nhiên thực sự, thì đây là một cái gì đó khác. Nhưng trong bối cảnh OP, nó phải đủ tốt.
TomTom

7

Nếu bạn muốn chọn dữ liệu lớn, cách tốt nhất mà tôi biết là:

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

Nguồn: MSDN


Tôi không chắc nhưng tôi nghĩ rằng việc sử dụng RAND () thay vì NEWID () để tạo các số thực sự ngẫu nhiên có thể tốt hơn vì những bất lợi của việc sử dụng NEWID () trong quá trình chọn.
QMaster

Tôi thử sử dụng phương pháp này với số lượng bản ghi chính xác thay vì cơ sở phần trăm, tôi đã làm điều đó với mở rộng phạm vi lựa chọn và giới hạn với TOP n, có gợi ý nào không?
QMaster

Tôi đã tìm thấy một vấn đề khác với trường hợp này, Nếu bạn sử dụng nhóm theo nhóm, bạn sẽ luôn nhận được cùng một thứ tự của các hàng được chọn ngẫu nhiên, vì vậy có vẻ như trong các bảng nhỏ, cách tiếp cận @skilvvz là thích hợp nhất.
QMaster 22/09/2016

0

Tôi đang tìm cách cải thiện các phương pháp tôi đã thử và xem qua bài đăng này. Tôi nhận ra nó cũ nhưng phương pháp này không được liệt kê. Tôi đang tạo và áp dụng dữ liệu thử nghiệm; điều này hiển thị phương thức cho "địa chỉ" trong SP được gọi với @st (trạng thái hai ký tự)

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

Nếu bạn thực sự muốn một mẫu ngẫu nhiên của các hàng riêng lẻ, hãy sửa đổi truy vấn của bạn để lọc ra các hàng một cách ngẫu nhiên, thay vì sử dụng TABLESAMPLE. Ví dụ: truy vấn sau sử dụng hàm NEWID để trả về khoảng một phần trăm các hàng của bảng Sales.SalesOrderDetail:

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

Cột SalesOrderID được bao gồm trong biểu thức CHECKSUM để NEWID () đánh giá một lần mỗi hàng để đạt được lấy mẫu trên cơ sở mỗi hàng. Biểu thức CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) cho giá trị thực ngẫu nhiên trong khoảng từ 0 đến 1. "

Nguồn: http://technet.microsoft.com/en-us/library/ms189108(v=sql.105).aspx

Điều này được giải thích thêm bên dưới:

Cái này hoạt động ra sao? Hãy tách mệnh đề WHERE và giải thích nó.

Hàm CHECKSUM đang tính tổng tổng cho các mục trong danh sách. Người ta vẫn tranh cãi về việc liệu SalesOrderID có được yêu cầu hay không, vì NEWID () là một hàm trả về một GUID ngẫu nhiên mới, do đó, nhân một số ngẫu nhiên với một hằng số sẽ dẫn đến ngẫu nhiên trong mọi trường hợp. Thật vậy, việc loại trừ SalesOrderID dường như không có gì khác biệt. Nếu bạn là một nhà thống kê nhạy bén và có thể biện minh cho việc đưa vào điều này, vui lòng sử dụng phần nhận xét bên dưới và cho tôi biết tại sao tôi sai!

Hàm CHECKSUM trả về một VARBINARY. Thực hiện phép toán theo chiều bit AND với 0x7fffffff, tương đương với (111111111 ...) trong hệ nhị phân, mang lại giá trị thập phân là biểu diễn của một chuỗi ngẫu nhiên gồm 0 và 1s. Chia cho 0x7fffffff đồng thời chuẩn hóa hiệu quả con số thập phân này thành một con số từ 0 đến 1. Sau đó, để quyết định xem mỗi hàng có xứng đáng được đưa vào tập kết quả cuối cùng hay không, ngưỡng 1 / x được sử dụng (trong trường hợp này là 0,01) trong đó x là phần trăm dữ liệu cần lấy làm mẫu.

Nguồn: https://www.mssqltips.com/sqlservertip/3157/dierence-ways-to-get-random-data-for-sql-server-data-sampling

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.