2009-09-16 9 views
3

OK, j'ai une table sans clé naturelle, seulement une colonne d'identité entière comme sa clé primaire. Je voudrais insérer et récupérer la valeur d'identité, mais également utiliser un déclencheur pour m'assurer que certains champs sont toujours définis. À l'origine, la conception devait utiliser à la place des déclencheurs d'insertion, mais cela casse l'identité scope_identity. La clause de sortie de l'instruction insert est également rompue par le paramètre insert au lieu de insert. Alors, je suis venu avec un autre plan et je voudrais savoir s'il y a quelque chose de toute évidence mal avec ce que je compte faire:SCOPE_IDENTITY Et au lieu de Insert Trigger contourner

commencent exemple artificiel:

CREATE TABLE [dbo].[TestData] (
    [TestId] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL, 
    [Name] [nchar](10) NOT NULL) 

    CREATE TABLE [dbo].[TestDataModInfo](
    [TestId] [int] PRIMARY KEY NOT NULL, 
    [RowCreateDate] [datetime] NOT NULL) 

    ALTER TABLE [dbo].[TestDataModInfo] WITH CHECK ADD CONSTRAINT 
    [FK_TestDataModInfo_TestData] FOREIGN KEY([TestId]) 
    REFERENCES [dbo].[TestData] ([TestId]) ON DELETE CASCADE 

CREATE TRIGGER [dbo].[TestData$AfterInsert] 
    ON [dbo].[TestData] 
    AFTER INSERT 
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from 
    -- interfering with SELECT statements. 
    SET NOCOUNT ON; 
    INSERT INTO [dbo].[TestDataModInfo] 
      ([TestId], 
      [RowCreateDate]) 
     SELECT 
      [TestId], 
      current_timestamp 
     FROM inserted 

    -- Insert statements for trigger here 

END 

Fin exemple artificiel.

Non, je ne le fais pas pour un petit champ de date - c'est juste un exemple.

Les champs que je veux m'assurer sont définis ont été déplacés vers une table séparée (dans TestDataModInfo) et le déclencheur s'assure qu'il est mis à jour. Cela fonctionne, il me permet d'utiliser scope_identity() après les insertions, et semble être sûr (si mon déclencheur après échoue, mon insertion échoue). Est-ce une mauvaise conception, et si oui, pourquoi?

+0

Merci Joel - n'a pas vu ma propre frappe bâclée. –

+0

Je suppose que vous validez des champs dans des déclencheurs plutôt que des contraintes en raison de données héritées? – Maslow

+0

@Maslow, les triggers devraient être le moyen le plus simple et le plus sûr de s'assurer que certains champs sont toujours ou jamais mis à jour (updateDate, createdate). Cela garantit que vous ne pouvez pas enfreindre ces règles, sauf si vous avez également la permission de désactiver les déclencheurs. Juste une couche supplémentaire de protection. –

Répondre

2

Comme vous l'avez mentionné, SCOPE_IDENTITY est conçu pour cette situation. Il n'est pas affecté par le code de déclenchement AFTER, contrairement à @@ IDENTITY.

En dehors de l'utilisation des process stockés, ceci est OK.

J'utilise les déclencheurs AFTER pour l'audit car ils sont pratiques ... c'est-à-dire, écrire dans une autre table de mon trigger.

Edit: SCOPE_IDENTITY and parallelism in SQL Server 2005 cam have a problem

+1

Vous ne pouvez pas vraiment dire que scope_identity n'est pas affecté par le code de déclenchement - il renvoie null si vous utilisez un déclencheur 'au lieu d'insert'. –

+0

@Peter, vrai, tapait trop vite. – gbn

0

Avez-vous essayé en utilisant une sortie pour récupérer la valeur de la place?

+0

La sortie renvoie également la valeur null lors de l'utilisation à la place des déclencheurs d'insertion. –

+0

bien alors je suppose que vous n'avez vraiment pas d'autre choix que d'utiliser un déclencheur après. – HLGEM

0

Vous pouvez utiliser un déclencheur INSTEAD OF très bien, par la gâchette capture de la juste valeur après l'insertion à la table principale, puis usurper la Scope_Identity() en @@Identity à la fin de la détente:

-- Inside of trigger 
SET NOCOUNT ON; 
INSERT dbo.YourTable VALUES(blah, blah, blah); 
SET @YourTableID = Scope_Identity(); 

-- ... other DML that inserts to another identity-bearing table 

-- Last statement in trigger 
SELECT YourTableID INTO #Trash FROM dbo.YourTable WHERE YourTableID = @YourTableID; 

Ou, voici une déclaration finale alternative qui n'utilise pas de lectures, mais peut entraîner des problèmes d'autorisation si l'utilisateur n'exécute pas de droits (bien qu'il existe des solutions à ce problème).

SET @SQL = 
    'SELECT identity(smallint, ' + Str(@YourTableID) + ', 1) YourTableID INTO #Trash'; 
EXEC (@SQL); 

Notez que Scope_Identity() peut retourner NULL sur une table avec un déclencheur INSTEAD OF sur elle, dans certains cas, même si vous utilisez cette méthode d'usurpation d'identité. Mais vous pouvez au moins obtenir la valeur en utilisant @@Identity. Cela peut faire que les projets ADP MS Access recommencent à fonctionner après la rupture car vous placez un déclencheur sur une table à laquelle l'extrémité avant s'insère.

De plus, sachez que tout parallélisme du tout peut faire @@Identity et Scope_Identity() retour valeurs incorrectes si utilisent OPTION (MAXDOP 1) ou TOP 1 ou une seule rangée VALUES clause pour vaincre ce problème.