Por favor, veja este código:
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');
Agora, sempre que você executar isso
select *,
( select top 1 val from #t1 order by NEWID()) rnd
from #t1 order by 1;
você obterá um resultado em que todas as linhas têm o mesmo valor aleatório. por exemplo
id val rnd
----------- ---------- ----------
1 a b
2 b b
3 c b
4 d b
Eu sei como usar um cursor para fazer um loop nas linhas e obter diferentes valores aleatórios, mas isso não é de alto desempenho.
Uma solução inteligente para isso é
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
Mas eu simplifiquei a consulta. A consulta real se parece mais com
select *,
( select top 1 val from t2 where t2.x <> t1.y order by NEWID()) rnd
from t1 order by 1;
e a solução simples não se encaixa. Estou procurando uma maneira de forçar a avaliação repetida de
( select top 1 val from #t1 order by NEWID()) rnd
sem o uso de cursores.
Editar: Saída desejada:
talvez 1 chamada
id val rnd
----------- ---------- ----------
1 a c
2 b c
3 c b
4 d a
e uma segunda chamada
id val rnd
----------- ---------- ----------
1 a a
2 b d
3 c d
4 d b
O valor para cada linha apenas deve ser um valor aleatório independente das outras linhas
Aqui está a versão do cursor do código:
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
sql-server
sql-server-2005
bernd_k
fonte
fonte
Respostas:
Uma subconsulta é avaliada uma vez, se possível. Não me lembro como o "recurso" é chamado (dobrável?) Desculpe.
O mesmo se aplica às funções GETDATE e RAND. NEWID é avaliado linha por linha porque é intrinsecamente um valor aleatório e nunca deve gerar o mesmo valor duas vezes.
As técnicas comuns são usar NEWID como entrada para CHECKSUM ou como uma semente para RAND
Para valores aleatórios por linha:
Se você deseja ordem aleatória:
Se você quiser ordem aleatória com uma ordem de linha também. A ordem ActualOrder aqui é preservada, independentemente da ordem do conjunto de resultados
Editar:
Nesse caso, podemos declarar o requisito como:
Isso é diferente do que eu ofereci acima, que simplesmente reordena as linhas de várias maneiras
Então, eu consideraria CROSS APPLY. A cláusula WHERE força a avaliação linha a linha e evita o problema de "dobragem" e garante que val e rnd sejam sempre diferentes. O CROSS APPLY também pode ser dimensionado muito bem
fonte