Làm cách nào để tạo số ngẫu nhiên cho mỗi hàng trong TSQL Chọn?


328

Tôi cần một số ngẫu nhiên khác nhau cho mỗi hàng trong bảng của tôi. Mã dường như rõ ràng sau đây sử dụng cùng một giá trị ngẫu nhiên cho mỗi hàng.

SELECT table_name, RAND() magic_number 
FROM information_schema.tables 

Tôi muốn có được một INT hoặc FLOAT từ điều này. Phần còn lại của câu chuyện là tôi sẽ sử dụng số ngẫu nhiên này để tạo ra một ngày bù ngẫu nhiên từ một ngày đã biết, ví dụ: 1-14 ngày bù từ ngày bắt đầu.

Đây là cho Microsoft SQL Server 2000.


4
Có giải pháp nào cho việc này không sử dụng NEWID () không? Tôi muốn có thể tạo cùng một chuỗi các số ngẫu nhiên cho một hạt giống nhất định.
Rory MacLeod

@Rory Hỏi rằng như câu hỏi mới, nó sẽ được chú ý nhiều hơn. (Câu trả lời của tôi sẽ được sử dụng bảng cố định của số ngẫu nhiên, ví dụ Ví dụ bộ này nổi tiếng tiêu chuẩn của số ngẫu nhiên:. Rand.org/pubs/monograph_reports/MR1418/index.html )
MatthewMartin


RAND được giới thiệu vào năm 2005, câu hỏi này đã được hỏi vào năm 2009, tổ chức nào vẫn sử dụng SQL 2000 vì đó là phiên bản đầu tiên đủ tốt để sử dụng mãi mãi.
MatthewMartin

Rory MacLeod hỏi: "Có giải pháp nào cho việc này không sử dụng NEWID () không? Tôi muốn có thể tạo cùng một chuỗi các số ngẫu nhiên cho một hạt giống nhất định." Câu trả lời là có, nhưng nó hơi phức tạp. 1. Tạo chế độ xem trả về select rand () 2. Tạo UDF chọn giá trị từ chế độ xem. 3. Trước khi chọn dữ liệu của bạn, hãy khởi tạo hàm rand (). 4. Sử dụng UDF trong câu lệnh chọn của bạn. Tôi sẽ đăng một ví dụ đầy đủ bên dưới
Mitselplik

Câu trả lời:


516

Hãy xem SQL Server - Đặt các số ngẫu nhiên dựa trên có giải thích rất chi tiết.

Tóm lại, đoạn mã sau tạo một số ngẫu nhiên trong khoảng từ 0 đến 13, bao gồm một phân phối thống nhất:

ABS(CHECKSUM(NewId())) % 14

Để thay đổi phạm vi của bạn, chỉ cần thay đổi số ở cuối biểu thức. Cẩn thận hơn nếu bạn cần một phạm vi bao gồm cả số dương và số âm. Nếu bạn làm sai, có thể nhân đôi số 0.

Một cảnh báo nhỏ cho các hạt toán học trong phòng: có một sai lệch rất nhỏ trong mã này. CHECKSUM()dẫn đến các số thống nhất trong toàn bộ phạm vi của kiểu dữ liệu Int sql, hoặc ít nhất là gần như thử nghiệm của tôi (trình soạn thảo) có thể hiển thị. Tuy nhiên, sẽ có một số sai lệch khi CHECKSUM () tạo ra một số ở đầu cuối của phạm vi đó. Bất cứ khi nào bạn nhận được một số giữa số nguyên tối đa có thể và bội số chính xác cuối cùng của kích thước của phạm vi mong muốn của bạn (14 trong trường hợp này) trước số nguyên tối đa đó, các kết quả đó được ưu tiên trên phần còn lại của phạm vi không thể được tạo từ bội số cuối cùng của 14.

