Триггеры на TSQL вставки неидентификационной таблицы - PullRequest
1 голос
/ 19 июня 2009

Я работаю над триггером, который должен повторно вставить свои данные в другую таблицу.

Таблица назначения имеет первичный ключ INT NOT NULL БЕЗ идентификатора, поэтому у меня есть 2 варианта:

  1. Рассчитайте максимум и вставьте отсюда.
  2. Взять максимальное значение из таблицы последовательностей.

Я использую, чтобы всегда создавать табличную переменную с идентификатором и вставлять смещение от значения, вычисленного ранее.

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

Есть ли лучший способ сделать это?

Заранее спасибо.

Ответы [ 4 ]

2 голосов
/ 20 июня 2009

Предполагая SQL 2005+, вы можете использовать ROW_NUMBER () :

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

Выполнение CROSS JOIN вместо получения начального значения напрямую избавляет вас от головной боли параллелизма MAX(Id), переходящей между получением значения и его вставкой. В противном случае вам потребуется транзакция SERIALIZABLE, чтобы предотвратить вставку новых строк в tableDest после прочтения.

1 голос
/ 08 февраля 2010

Просто чтобы уточнить, если я выполню этот оператор, он всегда даст мне уникальный столбец в таблице Foo, даже если одновременно выполняется много одновременных операторов?

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

В таком случае, что делает параллелизм операторов безопасным? Это потому, что select, который генерирует новое значение для Bar, вложен в оператор вставки? Или это перекрестное соединение, которое предотвращает одновременный доступ на чтение к таблице Foo? Как насчет описания случая, влияет ли это на результат с точки зрения параллелизма?

Спасибо!

0 голосов
/ 20 июня 2009

Полагаю, я задам очевидный вопрос, почему бы не установить значение идентификатора в таблице назначения? (Обратите внимание, что если вы сделаете это, убедитесь, что вы не используете @@ identity для получения значения идентификатора из исходной таблицы, поскольку это не то, что вы получите - вы получите идентификатор из целевой таблицы. )

Или вы можете использовать идентификатор из исходной таблицы в качестве идентификатора для другой таблицы. Это поражает меня как первоначальное намерение дизайна. В противном случае может быть сложно сопоставить эти записи с исходными данными.

0 голосов
/ 20 июня 2009

Действительно ли tableDest нужен искусственный первичный ключ?

Кроме того, параллелизм: есть ли вероятность других вставок, происходящих в tableDest между вычислением значения @ini и фактическим выполнением вставки?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...