2012-06-12 2 views
1

У меня небольшая проблема с триггером обновления в SQL Server 2008 R2. Я установил триггер для регистрации изменений в таблице журналов. Это работает отлично для меня, но не тогда, когда я обновляю несколько строк.Сложный триггер обновления SQL Server с несколькими строками

CREATE TRIGGER [dbo].[TR_CompaniesUpdated] 
ON [dbo].[Companies] 
AFTER UPDATE 
AS 
BEGIN 
SET NOCOUNT ON; 
DECLARE 
    @Return NVARCHAR(MAX), -- Is the JSON of the data 
    @ParameterSQL VARCHAR(MAX), -- Is the select we want to retreive in JSON format 

    -- Data from of the row updated 
    @ID INT, 
    @StatusID SMALLINT, 

    -- Old data from of the row 
    @StatusID2 SMALLINT 

-- Declare the cursor 
DECLARE InsertedCursor CURSOR FAST_FORWARD FOR 
SELECT [ID], [StatusID] FROM INSERTED 

-- Create the temporary table with logs so we'll need to do only one insert after this 
CREATE TABLE #tempLog(
    [ID] [int] NOT NULL, 
    [Data] [nvarchar](max) NOT NULL, 
    [ActionID] [int] NOT NULL, 
    [DtCreated] [datetime] NOT NULL, 
    [CreatedBy] [int] NOT NULL) 

-- Save updated values into variables 
OPEN InsertedCursor 
FETCH NEXT FROM InsertedCursor 
INTO @ID, @StatusID 

WHILE @@fetch_status = 0 
BEGIN 
    -- Save old values into variables 
    SELECT 
     @StatusID2 = [StatusID] 
    FROM DELETED 
    WHERE [ID] = @ID 

    SELECT * INTO #tempTable 
    FROM DELETED 
    WHERE [ID] = @ID 

    -- Build the select only to et the changed data 
    SET @ParameterSQL = 'SELECT ' 
    IF (ISNULL(@StatusID, '') != ISNULL(@StatusID2, '')) SET @ParameterSQL = @ParameterSQL + '[StatusID],' 
    IF (@ParameterSQL != 'SELECT ') 
    BEGIN 
     -- Update the modified date 
     UPDATE [dbo].[AssessedLevelCostCentre] 
     SET [DtModified] = GETDATE() 
     WHERE [ID] = @ID 

     SET @ParameterSQL = SUBSTRING(@ParameterSQL, 0, LEN(@ParameterSQL)) + ' FROM #tempTable' 

     -- Execute the JSON function to get the result in JSON format 
     EXEC [dbo].[GetJSON] @ParameterSQL, @Return = @Return OUTPUT 

     -- Insert the Change Log with the old data 
     INSERT INTO #tempLog 
     SELECT 
      [ID] AS [ID], 
      @Return AS [Data], 
      [ActionID] AS [ActionID], 
      CASE 
       WHEN [DtModified] IS NULL 
        THEN [DtCreated] 
        ELSE [DtModified] 
      END AS [DtModified], 
      CASE 
       WHEN [ModifiedBy] IS NULL 
        THEN [CreatedBy] 
        ELSE [ModifiedBy] 
      END AS [CreatedBy] 
     FROM #tempTable 
    END 
    ELSE 
    BEGIN 
     UPDATE [dbo].[AssessedLevelCostCentre] 
     SET [ModifiedBy] = (SELECT [ModifiedBy] FROM #tempTable) 
     WHERE [ID] = @ID 
    END 

    DROP TABLE #tempTable 

    -- Advance the Cursor 
    FETCH NEXT FROM InsertedCursor 
    INTO @ID, @StatusID 
END 

CLOSE InsertedCursor 
DEALLOCATE InsertedCursor 

-- Insert the Change Log with the old data 
INSERT INTO [dbo].[AssessedLevelsCostCentersLogs] 
SELECT * 
FROM #tempLog 

DROP TABLE #tempLog 
END 

Как вы можете видеть, у меня есть хранимая процедура, чем возвращает мне данные в формате JSON, проблема, чем хранимая процедура получения данных из запроса, и я не могу передать делеции или инсерциям таблицы, это Причина, по которой я использовал временную таблицу, которая, похоже, делает трюк, но добавляет предложение ID.

ОБНОВЛЕНО

Я обновил код, теперь у меня есть курсор, а как я могу видеть, производительность это очень медленно. Мне это нужно, потому что я не могу изменить приложение для создания JSON, и я думаю, что я запускаю, как это будет работать нормально. У кого-нибудь есть идея или как улучшить производительность этого?

+2

Вы должны перестроить свой триггер - я бы рекомендовал ** НЕ ** выполнять любую обработку длины (например, генерировать JSON, используя хранимую процедуру) внутри триггера. Триггер срабатывает много раз, и вы не можете контролировать, как часто и когда он срабатывает. Вы должны попытаться ограничить триггер ** абсолютным минимумом ** - просто введите запись в таблицу или что-то еще - не более. Если вам нужно выполнить дополнительную обработку - сделайте это в отдельной асинхронной задаче, например. регулярно работая как запланированное задание SQL. Сделайте ** НЕ ** перегружайте триггеры! Это будет ** убить ** вашу систему! –

ответ

2

Триггер на сервере Sql запускается один раз для каждой партии, поэтому, если вы вставляете 5000 записей в один оператор, триггер запускается ровно один раз. Что вы сделали, так это предположить, что он будет запускаться для каждой отдельной записи, установив значения, которые вы хотите скалярные переменные, которые, конечно, могут содержать только одно значение, а не 5000. Вам нужно пересмотреть свой процесс, чтобы воспользоваться преимуществами присоединения к вставленным и удаленным не проходя через одну запись за раз.

+2

Не за партию, за заявление. Это разница. – usr

+0

@usr, да, я был неточен. – HLGEM

+0

Да, это то, что я видел. Я передумал это, и я добавил в него курсор, но производительность очень медленная, взяв мой локальный около 30 секунд, чтобы обновить 5000 строк ... – vcliment89