2009-02-27 5 views
2

Nous avons une application Oracle qui utilise un modèle standard pour remplir les clés de substitution. Nous avons une série de lignes extrinsèques (qui ont des valeurs spécifiques pour les clés de substitution) et d'autres lignes qui ont des valeurs intrinsèques. Nous utilisons les éléments suivants extrait de déclencheur Oracle pour déterminer ce qu'il faut faire avec la clé Surrogate sur insert:Générateurs de séquence dans T-SQL

IF :NEW.SurrogateKey IS NULL THEN 

SELECT SurrogateKey_SEQ.NEXTVAL INTO :NEW.SurrogateKey FROM DUAL; 

END IF; 

Si la clé de remplacement fourni est nul alors obtenir une valeur de la séquence désignée, passe bien la clé de substitution fournie par à la rangée.

Je n'arrive pas à trouver un moyen facile de faire cela est T-SQL. Il existe toutes sortes d'approches, mais aucune n'utilise la notion de générateur de séquence comme Oracle et d'autres DB conformes à SQL-92.

Quelqu'un sait-il d'une manière très efficace de le faire dans SQL Server T-SQL? En passant, nous utilisons SQL Server 2008 si cela peut vous aider.

+0

Voir http://dba.stackexchange.com/questions/3307/emulate-a-tsql-sequence-via-a -stored-procedure – gbn

Répondre

2

Vous pouvez regarder IDENTITY. Cela vous donne une colonne pour laquelle la valeur sera déterminée lorsque vous insérez la ligne.

Cela peut signifier que vous devez insérer la ligne et déterminer la valeur par la suite, en utilisant SCOPE_IDENTITY().

Il y a aussi un article sur la simulation des séquences Oracle dans SQL Server ici: http://www.sqlmag.com/Articles/ArticleID/46900/46900.html?Ad=1

+2

Merci Matthieu, je connais la propriété IDENTITY pour la table, mais j'essaie de l'éviter car j'aimerais conserver la flexibilité que nous avons actuellement. En outre, l'article que vous avez désigné utilise des tables et des recherches supplémentaires que j'essaie d'éviter. – PaoloFCantoni

+0

Vous devez également créer une table par 'séquence'. –

0

L'identité est une approche, même si elle génère des identificateurs uniques à un niveau par table.

Une autre approche consiste à utiliser des identifiants uniques, en particulier en utilisant NewSequantialID(), ce qui fait que l'identifiant généré est toujours plus grand que le dernier. Le problème avec cette approche est que vous n'avez plus affaire à des entiers. La méthode la plus proche pour émuler la méthode Oracle est d'avoir une table séparée avec un champ de compteur, puis d'écrire une fonction définie par l'utilisateur qui interroge ce champ, l'incrémente et renvoie la valeur.

+0

Merci Conrad, nous avons considéré cela; mais il y a une exigence particulière que les clés de substitution soient des entiers. Si je comprends bien, les identifiants unque sont des GUID ou des UUID. – PaoloFCantoni

0

Voici une façon de le faire en utilisant une table pour stocker votre dernier numéro de séquence. Le proc stocké est très simple, la plupart des choses sont là parce que je suis fainéant et n'aime pas les surprises si j'oublie quelque chose alors ... voici:

----- Créer la valeur de la séquence table.

SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

CREATE TABLE [dbo].[SequenceTbl] 
(
    [CurrentValue] [bigint] 
) ON [PRIMARY] 

GO 

----------------- Créer la procédure stockée

SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

CREATE procedure [dbo].[sp_NextInSequence](@SkipCount BigInt = 1) 
AS 

BEGIN 

    BEGIN TRANSACTION 

    DECLARE @NextInSequence BigInt; 

    IF NOT EXISTS 
    (
     SELECT 
     CurrentValue 
     FROM 
     SequenceTbl 
    ) 

    INSERT INTO SequenceTbl (CurrentValue) VALUES (0); 

    SELECT TOP 1 
     @NextInSequence = ISNULL(CurrentValue, 0) + 1 
    FROM 
     SequenceTbl WITH (HoldLock); 

    UPDATE SequenceTbl WITH (UPDLOCK) 
     SET CurrentValue = @NextInSequence + (@SkipCount - 1); 

    COMMIT TRANSACTION 

    RETURN @NextInSequence 
END; 
GO 

-------- Utilisation la procédure stockée dans Sql Manager pour récupérer une valeur de test.

declare @NextInSequence BigInt 

exec @NextInSequence = sp_NextInSequence; 

--exec @NextInSequence = sp_NextInSequence <skipcount>; 

select NextInSequence = @NextInSequence; 

----- Affiche la valeur de la table en cours.

select * from SequenceTbl; 

L'astérisque remarquera qu'il existe un paramètre (optionnel) pour le proc mémorisé. Cela permet à l'appelant de réserver un bloc d'ID dans l'instance où l'appelant a plus d'un enregistrement nécessitant un ID unique. En utilisant le SkipCount, l'appelant n'a besoin de faire qu'un seul appel, quel que soit son ID. L'intégralité du bloc "IF EXISTS ... INSERT INTO ..." peut être supprimé si vous pensez à insérer un enregistrement lors de la création de la table. Si vous pensez également à insérer cet enregistrement avec une valeur (votre valeur initiale - un nombre qui ne sera jamais utilisé comme ID), vous pouvez également supprimer la partie ISNULL (...) de la sélection et utiliser simplement CurrentValue + 1. Maintenant, avant que quelqu'un ne fasse un commentaire, s'il vous plaît noter que je suis un ingénieur logiciel, pas un dba! Donc, toute critique constructive concernant l'utilisation de "Top 1", "With (HoldLock)" et "With (UPDLock)" est la bienvenue. Je ne sais pas à quel point cela va évoluer mais cela fonctionne bien pour moi jusqu'ici ...

+0

Critique constructive :) ne jouez pas avec ces verrous, vous serez blessé ... Pour éviter les blocages, je recommande plutôt d'utiliser une table avec l'identité (1,1), la première opération sera un insert (insert SequenceTbl par défaut valeurs), vous obtiendrez votre scope_identity(), seconde opération une suppression de la ligne égale à ce nombre. Cela peut être sérialisé avec le verrouillage de niveau ligne. – Rbjz