I am trying to generate triggers on a number of tables to force data to upper-case and to trim leading and trailing spaces when data is inserted or updated. The issue I am having is determining in the trigger a general way to tie the INSERTED pseudotable to the base table. Not all of the tables have the same PK name although many do have a unique column called _DataChanges_RowID in each table which can be used. I know that I can inspect the PK and generate teh triggers using those columns, but before I look at that, I wanted to know if there is a generic way to join the INSERTED pseudotable to the base table which is simpler and column agnostic.
DECLARE @TriggerTemplate AS VARCHAR(MAX) =
'CREATE TRIGGER <<SchemaName>>.<<TriggerName>> ON <<FullTableName>>
AFTER INSERT, UPDATE
AS BEGIN
IF (ROWCOUNT_BIG() = 0)
RETURN;
IF TRIGGER_NESTLEVEL(( SELECT object_id FROM sys.triggers WHERE QUOTENAME(name) = ''<<TriggerName>>'' ), ''AFTER'', ''DML'') < 1
UPDATE <<FullTableName>>
SET <<SQLColumnUpdate>>
WHERE <<FullTableName>>._DataChanges_RowID IN (SELECT _DataChanges_RowID FROM INSERTED);
END;
GO
sp_settriggerorder <<SchemaName>>.<<TriggerName>>, ''FIRST'', ''INSERT'';
sp_settriggerorder <<SchemaName>>.<<TriggerName>>, ''FIRST'', ''UPDATE'';
GO
';
WITH cols AS
(
SELECT
FullTableName = QUOTENAME(S.name) + '.' + QUOTENAME(T.name),
SchemaName = QUOTENAME(S.name),
TableName = QUOTENAME(T.name),
ColumnName = QUOTENAME(C.name)
FROM
sys.columns C
INNER JOIN sys.tables T
ON C.object_id = T.object_id
INNER JOIN sys.schemas S
ON T.schema_id = S.schema_id
WHERE
C.is_computed = 0
AND C.system_type_id IN (
167, -- varchar
175, -- char
231, -- nvarchar
239 -- nchar
)
)
, TablesAndColumns AS (
SELECT
cols.FullTableName
, cols.TableName
, cols.SchemaName
, TriggerName = QUOTENAME('TRG_DATA_CONFORM_' + cols.FullTableName)
, SQLColumnUpdate = STUFF((SELECT ', ' + c2.ColumnName + ' = UPPER(LTRIM(RTRIM(' + c2.ColumnName + ')))'
FROM cols c2
WHERE c2.FullTableName = cols.FullTableName
ORDER BY c2.ColumnName
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, '')
FROM cols
GROUP BY cols.FullTableName, cols.SchemaName, cols.TableName
)
SELECT FullTableName
, SQLTrigger = REPLACE(REPLACE(REPLACE(REPLACE(@TriggerTemplate, '<<SQLColumnUpdate>>', SQLColumnUpdate), '<<TriggerName>>', TriggerName), '<<FullTableName>>', FullTableName), '<<SchemaName>>', SchemaName)
, KeyOK = CASE WHEN EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE QUOTENAME(TABLE_SCHEMA) = SchemaName AND QUOTENAME(TABLE_NAME) = TableName AND COLUMN_NAME = '_DataChanges_RowID') THEN 1 ELSE 0 END
FROM TablesAndColumns
;
Best Answer
No, there is no generic, column-agnostic way to join the
INSERTED
pseudo-table to the base table. You will need to use a conventional inner join and reference the PK column (or columns if the key is composite) explicitly in the join predicate.Therefore, if the PK column is not the same in each of the tables you are generating the triggers for, there is no way around querying metadata to find out the column name to use. You can do that using the SQL Standard-compliant (well, more or less)
INFORMATION_SCHEMA
views (example) or using the native SQL Server system catalogues (example).