web-dev-qa-db-fra.com

Comment puis-je attribuer différentes valeurs aléatoires à chaque ligne dans une instruction SELECT?

S'il vous plaît 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 cela

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 boucle jeter les lignes et obtenir différentes valeurs aléatoires, mais ce n'est pas performant.

Une solution intelligente à ceci 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 devrait juste être une valeur aléatoire indépendante des autres rangées

Voici la version du 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
11
bernd_k

Une sous-requête est évaluée une fois si possible. Je ne me souviens pas de ce qu'on appelle la "fonctionnalité" (pliante?) Désolé.

La même chose s'applique aux fonctions GetDate et Rand. NeufID est évalué une ligne à la ligne, car elle est intrinsèquement une valeur aléatoire et ne doit jamais générer la même valeur deux fois.

Les techniques habituelles sont à utiliser utilisez NeufID comme entrée de la somme de contrôle ou comme une graine à rand

Pour les valeurs aléatoires par ligne:

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

Si vous voulez ordre aléatoire:

SELECT
   co1l, col2
FROM
   MyTable
ORDER BY
   NEWID()

Si vous voulez un ordre aléatoire avec une commande de ligne aussi. Commander actuel ici est préservé quelle que soit l'ordre des Resultset

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

Éditer:

Dans ce cas, nous pouvons énoncer l'exigence comme suit:

  1. renvoyer toute valeur aléatoire du jeu pour chaque ligne de l'ensemble
  2. la valeur aléatoire sera différente de la valeur réelle de n'importe quelle ligne

Ceci est différent de ce que j'ai proposé ci-dessus, qui recommande simplement des lignes de différentes manières

Donc, je considérerais croix s'appliquer. La clause oblige la clause à l'évaluation par la ligne d'évaluation et évite le problème "pliable" et garantit que Val et RND sont toujours différents. Cross Appliquer peut aussi bien augmenter

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
11
gbn