2009-09-23 8 views
7

J'essaie d'insérer de manière aléatoire des valeurs d'une liste de valeurs prédéfinies dans une table pour les tester. J'ai essayé d'utiliser la solution trouvée sur cette question StackOverflow:Comment puis-je insérer des valeurs aléatoires dans une table SQL Server?

stackoverflow.com/.../update-sql-table-with-random-value-from-other-table

Quand je j'ai essayé cela, toutes mes valeurs « aléatoires » qui sont insérés sont exactement les mêmes pour tous les 3000 dossiers. Lorsque j'exécute la partie de la requête qui sélectionne réellement la ligne aléatoire, elle sélectionne un enregistrement aléatoire chaque fois que je l'exécute à la main, donc je sais que la requête fonctionne. Mes meilleures suppositions quant à ce qui se passe sont:

  • SQL Server optimise la SELECT en quelque sorte, ne permettant pas la sous-requête à évaluer plus d'une fois
  • La graine de valeur aléatoire est la même sur tous les enregistrements des mises à jour de la requête

Je suis bloqué sur ce que mes options sont. Est-ce que je fais quelque chose de mal, ou y a-t-il une autre façon de le faire?

Ce code J'utilise:

DECLARE @randomStuff TABLE ([id] INT, [val] VARCHAR(100)) 

INSERT INTO @randomStuff ([id], [val]) 
VALUES (1, 'Test Value 1') 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (2, 'Test Value 2') 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (3, 'Test Value 3') 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (4, 'Test Value 4') 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (5, 'Test Value 5') 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (6, null) 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (7, null) 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (8, null) 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (9, null) 
INSERT INTO @randomStuff ([id], [val]) 
VALUES (10, null) 

UPDATE MyTable 
SET MyColumn = (SELECT TOP 1 [val] FROM @randomStuff ORDER BY NEWID()) 
+0

Cette question/réponse peut être utile: http://stackoverflow.com/a/9039661/47226 –

Répondre

14

Lorsque le moteur de recherche voit ...

(SELECT TOP 1 [val] FROM @randomStuff ORDER BY NEWID()) 

... il est tout comme, « Ooooh, un scalaire sous-requête cachable, je Je vais mettre ça en cache!

Vous avez besoin de tromper le moteur de recherche en lui faisant croire qu'il n'est pas disponible. Le answer de jfar était proche, mais le moteur de recherche était assez intelligent pour voir la tautalogie de MyTable.MyColumn = MyTable.MyColumn, mais il n'est pas assez intelligent pour voir à travers cela.

UPDATE MyTable 
    SET MyColumn = (SELECT TOP 1 val 
        FROM @randomStuff r 
          INNER JOIN MyTable _MT 
            ON M.Id = _MT.Id 
        ORDER BY NEWID()) 
FROM MyTable M 

En apportant la table externe (MT) dans la sous-requête, le moteur de recherche prend sous-requête devra être réévalué. Tout fonctionne vraiment, mais je suis allé avec la clé primaire (supposée) de MyTable.Id car il serait indexé et ajouterait très peu de frais généraux.

Un curseur serait probablement aussi rapide, mais certainement pas aussi amusant.

+0

OK, je ne me souviens pas si vous pouvez faire l'INNER JOIN comme ça dans SQL Server 2000, mais il y a un moyen de contourner cela, que j'ai utilisé tout le temps avant de partir en 2005. Cela faisait trop longtemps que je m'en souvenais , bien que. Mais cela devrait fonctionner en 2005 et plus tard très bien. –

+1

Cela a fonctionné très bien. Je vous remercie! –

+2

+1 c'est fantastique, plus il y a une petite faute de frappe, 'ON MT.Id = _MT.Id' devrait être 'ON M.Id = _MT.Id' – Rippo

0

Je n'ai pas le temps de vérifier ce moment, mais mon instinct me dit que si vous deviez créer une fonction sur le serveur pour obtenir la valeur aléatoire qu'elle ne l'optimiserait pas.

alors vous avez

UPDATE MyTable 
Set MyColumn = dbo.RANDOM_VALUE() 
0

Il n'y a pas d'optimisation qui se passe ici.

En utilisant une sous-requête qui sélectionne une seule valeur, il n'y a rien à optimiser.

Vous pouvez aussi essayer de mettre une colonne de la table à jour dans le select et voir si cela change quelque chose. Cela peut déclencher une évaluation pour chaque ligne de MyTable

UPDATE MyTable 
SET MyColumn = (SELECT TOP 1 [val] FROM @randomStuff ORDER BY NEWID() 
    WHERE MyTable.MyColumn = MyTable.MyColumn) 
+1

Je viens de l'essayer. N'a eu aucune chance de changer les résultats. –

2

utiliser une jointure croisée pour générer des données aléatoires

+0

Avez-vous un exemple que je pourrais utiliser? Je ne connais pas l'idée des jointures croisées. –

0

je suis venu avec une solution qui est un peu un hack et très inefficace (10 ~ secondes à mettre à jour 3000 enregistrements). Parce que cela est utilisé pour générer des données de test, je ne dois pas être préoccupé par la vitesse cependant.

Dans cette solution, parcourez chaque ligne de la table et mettez à jour les valeurs une ligne à la fois. Cela semble fonctionner:

DECLARE @rows INT 
DECLARE @currentRow INT 

SELECT @rows = COUNT(*) FROM dbo.MyTable 
SET @currentRow = 1 

WHILE @currentRow < @rows 
BEGIN 

UPDATE MyTable 
SET MyColumn = (SELECT TOP 1 [val] FROM @randomStuff ORDER BY NEWID()) 
WHERE MyPrimaryKey = (SELECT b.MyPrimaryKey 
FROM(SELECT a.MyPrimaryKey, ROW_NUMBER() OVER (ORDER BY MyPrimaryKey) AS rownumber 
     FROM MyTable a) AS b 
WHERE @currentRow = b.rownumber 
) 

SET @currentRow = @currentRow + 1 
END 
1

J'ai eu un jeu avec ceci, et ai trouvé une manière plutôt hacky de le faire avec l'utilisation d'une variable de table intermédiaire.

Une fois @randomStuff est mis en place, nous le faisons (note dans mon cas, @MyTable est une variable de table, ajuster en conséquence pour votre table normale):

DECLARE @randomMappings TABLE (id INT, val VARCHAR(100), sorter UNIQUEIDENTIFIER) 

INSERT INTO @randomMappings 
SELECT M.id, val, NEWID() AS sort 
FROM @MyTable AS M 
CROSS JOIN @randomstuff 

donc à ce stade, nous avons un table intermédiaire avec toutes les combinaisons de (identifiant mytable, valeur aléatoire) et une valeur de tri aléatoire pour chaque rangée spécifique à cette combinaison.Puis

DELETE others FROM @randomMappings AS others 
INNER JOIN @randomMappings AS lower 
ON (lower.id = others.id) AND (lower.sorter < others.sorter) 

C'est un vieux truc qui supprime toutes les lignes pour un MyTable.id donné sauf celui avec la valeur de tri inférieure - se joindre à la table pour lui-même où la valeur est plus petite et supprimer tout où ces une jointure a réussi. Cela laisse juste derrière la plus basse valeur. Ainsi, pour chaque MyTable.id, nous avons juste une valeur (aléatoire) gauche .. Ensuite, nous suffit de le brancher de nouveau dans la table:

UPDATE @MyTable 
SET MyColumn = random.val 
FROM @MyTable m, @randomMappings AS random 
WHERE (random.id = m.id) 

Et vous avez terminé!

Je dit était aki ...

Questions connexes