Ví dụ, hãy tưởng tượng toàn bộ phạm vi của kiểu Int chỉ 19. 19 là số nguyên lớn nhất có thể bạn có thể giữ. Khi CHECKSUM () kết quả trong 14-19, những kết quả này tương ứng với kết quả 0-5. Những con số này sẽ được rất nhiều ưu ái hơn 6-13, vì checksum () là gấp đôi khả năng để tạo ra chúng. Thật dễ dàng để chứng minh điều này một cách trực quan. Dưới đây là toàn bộ tập hợp kết quả có thể có cho phạm vi số nguyên tưởng tượng của chúng tôi:

Số nguyên kiểm tra: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Phạm vi Kết quả: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5

Bạn có thể thấy ở đây có nhiều cơ hội để tạo ra một số con số hơn những con số khác: thiên vị. Rất may, phạm vi thực tế của loại Int lớn hơn nhiều ... đến mức trong hầu hết các trường hợp, độ lệch gần như không thể phát hiện được. Tuy nhiên, đó là điều cần lưu ý nếu bạn thấy mình làm điều này vì mã bảo mật nghiêm trọng.


28
Trang được liên kết này có giải pháp: ABS (CHECKSUM (NewId ()))% 14
MatthewMartin

7
% 14 sẽ trả về các số từ 0 đến 13
CoderDennis

7
@Dennis Palmer, chỉ cần thêm 1
KM.

59
Chúng tôi chỉ phát hiện ra một lỗi thiên tài với điều này. Vì tổng kiểm tra trả về một int và phạm vi của một int là -2 ^ 31 (-2,147,483,648) đến 2 ^ 31-1 (2.147,483,647), hàm abs () có thể trả về lỗi tràn nếu kết quả xảy ra chính xác là -2,147,483,648 ! Cơ hội rõ ràng là rất thấp, khoảng 1 trong 4 tỷ, tuy nhiên chúng tôi đã điều hành nó trên một bảng hàng ~ 1,8b mỗi ngày, vì vậy nó đã xảy ra khoảng một lần một tuần! Khắc phục là bỏ tổng kiểm tra vào bigint trước abs.
EvilPuppetMaster

17
Tôi nghĩ rằng điều này sẽ nói "phân phối đồng đều" chứ không phải "phân phối chuẩn hóa" - mỗi số có khả năng như nhau, đó không phải là đường cong hình chuông. "Bình thường hóa" có ý nghĩa toán học cụ thể.
AnotherParker

95

Khi được gọi nhiều lần trong một lô, rand () trả về cùng một số.

Tôi khuyên bạn nên sử dụng convert ( varbinary, newid()) làm đối số hạt giống:

SELECT table_name, 1.0 + floor(14 * RAND(convert(varbinary, newid()))) magic_number 
FROM information_schema.tables

newid() được đảm bảo trả về một giá trị khác nhau mỗi lần nó được gọi, ngay cả trong cùng một lô, do đó, sử dụng nó làm hạt giống sẽ nhắc rand () đưa ra một giá trị khác nhau mỗi lần.

Chỉnh sửa để có được một số nguyên ngẫu nhiên từ 1 đến 14.


Làm thế nào để bạn có được một số trong một hướng dẫn hoặc varbinary? Tôi sẽ cập nhật câu hỏi để cho biết tôi đang hy vọng vào một số nguyên.
MatthewMartin

1
Bạn nhân nó với một số và xếp tầng đó :) vì vậy nếu bạn muốn có năm chữ số, nhân với 100000 và chuyển đổi thành số nguyên. Xấu xí, nhưng đủ đơn giản để làm.
Jeremy Smyth

1
Là một phụ lục bổ sung - sẽ cung cấp cho bạn tối đa năm chữ số - nếu bạn muốn sử dụng số 0, bạn sẽ phải sử dụng kiểu dữ liệu char và sử dụng bản sao để không có tối đa 5 chữ số.
Jeremy Smyth

Nếu bạn sử dụng chức năng trần thay vì sàn, bạn không phải thêm 1.
PopeDarren

Ngay cả khi tôi sử dụng điều này, có những lúc RAND () luôn mang lại cho tôi kết quả tương tự. Thậm chí xa lạ, có những lúc nó nhảy từ một hành vi đúng sang một hành vi không chính xác tùy thuộc vào số lần tôi đang sử dụng nó. Tôi đang cố gắng thực hiện RANDOM INNER THAM GIA và nếu tôi yêu cầu hơn 19 hàng (!!!), nó sẽ bắt đầu mang lại cho tôi kết quả tương tự ...
Julian Wentu

