2010-07-09 3 views
1

Il m'a été demandé de créer des tables d'historique pour chaque table d'une base de données. Créez ensuite un déclencheur qui écrira dans la table d'historique à chaque fois que la table primaire est mise à jour.Lecture des noms et des valeurs de colonnes insérées dans un déclencheur TSQL

Les tableaux d'histoire ont la même structure que la table primaire, mais avec quelques lignes supplémentaires (« id » et « type de mise à jour »)

Je ne l'ai jamais fait quoi que ce soit avec des déclencheurs avant, mais je comme faire est de parcourir dynamiquement les colonnes dans 'Inséré' et de construire une instruction d'insertion pour remplir la table de l'histoire.

Cependant, je ne peux pas comprendre comment lire les noms des colonnes et leurs valeurs individuelles.

Ma moitié fini déclencheur ressemble actuellement ...

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'; 

Lorsque la gâchette fonctionne il est dit que « Nombre de colonnes = 0 »

Quelqu'un peut-il me dire ce qui ne va pas avec:

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

Merci ...

Répondre

1

Le tableau 'inserted' est un PSEU do-table; il n'apparaît pas dans INFORMATION_SCHEMA.

Il est l'opérateur UPDATE() pour une utilisation dans les déclencheurs:

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()

+0

@ Beenay25 La table 'inserted' aura le même schéma que l'adresse qui apparaît dans' INFORMATION_SCHEMA' si c'est toute utilisation. –

+0

Salut Mitch, Merci pour cela, mais je ne sais pas quels seront les noms des colonnes avant que le déclencheur ne soit lancé ... C'est pourquoi j'essayais d'obtenir les informations sur INFORMATION_SCHEMA Je dois mettre un déclencher sur soixante tables. Je ne veux pas avoir à coder en dur les noms de chaque colonne :( Je veux juste réutiliser le même code (bien, avec des noms de tables différents évidemment) – Beenay25

+1

@ Beenay25: écrire un script pour générer les déclencheurs avec des noms câblés ... –

1

Il y a une façon de faire ce que le questionneur demande:

J'ai fait quelque chose à l'intérieur d'un déclencheur qui teste si toutes les colonnes d'une table particulière ont effectivement participé à une insertion t à cette table. Si c'était le cas, je les ai ensuite copiés sur une table d'histoire. Si ce n'est pas le cas, l'annulation et l'impression des lignes complètes uniquement peuvent être insérées dans la table de rapport. Peut-être qu'ils pourraient adapter cela à leurs besoins:

ici est:

[ 

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 ['[email protected]_columnname+'] from #temp_for_dynamic_sql where ['[email protected]_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 

    ] 

Ensuite, vous pouvez simplement faire si @inserted_columncount < @actual_num_of_columns ..........

I fait cela parce que j'ai un sp qui insère 1 ligne complète à la table de rapport chaque fois qu'il s'exécute. C'est bien, mais je ne veux pas que quelqu'un d'autre touche cette table par erreur. pas même moi. Je veux aussi garder l'histoire. J'ai donc fait ce déclencheur pour conserver l'historique mais aussi pour vérifier si une insertion a été tentée sans valeurs pour toutes les colonnes de la table de rapport et plus bas le code vérifie si une mise à jour ou une suppression a été tentée.

Je pensais à étendre ceci pour permettre une mise à jour mais dans laquelle toutes les colonnes sont définies. cela pourrait se faire comme suit:

si la mise à jour a été tentée,

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 
2

solution proposée par Beenay25 est bon, mais vous devez utiliser la table affectée au lieu de « inséré » pseudotable.

C'est:

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

Au lieu de insertbefore '

En outre, vous devez utiliser SQL dynamique.

Ce sera une solution de travail complète:

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 
Questions connexes