TSQL Select의 각 행에 대해 난수를 어떻게 생성합니까?


328

내 테이블의 각 행마다 다른 임의의 숫자가 필요합니다. 다음의 명백한 코드는 각 행에 대해 동일한 임의의 값을 사용합니다.

SELECT table_name, RAND() magic_number 
FROM information_schema.tables 

이 중 INT 또는 FLOAT를 얻고 싶습니다. 이야기의 나머지 부분은이 난수를 사용하여 알려진 날짜에서 임의의 날짜 오프셋을 생성합니다 (예 : 시작 날짜에서 1-14 일 오프셋).

Microsoft SQL Server 2000 용입니다.


4
NEWID ()를 사용하지 않는 해결책이 있습니까? 주어진 시드에 대해 동일한 난수 시퀀스를 생성 할 수 있기를 원합니다.
Rory MacLeod

@Rory 새로운 질문으로 더 많은 주목을받을 것입니다. (제 대답은 고정 된 난수 표를 사용하는 것입니다. 예를 들어이 유명한 표준 난수 세트 : rand.org/pubs/monograph_reports/MR1418/index.html )
MatthewMartin


RAND는 2005 년에 도입 된이 질문은 2009 년에 요청되었으며,이 조직은 SQL 2000을 계속 사용하기에 충분한 첫 번째 버전이기 때문에 여전히 SQL 2000을 사용했습니다.
MatthewMartin

Rory MacLeod는 "NEWID ()를 사용하지 않는 솔루션이 있습니까? 주어진 시드에 대해 동일한 난수 시퀀스를 생성 할 수 있기를 원합니다." 대답은 그렇습니다. 그러나 약간 복잡했습니다. 1. select rand ()를 리턴하는보기를 작성하십시오. 2.보기에서 값을 선택하는 UDF를 작성하십시오. 3. 데이터를 선택하기 전에 rand () 함수를 시드하십시오. 4. select.에서 UDF를 사용하십시오. 아래에 전체 예제를 게시하겠습니다
Mitselplik

답변:


516

SQL Server-Set based random numbers 를 살펴보십시오 . 자세한 설명이 나와 있습니다.

요약하면, 다음 코드는 균일 분포로 0에서 13 사이의 난수를 생성합니다.

ABS(CHECKSUM(NewId())) % 14

범위를 변경하려면 식 끝에 숫자를 변경하십시오. 양수와 음수를 모두 포함하는 범위가 필요한 경우 각별히주의하십시오. 잘못하면 숫자 0을 두 번 계산할 수 있습니다.

방의 수학 너트에 대한 작은 경고 :이 코드에는 약간의 편견이 있습니다. CHECKSUM()sql Int 데이터 유형의 전체 범위에서 균일하거나 최소한 (편집자) 테스트가 표시 할 수있는 정도의 숫자가됩니다. 그러나 CHECKSUM ()이 해당 범위의 맨 끝에 숫자를 생성 할 때 약간의 편차가 있습니다. 가능한 최대 정수와 원하는 최대 크기 (이 경우 14)의 크기의 마지막 정확한 배수 사이의 숫자를 얻을 때마다 그 결과는 범위의 나머지 부분보다 선호됩니다. 마지막 배수는 14의 배수입니다.

예를 들어, Int 유형의 전체 범위가 19에 불과하다고 가정하십시오. 19는 보유 할 수있는 가장 큰 정수입니다. CHECKSUM ()의 결과가 14-19 인 경우 결과 0-5에 해당합니다. CHECKSUM ()이 생성 할 가능성이 두 배이기 때문에이 숫자는 6-13보다 많이 선호됩니다. 이것을 시각적으로 설명하는 것이 더 쉽습니다. 다음은 가상 정수 범위에 대한 가능한 전체 결과 집합입니다.

체크섬 정수 : 12 34 5678 9 10 11 12 1314 15 16 17 18 19
범위 결과 : 12 34 5678 9 10 11 12 1103 34 5

여기에서 다른 것보다 더 많은 숫자를 생성 할 수있는 기회가 더 많다는 것을 알 수 있습니다 : 편향. 고맙게도 Int 유형의 실제 범위는 훨씬 큽니다. 그래서 대부분의 경우 바이어스를 거의 감지 할 수 없습니다. 그러나 심각한 보안 코드를 위해이 작업을 수행하는 경우 알고 있어야합니다.


28
이 링크 된 페이지에 해결책이 있습니다 : ABS (CHECKSUM (NewId ())) % 14
MatthewMartin 2016 년

7
% 14은 0에서 13 사이의 숫자를 반환합니다.
CoderDennis 2016 년

7
@Dennis Palmer, 1
KM을