72
RAND(CHECKSUM(NEWID()))

Ở trên sẽ tạo ra một số ngẫu nhiên (giả) trong khoảng từ 0 đến 1, độc quyền. Nếu được sử dụng trong một lựa chọn, vì giá trị hạt giống thay đổi cho mỗi hàng, nó sẽ tạo ra một số ngẫu nhiên mới cho mỗi hàng (tuy nhiên nó không được đảm bảo để tạo một số duy nhất cho mỗi hàng).

Ví dụ khi kết hợp với giới hạn trên là 10 (tạo số 1 - 10):

CAST(RAND(CHECKSUM(NEWID())) * 10 as INT) + 1

Tài liệu giao dịch-SQL:

  1. CAST(): https://docs.microsoft.com/en-us/sql/t-sql/fifts/cast-and-convert-transact-sql
  2. RAND(): http://msdn.microsoft.com/en-us/l Library / ms177610.aspx
  3. CHECKSUM(): http://msdn.microsoft.com/en-us/l Library / ms189788.aspx
  4. NEWID(): https://docs.microsoft.com/en-us/sql/t-sql/fifts/newid-transact-sql

39

Tạo số ngẫu nhiên trong khoảng từ 1000 đến 9999:

FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000)

"+1" - để bao gồm các giá trị giới hạn trên (9999 cho ví dụ trước)


Giới hạn trên là độc quyền với phương pháp này, vì vậy nếu bạn muốn bao gồm số cao nhất bạn cần thực hiệnFLOOR(RAND(CHECKSUM(NEWID()))*(10000-1000)+1000)
vaindil

20

Trả lời câu hỏi cũ, nhưng câu trả lời này chưa được cung cấp trước đây và hy vọng điều này sẽ hữu ích cho ai đó tìm thấy kết quả này thông qua một công cụ tìm kiếm.

Với SQL Server 2008, một chức năng mới đã được giới thiệu, CRYPT_GEN_RANDOM(8)sử dụng CryptoAPI để tạo ra một số ngẫu nhiên mạnh về mật mã, được trả về là VARBINARY(8000). Đây là trang tài liệu: https://docs.microsoft.com/en-us/sql/t-sql/fifts/crypt-gen-random-transact-sql

Vì vậy, để có được một số ngẫu nhiên, bạn chỉ cần gọi hàm và chuyển nó thành loại cần thiết:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint)

hoặc để có được floattừ -1 đến +1, bạn có thể làm một cái gì đó như thế này:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint) % 1000000000 / 1000000000.0

13

Hàm Rand () sẽ tạo cùng một số ngẫu nhiên, nếu được sử dụng trong truy vấn CHỌN bảng. Áp dụng tương tự nếu bạn sử dụng hạt giống cho hàm Rand. Một cách khác để làm điều đó, là sử dụng điều này:

SELECT ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]

Có thông tin từ đây , giải thích vấn đề rất tốt.


5

Bạn có một giá trị số nguyên trong mỗi hàng mà bạn có thể chuyển dưới dạng hạt giống cho hàm RAND không?

Để có được một số nguyên từ 1 đến 14 tôi tin rằng điều này sẽ hoạt động:

FLOOR( RAND(<yourseed>) * 14) + 1

Điều này hoạt động trên lý thuyết, nhưng trong thực tế tôi đã thấy rằng RAND(<seed>)dường như không phải là rất ngẫu nhiên cho những thay đổi nhỏ trong <seed>. Ví dụ, một bài kiểm tra nhanh tôi đã làm: Tôi cho <seed>là 184380, 184383, 184386 và các RAND(<seed>)giá trị tương ứng là: 0.14912, 0.14917, 0.14923.
ImaginaryHuman072889

Có thể để có thêm một số kết quả ngẫu nhiên "dường như", hãy thử một số thứ như:RAND(<seed>)*100000) - FLOOR(RAND(<seed>)*100000)
ImaginaryHuman072889

5

