如何在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

第二个电话

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

我正在准备一个游标版本以使其清楚
bernd_k 2011年

那么rnd和val在每一行中总是不同的吗?如果是“随机的”,那么有时候它们也会一样。另外,在您提到的2个调用中,rnd在该列上没有所有值是否重要?
gbn

它用于从大量真实数据中生成中小型随机演示。是的,可以补充。
bernd_k 2011年

Answers:


11

如果可能,子查询将被评估一次。我不记得所谓的“功能”是什么(折叠?)。

GETDATE和RAND函数也是如此。NEWID逐行求值,因为它本质上是一个随机值,并且永远不会两次生成相同的值。

通常的技术是使用NEWID作为CHECKSUM的输入或RAND的种子。

对于每行随机值:

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. 随机值将与任何行中的实际值不同

这与我上面提供的内容不同,后者仅以各种方式对行进行重新排序

因此,我将考虑交叉申请。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

APPLY是SQL Server 2005及更高版本
bernd_k,2011年

1
@bernd_k:是的,但是在2011年忽略SQL Server 2000用户应该是现实的……
gbn
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.