SELECTステートメントの各行に異なるランダムな値を割り当てるにはどうすればよいですか?


11

このコードを見てください:

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

今、これを実行するたびに

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

すべての行が同じランダム値を持つ結果が得られます。例えば

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

カーソルを使用してループで行をスローし、さまざまなランダムな値を取得する方法を知っていますが、これはパフォーマンスが良くありません。

これに対する賢い解決策は

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 

しかし、クエリを簡略化しました。実際のクエリはより似ています

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

シンプルなソリューションは適合しません。私は繰り返し評価を強制する方法を探しています

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

カーソルを使用しません。

編集:必要な出力:

たぶん1コール

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

そして2回目の呼び出し

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

各行の値は、他の行から独立したランダムな値である必要があります

次に、カーソルバージョンのコードを示します。

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

あなたの完璧な出力は何ですか?多分私は何かが足りない
gbn


それで、rndとvalは常にすべての行で異なりますか?それが「ランダム」である場合、時々それらは同じでした。また、言及した2つの呼び出しでは、rndが列にすべての値を持たないことが重要ですか?
gbn 2011年

これは、実際のデータの大きなプールから小規模から中規模のランダムなデモを生成するために使用されます。はい補充は許可されています。
bernd_k

回答:


11

サブクエリは、可能であれば一度評価されます。申し訳ありませんが、この「機能」の名前(折りたたみ?)を思い出せません。

同じことがGETDATE関数とRAND関数にも当てはまります。NEWIDは本質的にランダムな値であり、同じ値を2回生成することはないため、行ごとに評価されます。

通常のテクニックは、チェックサムへの入力として、またはRANDへのシードとしてNEWIDを使用することです

行ごとにランダムな値の場合:

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

ランダムな順序が必要な場合:

SELECT
   co1l, col2
FROM
   MyTable
ORDER BY
   NEWID()

行順もランダムな順序が必要な場合。ここでのActualOrder順序は、結果セットの順序に関係なく保持されます

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

編集:

この場合、要件を次のように述べることができます。

  1. セット内の各行について、セットからランダムな値を返します
  2. ランダムな値は、任意の行の実際の値とは異なります

これは、上で提供したものとは異なり、さまざまな方法で行を並べ替えるだけです

したがって、CROSS APPLYを検討します。WHERE句は行ごとの評価を強制し、「折りたたみ」の問題回避し、valとrndが常に異なることを保証します。CROSS APPLYも非常にうまく拡張できます

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

適用は、SQL Server 2005の上位と
bernd_k

1
@bernd_k:はい、しかし2011年にSQL Server 2000ユーザーを無視することは現実的です...
gbn
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.