2009-06-19 4 views
1

Ich arbeite an einem Auslöser, der seine Daten in einer anderen Tabelle neu einfügen muss.Triggers auf TSQL, das eine Nicht-Identitätstabelle einfügt

Die Zieltabelle hat einen Primärschlüssel INT NOT NULL ohne Identität, so dass ich die Wahl zwischen 2:

  1. den maximalen Berechnen und von hier einfügen.
  2. Maximalwert aus einer Sequenztabelle nehmen.

Ich verwende immer eine Tabellenvariable mit Identität erstellen und Verschiebung von dem zuvor berechneten Wert einfügen.

CREATE TRIGGER trg 
    ON [dbo].[table] 
    AFTER INSERT 
AS 
BEGIN 
    SET NOCOUNT ON; 

    DECLARE @t TABLE (sec INT IDENTITY(1, 1), id INT) 
    DECLARE @ini INT 

    SELECT @ini = ISNULL(MAX(id), 0) FROM tableDest 
    -- SELECT @ini = value FROM sequencesTable WHERE seqId = 987 

    INSERT INTO @t (id) SELECT id FROM inserted 

    INSERT INTO tableDest 
     (id, field1, field2) 
    SELECT @ini + t.sec, field1, field2 
    FROM @t t 
     JOIN inserted ON t.id = inserted.id 


    -- SELECT @ini = @ini + MAX(t.sec) FROM @t 
    -- UPDATE sequencesTable SET value = @ini WHERE seqId = 987 
END 

Ist es eine bessere Möglichkeit, dies zu tun?

Vielen Dank im Voraus.

+0

Sie müssen bestimmen, was die Absicht und Bedeutung dieser int PK in der anderen Tabelle ist. Nur so können Sie sicher sein, dass Sie den richtigen Schlüsselwert erhalten. – RBarryYoung

Antwort

2

Unter der Annahme, SQL 2005+ können Sie ROW_NUMBER() verwenden:

CREATE TRIGGER trg 
    ON [dbo].[table] 
    AFTER INSERT 
AS 
BEGIN 
    SET NOCOUNT ON; 

    INSERT INTO tableDest 
     (id, field1, field2) 
    SELECT 
     Seed.Value + ROW_NUMBER() OVER(ORDER BY Id), 
     field1, 
     field2 
    FROM Inserted 
    CROSS JOIN (
     SELECT MAX(id) as Value 
     FROM tableDest 
    ) as Seed 
END 

ein CROSS JOIN tun, anstatt sich der Startwert erspart Ihnen direkt von der Gleichzeitigkeit Kopfschmerzen von MAX(Id) Wechsel zwischen den Wert bekommen und es einfügen. Andernfalls benötigen Sie eine SERIALIZABLE-Transaktion, um zu verhindern, dass neue Zeilen nach dem Lesen in tableDest eingefügt werden.

+0

leider ist es sql2000 –

+0

Dann noch die CROSS JOIN für den Startwert, aber Sie müssen Ihre Tabellenvariable verwenden, um den Inkrementwert zu erhalten. –

0

Braucht tableDest wirklich einen künstlichen Primärschlüssel?

Auch Nebenläufigkeit: gibt es eine Wahrscheinlichkeit von anderen Einfügungen passiert auf TableDest zwischen Ihnen Berechnung des Wertes von @ini und tatsächlich die Einfügung?

+0

Welll Ich habe darüber nachgedacht, die Nebenläufigkeit zu lösen, indem man BEGIN TRAN/COMMIT ... –

+0

umschließt Code innerhalb eines Triggers mit Anfang tran/commit kaufst, kauft dir nichts. Trigger sind eine implizite Transaktion. –

+0

Wäre ein CTE besser? So etwas wie MIT ini (Offset) AS SELECT MAX (id) AS versetzt tableDest INSERT INTO tableDest (id, Feld1, Feld2) SELECT ini.offset + inserted.sec, inserted.field1, inserted.field2 FROM ini CROSS JOIN eingefügt Ich weiß nicht sicher, ob CTEs Atom garantiert sind. Meine ursprüngliche Frage bleibt jedoch: Brauchen Sie wirklich eine eindeutige ID-Spalte oder müssen Sie eine wegen einer Dummkopf "jede Tabelle muss ein ID-Feld" -Regel haben? –

0

Ich denke, ich werde die offensichtliche Frage stellen, warum nicht einen Identitätswert auf der Zieltabelle setzen? (Wenn Sie dies tun, vergewissern Sie sich, dass Sie nicht die @@ identity verwenden, um den Identitätswert aus der Quelltabelle zu erhalten, da dies nicht der ist, den Sie erhalten werden - Sie erhalten die Identität aus der Zieltabelle.)

Oder könnten Sie die Identität aus der Quellentabelle als ID für die andere Tabelle verwenden. Das scheint mir die ursprüngliche Absicht des Designs zu sein. Andernfalls könnte es schwierig sein, diese Datensätze mit den ursprünglichen Daten abzugleichen.

1

Nur um zu verdeutlichen, wenn ich diese Anweisung ausführen, wird es mir immer einen eindeutigen Balken in Tabelle Foo geben, obwohl viele gleichzeitige Anweisungen gleichzeitig parallel ausgeführt werden?

INSERT INTO Foo (Bar) 
    SELECT 
    CASE WHEN Bar = -1 
    THEN (SELECT Seed.Value + ROW_NUMBER() OVER(ORDER BY AutoId) 
      FROM inserted CROSS JOIN (SELECT MAX(Bar) as Value FROM Foo) as Seed) 
    ELSE Bar 
    END 

In diesem Fall, was macht die Anweisung Nebenläufigkeit sicher? Liegt das daran, dass das select, das den neuen Wert für Bar generiert, in der insert-Anweisung verschachtelt ist? Oder verhindert der Cross-Join den gleichzeitigen Lesezugriff auf die Tabelle Foo? Was ist mit der Case-Aussage, hat sie Auswirkungen auf das Ergebnis, was die Parallelität angeht?

Danke!