web-dev-qa-db-fra.com

SQL Server 2005 ROW_NUMBER () sans ORDER BY

J'essaie d'insérer d'une table dans une autre en utilisant

DECLARE @IDOffset int;
SELECT @IDOffset = MAX(ISNULL(ID,0)) FROM TargetTable

INSERT INTO TargetTable(ID, FIELD)
SELECT [Increment] + @IDOffset ,FeildValue
FROM SourceTable
WHERE [somecondition]

TargetTable.ID n'est pas une colonne d'identité, c'est pourquoi je dois trouver un moyen de l'incrémenter automatiquement moi-même.

Je sais que je peux utiliser un curseur ou créer une variable de table avec une colonne d'identité et un champ FieldValue, remplissez-le, puis utilisez-le dans mon insert into...select, mais ce n'est pas très efficace. J'ai essayé d'utiliser la fonction ROW_NUMBER pour incrémenter, mais je n'ai vraiment pas de champ ORDER BY légitime dans le SourceTable que je puisse utiliser, et je voudrais conserver l'ordre d'origine du SourceTable (si possible).

Quelqu'un peut-il suggérer quelque chose?

29
Fragilerus

Vous pouvez éviter de spécifier un ordre explicite comme suit:

INSERT dbo.TargetTable (ID, FIELD)
SELECT
   Row_Number() OVER (ORDER BY (SELECT 1))
      + Coalesce(
         (SELECT Max(ID) FROM dbo.TargetTable WITH (TABLOCKX, HOLDLOCK)),
         0
      ),
   FieldValue
FROM dbo.SourceTable
WHERE {somecondition};

Cependant, veuillez noter que c'est simplement un moyen d'éviter de spécifier une commande et ne garantit PAS que toute commande de données d'origine sera conservée. Il existe d'autres facteurs pouvant entraîner le classement du résultat, tels qu'un ORDER BY dans la requête externe. Pour bien comprendre cela, il faut comprendre que le concept "non ordonné (d'une manière particulière)" n'est pas la même chose que "conserver l'ordre d'origine" (qui IS ordonné d'une manière particulière!) . Je crois que du point de vue de la base de données relationnelle pure, ce dernier concept n'existe pas, par définition (bien qu'il puisse y avoir implémentations de base de données qui violent cela, SQL Server n'est pas l'un d'eux).

La raison des conseils de verrouillage est d'empêcher le cas où certains autres processus insèrent en utilisant la valeur que vous prévoyez d'utiliser, entre les parties de l'exécution de la requête.

Remarque: de nombreuses personnes utilisent (SELECT NULL) pour contourner la restriction "aucune constante autorisée dans la clause ORDER BY d'une fonction de fenêtrage". Pour une raison quelconque, je préfère 1 sur NULL.

Aussi: je pense qu'une colonne d'identité est de loin supérieure et devrait être utilisée à la place. Il n'est pas bon pour la concurrence de verrouiller exclusivement des tables entières. Euphémisme.

76
ErikE

Vous pouvez ignorer l'ordre en utilisant order by (select null) comme ceci:

declare @IDOffset int;
select  @IDOffset = max(isnull(ID, 0)) from TargetTable

insert  into TargetTable(ID, FIELD)
select  row_number() over (order by (select null)) + @IDOffset, FeildValue
  from  SourceTable
 where  [somecondition]
4
Mohammad Anini