59
우리는 이것으로 천재 버그를 발견했습니다. 체크섬은 int를 반환하고 int의 범위는 -2 ^ 31 (-2,147,483,648)에서 2 ^ 31-1 (2,147,483,647)이므로 abs () 함수는 결과가 정확히 -2,147,483,648 인 경우 오버플로 오류를 반환 할 수 있습니다. ! 확률은 40 억분의 1 정도로 매우 낮지 만, 매일 ~ 1.8b 행 테이블에서 실행하고 있었기 때문에 일주일에 한 번 정도 발생했습니다! 수정은 abs 전에 체크섬을 bigint로 캐스팅하는 것입니다.
EvilPuppetMaster

17
나는 이것이 "정규 분포"가 아닌 "고른 분포"라고 말해야한다고 생각합니다. 각 숫자는 똑같이 가능하고 종 곡선이 아닙니다. "정규화 된"은 특정한 수학적 의미를 가지고 있습니다.
AnotherParker

95

단일 배치에서 여러 번 호출되면 rand ()는 동일한 숫자를 반환합니다.

시드 인수로 convert ( varbinary, newid())를 사용하는 것이 좋습니다 .

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

newid() 같은 배치 내에서도 호출 될 때마다 다른 값을 반환하도록 보장되므로 시드로 사용하면 rand ()가 매번 다른 값을 지정하도록 프롬프트합니다.

1에서 14까지 임의의 정수를 얻도록 편집되었습니다.


guid 또는 varbinary에서 숫자를 어떻게 얻습니까? 정수를 바라고 있음을 나타 내기 위해 질문을 업데이트합니다.
MatthewMartin 2016 년

1
숫자를 곱한 후 그 값을 곱하면 :) 5 자리 숫자를 원하면 100000을 곱한 다음 정수로 변환하십시오. 추악하지만 할만 큼 간단합니다.
Jeremy Smyth

1
추가로- 최대 5 자리 숫자 제공합니다 -0을 채우려면 char 데이터 유형을 사용하고 최대 5 자리를 0으로 채우려면 복제를 사용해야합니다.
Jeremy Smyth

대신 바닥의 천장 기능을 사용하는 경우에는 1을 추가 할 필요가 없습니다
PopeDarren

이것을 사용하더라도 RAND ()가 항상 동일한 결과를 제공하는 경우가 있습니다. 낯선 사람조차도 사용 횟수에 따라 올바른 동작에서 잘못된 동작으로 점프하는 경우가 있습니다. 나는 RANDOM INNER JOIN을 구현하려고 노력 중이며 19 개 이상의 행을 요청하면 항상 같은 결과를 제공하기 시작합니다.
Johannes Wentu

72
RAND(CHECKSUM(NEWID()))

위의 0과 1 사이의 (의사) 난수를 제외하고 생성합니다. 선택에 사용될 경우 각 행의 시드 값이 변경되므로 각 행에 대해 새로운 난수를 생성합니다 (단, 행당 고유 한 숫자를 생성 할 수는 없습니다).

상한을 10으로 조합 한 예 (숫자 1 ~ 10 생성) :

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

Transact-SQL 설명서 :

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

39

1000에서 9999 사이의 난수 생성 :

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

"+1"-상한값 포함 (이전 예의 경우 9999)


상한은이 방법으로 배타적이므로 최고 수를 포함하려면해야 할 일FLOOR(RAND(CHECKSUM(NEWID()))*(10000-1000)+1000)
vaindil

20

이전 질문에 대답했지만이 답변은 이전에 제공되지 않았으므로 검색 엔진을 통해이 결과를 찾는 사람에게 유용 할 것입니다.

SQL Server 2008 CRYPT_GEN_RANDOM(8)에는 CryptoAPI를 사용하여 암호화 적으로 강력한 난수를 생성하는 새로운 기능이 도입되었으며 로 반환됩니다 VARBINARY(8000). 설명서 페이지는 다음과 같습니다. https://docs.microsoft.com/en-us/sql/t-sql/functions/crypt-gen-random-transact-sql

임의의 숫자를 얻으려면 간단히 함수를 호출하여 필요한 유형으로 캐스팅하면됩니다.

select CAST(CRYPT_GEN_RANDOM(8) AS bigint)

또는 float-1과 +1 사이 를 얻으려면 다음과 같이 할 수 있습니다.

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

13

Rand () 함수는 테이블 SELECT 쿼리에 사용되는 경우 동일한 난수를 생성합니다. Rand 함수에 시드를 사용하는 경우에도 동일하게 적용됩니다. 다른 방법은 다음을 사용하는 것입니다.

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

여기 에서 정보를 얻었습니다 . 문제를 잘 설명합니다.


5

RAND 함수에 시드로 전달할 수있는 각 행에 정수 값이 있습니까?

1과 14 사이의 정수를 얻으려면 이것이 효과가 있다고 생각합니다.

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

이것은 이론적으로 작동하지만 실제로 RAND(<seed>)는의 작은 변경 사항에 대해 무작위로 나타나지 않는 것으로 나타났습니다 <seed>. 예를 들어 빠른 테스트를 수행했습니다. <seed>184380, 184383, 184386으로 설정했으며 해당 RAND(<seed>)값은 0.14912, 0.14917, 0.14923입니다.
ImaginaryHuman072889

