2010-12-19 9 views
0

J'ai une question sur la raison pour laquelle la sortie de ces deux requêtes diffère. Je me serais attendu à ce qu'ils travaillent de la même manière.SqlServer Génération de données aléatoires Observation

Requête 1:

declare @cache table(originalValue nvarchar(255), obfuscateValue nvarchar(255)); 

declare @table1 table(c char(1)); 
declare @i1 int; 
set @i1 = ASCII('0'); 

while @i1 <= ASCII('9') 
begin 
    insert into @table1 (c) 
    select (CHAR(@i1))  

    set @i1 = @i1 +1; 
end 


insert into @cache (originalValue, obfuscateValue) 
select [firstname], 
     (select top 1 c from @table1 order by NEWID()) + 
     (select top 1 c from @table1 order by NEWID()) 
from Customer 
where [firstname] is not null 

select * from @cache; 

Requête 2:

declare @cache table(originalValue nvarchar(255), obfuscateValue nvarchar(255)); 

declare @table1 table(c char(1)); 
declare @i1 int; 
set @i1 = ASCII('0'); 

while @i1 <= ASCII('9') 
begin 
    insert into @table1 (c) 
    select (CHAR(@i1))  

    set @i1 = @i1 +1; 
end 


insert into @cache (originalValue) 
select [firstname] 
from Customer 
where [firstname] is not null 

update c 
set c.obfuscateValue = t.Value 
from @cache c 
join 
(
    select originalValue, 
    (  
     (select top 1 c from @table1 order by NEWID()) + 
     (select top 1 c from @table1 order by NEWID()) 
    ) as Value 
    from @cache 
) t on t.originalValue = c.originalValue 

select * from @cache; 

Ils devraient faire les mêmes, mais les premiers retours de la requête les résultats suivants:

Jonathon 73 
Everett 73 
Janet 73 
Andy 73 
Shauna 73 

et deuxième :Comme vous l'avez remarqué, la deuxième colonne du deuxième résultat a des valeurs différentes, alors que les mêmes premières valeurs.

Il ressemble à la première requête est appelée une seule fois la

(select top 1 c from @table1 order by NEWID()) + 
     (select top 1 c from @table1 order by NEWID()) 

.

Quelqu'un peut-il expliquer ce mystère?

+0

Probablement cela explique quelque chose http://stackoverflow.com/questions/1468159/how-can-i-insert-random-values-into-a-sql-server-table –

+0

Essayez-vous simplement de générer un nombre entre 00 et 99? Ou autre chose? – gbn

+0

Non, c'est un exemple. Cela devrait fonctionner pour n'importe quelle stratégie, par exemple 000-999999. –

Répondre

0

Je pense que les valeurs aléatoires peuvent être générées d'une autre manière.

Voici comment généré [a-zA-Z] {} 3,6

declare @min int, @max int; 
declare @alpha varchar(max) 

set @min = 3; 
set @max = 6; 
set @alpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 

declare @cache table(originalValue nvarchar(255), obfuscateValue nvarchar(255)); 

insert into @cache (originalValue, obfuscateValue) 
select [firstname], LEFT(t.Value, case when t.maxLen < @min then @min else t.maxLen end) 
from Customer 
join 
(
    select ABS(CHECKSUM(NEWID()))%@max + 1 as maxLen, 
      SUBSTRING(@alpha, ABS(CHECKSUM(NEWID()))%LEN(@alpha) + 1, 1) + 
      SUBSTRING(@alpha, ABS(CHECKSUM(NEWID()))%LEN(@alpha) + 1, 1) + 
      SUBSTRING(@alpha, ABS(CHECKSUM(NEWID()))%LEN(@alpha) + 1, 1) + 
      SUBSTRING(@alpha, ABS(CHECKSUM(NEWID()))%LEN(@alpha) + 1, 1) + 
      SUBSTRING(@alpha, ABS(CHECKSUM(NEWID()))%LEN(@alpha) + 1, 1) + 
      SUBSTRING(@alpha, ABS(CHECKSUM(NEWID()))%LEN(@alpha) + 1, 1) as Value 
)t on t.Value is not null 
where [firstname] is not null 

select * from @cache; 
0

Une ligne?

SELECT 
    RIGHT(--number of zeros to match expected max length. Or use REPLICATE. 
     '000000' + CAST(
      --The 2 newid() expression means we'll get a larger number 
      --less chance of using leading static zeroes 
      CAST(CHECKSUM(NEWD_ID()) as bigint) * CAST(CHECKSUM(NEWD_ID()) as bigint) 
      as varchar(30)) 
     --The 3 gives us the desired mask. Currently 3 digits. 
     , 3) 
+0

Oui, mais je pense que ma solution est meilleure pour un ensemble d'éléments connus. –

+0

@denis_n: à vous de choisir, mais il fait des choses différentes à ce que vous avez demandé dans la question ... – gbn

0

Vous avez raison de supposer que la première requête exécute une seule fois le 'select top'. Le comportement est dû à la façon dont l'optimiseur a choisi d'optimiser la requête. Il a été décidé que les sous-requêtes (les requêtes de sélection les plus élevées) étant autonomes et n'étant pas corrélées avec la requête de sélection externe, elles utilisent un opérateur Tablespool (Lazy Spool) dans le plan d'exécution. Cela provoque la sélection de la valeur supérieure à placer dans le tempdb pour la réutilisation. Étant donné que l'optimiseur choisit d'utiliser un opérateur Boucles imbriquées pour rassembler toutes les données, aucune reliure n'est nécessaire, la valeur spoule est utilisée au lieu de réappliquer les requêtes pour chaque ligne externe d'entrée.

Lors de la deuxième requête, l'optimiseur a choisi de ne pas utiliser un opérateur Tablespool (je crois que la table d'entrée provient de tempdb). Les sous-requêtes select top sont donc réappliquées pour chaque ligne d'entrée de la table temporaire.

Si nécessaire, vous pouvez utiliser des indicateurs de table/requête si vous souhaitez forcer le plan d'exécution à fonctionner comme vous le souhaitez.

+0

Je n'ai jamais utilisé de conseils de table/requête, peut-être que vous fournissez un exemple? –

Questions connexes