Nếu bạn cần bảo tồn hạt giống của mình để nó tạo ra dữ liệu ngẫu nhiên "giống nhau" mỗi lần, bạn có thể làm như sau:

1. Tạo chế độ xem trả về chọn rand ()

if object_id('cr_sample_randView') is not null
begin
    drop view cr_sample_randView
end
go

create view cr_sample_randView
as
select rand() as random_number
go

2. Tạo UDF chọn giá trị từ chế độ xem.

if object_id('cr_sample_fnPerRowRand') is not null
begin
    drop function cr_sample_fnPerRowRand
end
go

create function cr_sample_fnPerRowRand()
returns float
as
begin
    declare @returnValue float
    select @returnValue = random_number from cr_sample_randView
    return @returnValue
end
go

3. Trước khi chọn dữ liệu của bạn, hãy chọn hàm rand () và sau đó sử dụng UDF trong câu lệnh chọn của bạn.

select rand(200);   -- see the rand() function
with cte(id) as
(select row_number() over(order by object_id) from sys.all_objects)
select 
    id,
    dbo.cr_sample_fnPerRowRand()
from cte
where id <= 1000    -- limit the results to 1000 random numbers

4

thử sử dụng giá trị hạt giống trong RAND (seedInt). RAND () sẽ chỉ thực hiện một lần cho mỗi câu lệnh, đó là lý do tại sao bạn nhìn thấy cùng một số mỗi lần.


Đơn giản nhất! Mặc dù các giá trị dường như phân tán hơn rất nhiều, bằng cách sử dụng các chữ số ở giữa đó, như RIGHT(CONVERT(BIGINT, RAND(RecNo) * 1000000000000), 2) (lưu ý: Tôi đang nhìn thấy RIGHTchuyển đổi hoàn toàn BIGINTsang CHAR, nhưng để nghiêm ngặt, bạn sẽ có một chữ số khác CONVERTở đó).
Doug_Ivison

4

Nếu bạn không cần nó là một số nguyên, nhưng bất kỳ định danh duy nhất ngẫu nhiên nào, bạn có thể sử dụng newid()

SELECT table_name, newid() magic_number 
FROM information_schema.tables

4

Liên kết chết :( Có bản sao nào có thể được đưa vào câu trả lời không?
vui đùa vào

Anh ta đưa RAND()vào một khung nhìn, đặt một SELECTkhung nhìn đó vào một hàm và sau đó gọi hàm đó từ bất cứ đâu. Tài giỏi.
Doug_Ivison

Tôi đã đăng một giải pháp giải quyết vấn đề theo cách chính xác giống như trong bài viết được liên kết, nhưng ở đây trong blog này trực tiếp như một câu trả lời năm bài viết trước đây! Không ai gọi tôi là khuôn mặt đáng ghen tị hehe
Mitselplik

4
select round(rand(checksum(newid()))*(10)+20,2)

Ở đây, số ngẫu nhiên sẽ nằm trong khoảng từ 20 đến 30. roundsẽ cho hai vị trí thập phân tối đa.

Nếu bạn muốn số âm bạn có thể làm điều đó với

select round(rand(checksum(newid()))*(10)-60,2)

Khi đó giá trị tối thiểu sẽ là -60 và tối đa sẽ là -50.


3

Nó dễ như:

DECLARE @rv FLOAT;
SELECT @rv = rand();

Và điều này sẽ đặt một số ngẫu nhiên trong khoảng 0-99 vào một bảng:

CREATE TABLE R
(
    Number int
)

DECLARE @rv FLOAT;
SELECT @rv = rand();

INSERT INTO dbo.R
(Number)
    values((@rv * 100));

SELECT * FROM R

2

Vấn đề đôi khi tôi gặp phải với "Trả lời" được chọn là phân phối không phải lúc nào cũng đồng đều. Nếu bạn cần phân phối ngẫu nhiên 1 - 14 trong số rất nhiều hàng, bạn có thể làm một cái gì đó như thế này (cơ sở dữ liệu của tôi có 511 bảng, vì vậy điều này hoạt động. Nếu bạn có ít hàng hơn bạn thực hiện nhịp số ngẫu nhiên, điều này không hoạt động tốt):

SELECT table_name, ntile(14) over(order by newId()) randomNumber 
FROM information_schema.tables

Kiểu này ngược lại với các giải pháp ngẫu nhiên thông thường theo nghĩa là nó giữ các số được sắp xếp theo thứ tự và ngẫu nhiên hóa cột khác.

Hãy nhớ rằng, tôi có 511 bảng trong cơ sở dữ liệu của mình (chỉ phù hợp với b / c chúng tôi đang chọn từ information_schema). Nếu tôi lấy truy vấn trước đó và đặt nó vào bảng tạm thời #X, rồi chạy truy vấn này trên dữ liệu kết quả:

select randomNumber, count(*) ct from #X
group by randomNumber

Tôi nhận được kết quả này, cho tôi thấy rằng số ngẫu nhiên của tôi RẤT phân bổ đều giữa nhiều hàng:

nhập mô tả hình ảnh ở đây


2
select ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) as [Randomizer]

