TSQL Selectの行ごとに乱数を生成するにはどうすればよいですか?


328

テーブルの行ごとに異なる乱数が必要です。次の一見明白なコードでは、各行に同じランダム値を使用しています。

SELECT table_name, RAND() magic_number 
FROM information_schema.tables 

これからINTまたはFLOATを取得します。ストーリーの残りの部分では、この乱数を使用して、既知の日付からランダムな日付オフセットを作成します。たとえば、開始日から1〜14日オフセットします。

これはMicrosoft SQL Server 2000用です。


4
NEWID()を使用しないこれに対する解決策はありますか?特定のシードに対して同じ乱数列を生成できるようにしたいと思います。
ロリー・マクラウド

@ロリー新しい質問として、もっと注目されるようにお願いします。(私の答えは、乱数の固定テーブルを使用することです。たとえば、この有名な乱数の標準セット:rand.org/pubs/monograph_reports/MR1418/index.html
MatthewMartin 2010


RANDは2005年に導入されましたが、この質問は2009年に尋ねられました。SQL2000を使用した組織は、SQL 2000を永久に使用できる最初のバージョンでした。
MatthewMartin 2014年

Rory MacLeod氏は、「NEWID()を使用しない解決策はありますか?特定のシードに対して同じ乱数列を生成できるようにしたいのです」と尋ねました。答えはイエスですが、少し複雑です。1. select rand()を返すビューを作成します。2.ビューから値を選択するUDFを作成します。3.データを選択する前に、rand()関数をシードします。4. selectステートメントでUDFを使用します。以下に完全な例を掲載します
Mitselplik

回答:


516

SQL Server-セットベースの乱数を見てください。これには非常に詳細な説明があります。

要約すると、次のコードは0から13までの乱数を生成し、一様な分布を示します。

ABS(CHECKSUM(NewId())) % 14

範囲を変更するには、式の最後にある数値を変更します。正の数と負の数の両方を含む範囲が必要な場合は、特に注意してください。間違えた場合は、数値0を二重にカウントする可能性があります。

部屋の数学のナッツに対する小さな警告:このコードには非常にわずかな偏りがあります。CHECKSUM()結果は、sql Intデータ型の範囲全体で均一であるか、少なくとも私の(エディター)テストが示すことができる程度に近い数値になります。ただし、CHECKSUM()がその範囲の最上位で数値を生成する場合、バイアスが発生します。最大可能整数と、その最大整数の前に目的の範囲のサイズの最後の正確な倍数(この場合は14)の間の数値を取得すると、それらの結果は、範囲の残りの部分よりも優先されます。その最後の14の倍数。

例として、Int型の範囲全体が19しかないことを想像してください。19は、保持できる最大の整数です。CHECKSUM()の結果が14〜19の場合、これらは結果0〜5に対応します。CHECKSUM()がそれらを生成する可能性が2倍であるため、これらの数値は6〜13よりも大幅に優先されます。これを視覚的に示す方が簡単です。以下は、架空の整数の範囲で考えられる結果セット全体です。

チェックサム整数:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
範囲結果:0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5

他の数値よりもいくつかの数値を生成する可能性が高いことがわかります:バイアス。ありがたいことに、Int型の実際の範囲ははるかに大きいので、ほとんどの場合、バイアスはほとんど検出されません。ただし、深刻なセキュリティコードに対してこれを行っている場合は、注意が必要です。


28
このリンクされたページには解決策がありました:ABS(CHECKSUM(NewId()))%14
MatthewMartin

7
%14は
0〜13の

7
@Dennis Palmer、1
KMを

59
これで天才的なバグを発見しました。チェックサムはintを返し、intの範囲は-2 ^ 31(-2,147,483,648)〜2 ^ 31-1(2,147,483,647)であるため、結果がちょうど-2,147,483,648の場合、abs()関数はオーバーフローエラーを返す可能性があります。 !可能性は明らかに非常に低く、約40億分の1ですが、毎日約18億行のテーブルで実行しているため、週に1回程度発生していました。修正は、ABSの前にチェックサムをbigintにキャストすることです。
EvilPuppetMaster 2016年

17
これは「正規化された分布」ではなく「均一分布」と言うべきだと思います。各数値は等しく可能性があり、ベルカーブではありません。「正規化」には特定の数学的な意味があります。
AnotherParker

95

1つのバッチで複数回呼び出されると、rand()は同じ数を返します。

シード引数としてconvert(varbinarynewid())を使用することをお勧めします。

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

newid() 同じバッチ内でも、呼び出されるたびに異なる値を返すことが保証されているため、シードとして使用すると、rand()に毎回異なる値を与えるように求められます。

1から14までのランダムな整数を取得するように編集されました。


どのようにしてguidまたはvarbinaryから数値を取得しますか?私は整数を期待していることを示すために質問を更新します。
MatthewMartin 09年

1
あなたはそれを数で乗算し、それをフロアします:)それであなたが5桁が欲しいなら、100000を掛けて、intに変換します。醜いですが、やるのに十分簡単です。
ジェレミー・スミス

