Veuillez regarder ce code:
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');
Maintenant, chaque fois que vous exécutez cette
select *,
( select top 1 val from #t1 order by NEWID()) rnd
from #t1 order by 1;
vous obtiendrez un résultat avec où toutes les lignes ont la même valeur aléatoire. par exemple
id val rnd
----------- ---------- ----------
1 a b
2 b b
3 c b
4 d b
Je connais un moyen d'utiliser un curseur pour lancer en boucle les lignes et obtenir différentes valeurs aléatoires, mais ce n'est pas performant.
Une solution intelligente à cela est
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
Mais j'ai simplifié la requête. La vraie requête ressemble plus à
select *,
( select top 1 val from t2 where t2.x <> t1.y order by NEWID()) rnd
from t1 order by 1;
et la solution simple ne convient pas. Je cherche un moyen de forcer une évaluation répétée de
( select top 1 val from #t1 order by NEWID()) rnd
sans l'utilisation de curseurs.
Edit: Sortie souhaitée:
peut-être 1 appel
id val rnd
----------- ---------- ----------
1 a c
2 b c
3 c b
4 d a
et un deuxième appel
id val rnd
----------- ---------- ----------
1 a a
2 b d
3 c d
4 d b
La valeur de chaque ligne doit simplement être une valeur aléatoire indépendante des autres lignes
Voici la version curseur du code:
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
la source
la source
Réponses:
Une sous-requête est évaluée une fois si possible. Je ne me souviens plus de ce que la "fonction" est appelée (pliage?)
Il en va de même pour les fonctions GETDATE et RAND. NEWID est évalué ligne par ligne car il est intrinsèquement une valeur aléatoire et ne doit jamais générer deux fois la même valeur.
Les techniques habituelles sont d'utiliser use NEWID comme entrée pour CHECKSUM ou comme graine pour RAND
Pour les valeurs aléatoires par ligne:
Si vous voulez un ordre aléatoire:
Si vous souhaitez également un ordre aléatoire avec un ordre de ligne. L'ordre Ordre réel ici est conservé quel que soit l'ordre du jeu de résultats
Éditer:
Dans ce cas, nous pouvons énoncer l'exigence comme suit:
Ceci est différent de ce que j'ai proposé ci-dessus qui réorganise simplement les lignes de diverses manières
Donc, je considérerais CROSS APPLY. La clause WHERE force l'évaluation ligne par ligne et évite le problème de "pliage" et garantit que val et rnd sont toujours différents. CROSS APPLY peut aussi très bien évoluer
la source