Чтение вставленных имен столбцов и значений в триггере TSQL - PullRequest
1 голос
/ 09 июля 2010

Меня попросили создать таблицы истории для каждой таблицы в базе данных.Затем создайте триггер, который будет записывать в таблицу истории при каждом обновлении первичной таблицы.

Таблицы истории имеют ту же структуру, что и первичная таблица, но с несколькими дополнительными строками («id» и «update»).type ')

Я никогда ничего раньше не делал с триггерами, но мне хотелось бы, чтобы динамически проходить по столбцам в' Inserted 'и создавать оператор вставки для заполнения таблицы истории.

Однако я не могу понять, как читать имена столбцов и их отдельные значения.

Мой недоделанный триггер в настоящее время выглядит как ...

CREATE TRIGGER tr_address_history
ON address
FOR UPDATE
AS

DECLARE @colCount int
DECLARE @maxCols int
SET @colCount = 0
SET @maxCols = (SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted')

PRINT 'Number of columns = ' + CONVERT(varChar(10),@maxCols)
WHILE (@colCount <= @maxCols)
BEGIN
    DECLARE @name varchar(255)
    SELECT @name = column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted'
    DECLARE @value varchar(255)
    SELECT @value = @name FROM Inserted

    PRINT 'name = ' + @name + ' and value = ' + @value
    SET @colCount = @colCount + 1
END
PRINT 'Done';

Когда триггер запускает егопросто говорит "Количество столбцов = 0"

Может кто-нибудь сказать мне, что не так с:

SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted'

Спасибо ...

Ответы [ 3 ]

2 голосов
/ 23 августа 2012

Первое решение, предложенное Beenay25, хорошо, но вы должны использовать уязвимую таблицу вместо «вставленной» псевдотаблицы.

Это:

SELECT @name = column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'AFFECTED_TABLE'

вместо 'INSERTED'

Кроме того, вы должны использовать динамический SQL.

Это будет полное рабочее решение:

ALTER TRIGGER [dbo].[tr_address_history]
ON [dbo].[address]
AFTER Insert
AS

DECLARE @ColumnName nvarchar(500)
DECLARE @TableName nvarchar(500)
DECLARE @value nvarchar(500)
DECLARE @Sql nvarchar(500)

Set @TableName='address'

DECLARE ColumnsCursor CURSOR FOR
select column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'address'

OPEN ColumnsCursor
FETCH NEXT FROM ColumnsCursor into @ColumnName

WHILE @@FETCH_STATUS=0
BEGIN 

      select * into #tmp from inserted
      Set @Sql= 'SELECT @value =' + @ColumnName + ' FROM #tmp'

      EXEC sp_executesql @Sql, N'@Value nvarchar(500) OUTPUT', @Value OUTPUT

      DROP TABLE #TMP

      print '[' + @ColumnName +'='+ ltrim(rtrim(@Value))+']'

      FETCH NEXT FROM ColumnsCursor into @ColumnName
END   

CLOSE ColumnsCursor
DEALLOCATE ColumnsCursor
1 голос
/ 12 марта 2011

Есть способ сделать то, что требует спрашивающий:

Я сделал что-то внутри триггера, который проверяет, действительно ли все столбцы конкретной таблицы участвовали в вставке в эту таблицу.Если они это сделали, я позже скопировал их в таблицу истории.Если этого не произошло, то в таблицу отчета можно вставить откат и напечатать только полные строки.Возможно, они могли бы адаптировать это к своим потребностям:

вот оно:

    [

if exists (select 1 from inserted) and not exists (select 1 from deleted) -- if an insert has been performed
begin -- and we want to test whether all the columns in the report table were included in the insert
declare @inserted_columncount int, @actual_num_of_columns int, @loop_columns int, @current_columnname nvarchar(300),
    @sql_test nvarchar(max), @params nvarchar(max), @is_there bit
set @actual_num_of_columns = (
    select count(*) from (
    select COLUMN_NAME
    from INFORMATION_SCHEMA.COLUMNS
    where TABLE_NAME = 'renameFilesFromTable_report') as z)
set @inserted_columncount = 0
set @loop_columns = 1
declare inserted_columnnames cursor scroll for -- these are not really the inserted ones, but we are going to test them 1 by 1
    select COLUMN_NAME
    from INFORMATION_SCHEMA.COLUMNS
    where TABLE_NAME = 'renameFilesFromTable_report'
set @params = '@is_there_in bit output'
open inserted_columnnames
fetch next from inserted_columnnames into @current_columnname
select * into #temp_for_dynamic_sql from inserted -- this is necessary because the scope of sp_executesql does not include inserted pseudo table
while (@loop_columns <= @actual_num_of_columns) -- looping with independent integer arithmetic
begin
set @sql_test = '
set @is_there_in = 0
if exists (select ['+@current_columnname+'] from #temp_for_dynamic_sql where ['+@current_columnname+'] is not null)
set @is_there_in = 1'
exec sp_executesql @sql_test, @params, @is_there output
if @is_there = 1
begin
fetch next from inserted_columnnames into @current_columnname
set @inserted_columncount = @inserted_columncount + 1
set @loop_columns = @loop_columns + 1
end
else if @is_there <> 1
begin
fetch next from inserted_columnnames into @current_columnname
set @loop_columns = @loop_columns + 1
end
end 
close inserted_columnnames
deallocate inserted_columnnames
-- at this point we hold in two int variables the number of columns participating in the insert and the total number of columns

    ]

Тогда вы можете просто сделать это, если @inserted_columncount <@actual_num_of_columns .......... </p>

Я сделал это, потому что у меня есть sp, который вставляет 1 полную строку в таблицу отчета при каждом запуске.Это хорошо, но я не хочу, чтобы кто-нибудь еще случайно дотронулся до этого стола.даже не я.Я тоже хочу сохранить историю.Поэтому я сделал этот триггер, чтобы сохранить историю, а также проверить, была ли предпринята попытка вставки без значений для всех столбцов таблицы отчета, и далее по коду, который он проверяет, предпринимались ли попытки обновления или удаления, и происходит ли откат.

Я думал о расширении этого, чтобы позволить обновление, но в котором установлены все столбцы.возможно, это можно сделать следующим образом:

в случае попытки обновления

and exists (
select possibly_excluded.COLUMN_NAME from (
select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'renameFilesFromTable_report') as possibly_excluded
group by possibly_excluded.COLUMN_NAME
having COLUMN_NAME not in (
select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'renameFilesFromTable_report' and
sys.fn_IsBitSetInBitmask(@ColumnsUpdated, COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME), COLUMN_NAME, 'ColumnID')) <> 0)
)
begin
rollback transaction
print 'Only updates that set the values for a complete row are allowed on the report table..'
end
1 голос
/ 09 июля 2010

Таблица 'inserted' является псевдотаблицей;он не отображается в INFORMATION_SCHEMA.

Существует оператор UPDATE() для использования в триггерах:

CREATE TRIGGER trigger_name ON tablename
FOR UPDATE
AS
SET NOCOUNT ON
IF (UPDATE(Column1) OR UPDATE(Column2))
BEGIN
  your sql here
END

COLUMNS_UPDATED

UPDATE ()

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