아마, 좀 더 "겉으로는"임의의 결과를 얻을 수 같은 것을 시도 :RAND(<seed>)*100000) - FLOOR(RAND(<seed>)*100000)
ImaginaryHuman072889

5

시드가 매번 "동일한"임의의 데이터를 생성하도록 시드를 보존해야하는 경우 다음을 수행 할 수 있습니다.

1. select 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.보기에서 값을 선택하는 UDF를 작성하십시오.

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. 데이터를 선택하기 전에 rand () 함수를 시드 한 후 select 문에서 UDF를 사용하십시오.

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

RAND (seedInt)에서 시드 값을 사용해보십시오. RAND ()는 명령문 당 한 번만 실행되므로 매번 같은 숫자가 표시됩니다.


가장 간단합니다! 값이 보인다 비록 많은 더 좋아 그 중간에서 숫자를 사용하여, 분산 RIGHT(CONVERT(BIGINT, RAND(RecNo) * 1000000000000), 2) (참고 : 제가 보는 RIGHT변환 암시 적 BIGINT으로 CHAR, 그러나 엄격한 것으로, 다른이있을 것이다 CONVERT거기에).
Doug_Ivison

4

정수가 아닌 임의의 고유 식별자 인 경우 사용할 수 있습니다 newid()

SELECT table_name, newid() magic_number 
FROM information_schema.tables

4

Dead link :( 답변에 포함 할 수있는 사본이 있습니까?
jocull

그는 RAND()뷰에 넣고 SELECT해당 뷰 중 하나를 함수에 넣은 다음 어디에서나 함수를 호출합니다. 영리한.
Doug_Ivison

링크 된 기사와 동일한 방식으로 문제를 해결하는 솔루션을 게시했지만이 블로그에서는 5 게시물 전에 답변으로 직접 게시했습니다! 아무도 나를 영리한 부러워하는 얼굴 hehe
Mitselplik

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

여기서 난수는 20에서 30 사이 round입니다. 소수점 이하 두 자리를 최대 값으로 지정합니다.

음수를 원하면 함께 할 수 있습니다.

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

그러면 최소값은 -60이되고 최대 값은 -50이됩니다.


3

다음과 같이 쉽습니다.

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

그리고 이것은 0-99 사이의 임의의 숫자를 테이블에 넣습니다.

CREATE TABLE R
(
    Number int
)

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

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

SELECT * FROM R

2

내가 선택한 "답변"에서 때때로 문제는 분포가 항상 고르지 않다는 것입니다. 많은 행 사이에 임의의 1-14 분포가 매우 균일 해야하는 경우 다음과 같이 할 수 있습니다 (데이터베이스에 511 개의 테이블이 있으므로 작동합니다. 임의의 수 범위보다 행이 적을 경우 작동하지 않습니다) 잘):

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

이런 종류의 숫자는 숫자를 순서대로 유지하고 다른 열을 무작위 화한다는 점에서 일반적인 무작위 솔루션과 반대입니다.

데이터베이스에 511 개의 테이블이 있습니다 (Information_schema에서 선택한 b / c에만 해당됨). 이전 쿼리를 가져 와서 임시 테이블 #X에 넣은 다음 결과 데이터에서이 쿼리를 실행합니다.

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

이 결과를 얻었고 내 임의의 숫자가 많은 행에 균등하게 분포되어 있음을 보여줍니다.

여기에 이미지 설명을 입력하십시오


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

항상 나를 위해 일했다



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;

내가 잘 설명하지 않은 경우 죄송 @arnt
ichak 쿠리

죄송합니다 @arnt, 여기 에 32 비트 영숫자 키를 생성하는 두 가지 함수 CTDE_GENERATE_32_BIT_KEY (더 많거나 적을 수 있음)와 첫 번째 함수를 호출하고 32 비트의 공개 키를 반환하는 CTDE_GENERATE_PUBLIC_KEY 라는 다른 함수가 있습니다. 16 비트의 개인 키 ... 공개 키로 select dbo.CTDE_GENERATE_PUBLIC_KEY () 를 호출 하면됩니다. 논리는 우리가 임의의 영숫자 키를 얻기 위해 영숫자 문자 목록에서 하나의 문자를 32 번 선택하고 함께 연결한다는 것입니다. 연구 후.
ichak khoury

좋은. 그 설명은 훨씬 더 나은 답변을 만듭니다. (누군가 그것을 삭제 한 것으로 표시했습니다. 나는 그것을 열어두고 그 의견을 남겨두기로 투표했습니다.)
arnt

0

이 시도:

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

어디 a낮은 수이고 b상단 번호


1
질문에 대답하면서 더 명확하게 노력할 수 있습니까?
Yunus Temurlenk

0
Update my_table set my_field = CEILING((RAND(CAST(NEWID() AS varbinary)) * 10))

1에서 10 사이의 숫자입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.