đã luôn làm việc cho tôi



1
    DROP VIEW IF EXISTS vwGetNewNumber;
    GO
    Create View vwGetNewNumber
    as
    Select CAST(RAND(CHECKSUM(NEWID())) * 62 as INT) + 1 as NextID,
    'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'as alpha_num;

    ---------------CTDE_GENERATE_PUBLIC_KEY -----------------
    DROP FUNCTION IF EXISTS CTDE_GENERATE_PUBLIC_KEY;  
    GO
    create function CTDE_GENERATE_PUBLIC_KEY()
    RETURNS NVARCHAR(32)
    AS 
    BEGIN
        DECLARE @private_key NVARCHAR(32);
        set @private_key = dbo.CTDE_GENERATE_32_BIT_KEY();
        return @private_key;
    END;
    go

---------------CTDE_GENERATE_32_BIT_KEY -----------------
DROP FUNCTION IF EXISTS CTDE_GENERATE_32_BIT_KEY;  
GO
CREATE function CTDE_GENERATE_32_BIT_KEY()
RETURNS NVARCHAR(32)
AS 
BEGIN
    DECLARE @public_key NVARCHAR(32);
    DECLARE @alpha_num NVARCHAR(62);
    DECLARE @start_index INT = 0;
    DECLARE @i INT = 0;
    select top 1 @alpha_num = alpha_num from vwGetNewNumber;
        WHILE @i < 32
        BEGIN
          select top 1 @start_index = NextID from vwGetNewNumber;
          set @public_key = concat (substring(@alpha_num,@start_index,1),@public_key);
          set @i = @i + 1;
        END;
    return @public_key;
END;
    select dbo.CTDE_GENERATE_PUBLIC_KEY() public_key;

xin lỗi @arnt nếu tôi không giải thích rõ,
ichak khoury

xin lỗi @arnt, ở đây chúng tôi có hai hàm CTDE_GENERATE_32_BIT_KEY tạo khóa chữ và số 32 bit (có thể được mở rộng thành nhiều hơn hoặc ít hơn) và một hàm khác gọi là CTDE_GENERATE_PUBLIC_KEY gọi lại hàm đầu tiên của bạn khóa riêng 16 bit ... bạn chỉ cần gọi select dbo.CTDE_GENERATE_PUBLIC_KEY () làm khóa chung; logic đằng sau là chúng tôi chọn một ký tự từ danh sách ký tự chữ và số 32 lần và ghép chúng lại với nhau để lấy khóa chữ và số ngẫu nhiên. sau khi nghiên cứu.
ichak khoury

Đẹp. Lời giải thích đó làm cho nó một câu trả lời tốt hơn nhiều. (Ai đó đã gắn cờ để xóa; Tôi đã bỏ phiếu để nó mở và để lại nhận xét đó cho bạn.)
arnt

0

Thử cái này:

SELECT RAND(convert(varbinary, newid()))*(b-a)+a magic_number 

Trong trường hợp alà số thấp hơn và blà số trên


1
Bạn có thể cố gắng rõ ràng hơn trong khi trả lời một câu hỏi?
Yunus Temurlenk

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.