1
さらなる補遺として- 最大 5桁が得られます-ゼロ詰めをしたい場合は、charデータ型を使用し、replicateを使用して最大5桁まで埋め込む必要があります。
ジェレミー・スミス

あなたの代わりに、フロアの天井機能を使用する場合は、1を追加する必要はありません
PopeDarren

これを使用しても、RAND()が常に同じ結果を返すことがあります。さらに奇妙なことに、私が使用している回数によっては、正しい動作から誤った動作にジャンプする場合があります。RANDOM INNER JOINを実装しようとしていますが、19(!!!)を超える行を要求すると、常に同じ結果が
返され

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()関数をシードしてから、選択ステートメントで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()はステートメントごとに1回だけ実行されるため、毎回同じ番号が表示されます。


最も簡単!値はより分散しているように見えますが、その真ん中の数字を使用すると、次のようになりますRIGHT(CONVERT(BIGINT, RAND(RecNo) * 1000000000000), 2) (注:RIGHT暗黙的にをに変換しているBIGINTようCHARですが、厳密に言うとCONVERT、そこに別の値があるはずです)。
Doug_Ivison 2015

4

整数ではなく、ランダムな一意の識別子である必要がない場合は、 newid()

SELECT table_name, newid() magic_number 
FROM information_schema.tables

4

行ごとにRAND()を呼び出す必要があります。ここに良い例があります

https://web.archive.org/web/20090216200320/http://dotnet.org.za/calmyourself/archive/2007/04/13/sql-rand-trap-same-value-per-row.aspx


デッドリンク:(回答に含まれる可能性のあるコピーはありますか?
jocull

彼はRAND()ビューに入れ、SELECTそのビューのaを関数に入れてから、どこからでもその関数を呼び出します。賢い。
Doug_Ivison 2015

私はリンクされた記事とまったく同じ方法で問題を解決する解決策を投稿しましたが、ここ5日前の回答としてこのブログに直接回答しました!誰も賢い私を呼ばない羨望の顔
Mitselplik

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

ここで、乱数は20から30の間になります。 round最大で小数点以下2桁になります。

負の数が必要な場合は、

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 khoury

申し訳ありませんが、@ arntには、32ビットの英数字キー(多かれ少なかれ拡張可能)を生成する2つの関数CTDE_GENERATE_32_BIT_KEYと、最初の関数を呼び出して32ビットの公開鍵を返すCTDE_GENERATE_PUBLIC_KEYと呼ばれるもう1つの関数があります。 16ビットの秘密鍵... select dbo.CTDE_GENERATE_PUBLIC_KEY()を公開鍵として呼び出すだけです背後にあるロジックは、ランダムな英数字のキーを取得するために、英数字リストから1文字を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.