Help with SQL Server Trigger to truncate bad data before insert
The error cannot be avoided because the error is happening when the inserted table is populated.
From the documentation:
http://msdn.microsoft.com/en-us/library/ms191300.aspx
"The format of the inserted and deleted tables is the same as the format of the table on which the INSTEAD OF trigger is defined. Each column in the inserted and deleted tables maps directly to a column in the base table."
The only really "clever" idea I can think of is to take advantage of schemas and the default schema used by a login. If you can get the login that the web service is using to reference another table, you can increase the column size on that table and use the INSTEAD OF INSERT trigger to perform the INSERT into the vendor table. A variation of this is to create the table in a different database and set the default database for the web service login.
CREATE TRIGGER [myDB].[mySchema].[TruncDescription]
ON [myDB].[mySchema].[myTable]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [VendorDB].[VendorSchema].[VendorTable]
SELECT SubType, type, substring(description, 1, 255)
FROM inserted
END
SQL trigger on Truncate
From msdn:
TRUNCATE TABLE cannot activate a trigger because the operation does not log individual row deletions.
Truncate table before inserting new data
You can use a TRANSACTION, so your Table is never empty for other queries but this is not the fastest way.
START TRANSACTION;
DELETE FROM your_table;
INSERT into your_table ...
...
INSERT into your_table ....
COMMIT;
or the faster way:
TRUNCATE yout_table;
INSERT into your_table ...
...
INSERT into your_table ....
in this way the TRUNCATE deletes and recreate the table and cant be used in a TRANSACTION, but is really fast.
Create a trigger to run before TRUNCATE TABLE
Did you tried reading the specific document? Triggers applies only for DML statements (INSERT | UPDATE | DELETE
) and not for DDL commands (TRUNCATE
).
Since you tagged both MySQL
and SQL Server
Look at MySQL Documentation; create trigger
syntax says
CREATE
[DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
trigger_body
Where trigger_event: { INSERT | UPDATE | DELETE }
Look at SQL Server Documentation; create trigger
syntax says
CREATE TRIGGER [ schema_name . ]trigger_name
ON { table | view }
[ WITH <dml_trigger_option> [ ,...n ] ]
{ FOR | AFTER | INSTEAD OF }
{ [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
As you can see, trigger
events are only INSERT | UPDATE | DELETE
.
It's not possible using trigger in truncate
statement.
Truncate table with a trigger
Below is the snipped using which you can achieve your goal
create table table1 (id int, age number,name varchar2(100));
create table table2 (id int, age number,name varchar2(100));
create or replace trigger sandeeptest after insert on table1 for each row
declare
PRAGMA AUTONOMOUS_TRANSACTION;
begin
execute immediate 'truncate table table2';
insert into table2(id,age,name) values (:new.id,:new.age,:new.name);
commit;
end;
insert into table1 (id,age,name) values (1,21,'A');
commit; -- after commit data is persisted in both the tables
insert into table1 (id,age,name) values (2,21,'B');
rollback;-- even after rollback data is presisted in table2 because we created an autonomous trigger
select * from table1;
select * from table2;
How to trigger before INSERT statement in stored procedure in SQL Server 2005?
In the stored procedure itself truncate the table. No need to write a trigger.
Trigger will slow down your process
ALTER PROCEDURE [dbo].[aProcedure]
@val1 VARCHAR(255),
@val2 VARCHAR(255),
...
AS
BEGIN
SELECT @sql = '//Do SELECT statement'
TRUNCATE table dbo.MyTable -- < add truncate here
INSERT INTO dbo.MyTable(....)
SELECT @paramList = ' /*list of parameters*/';
EXEC sp_executesql @sql, @paramList, ... parameters
END
SQL Server - Instead of Insert Trigger - override truncation error?
Below is an incomplete idea how to work-around the above problem. It kind of evasive. But interesting way test how far you can rework a database.
The below changes the schema around when a table is created or altered. On creating or altering a table, it will be swapped out for an indexed view after the table gets renamed.
ALTER TRIGGER trigger_CreateTable
ON DATABASE
AFTER CREATE_TABLE, ALTER_TABLE
AS
BEGIN
--SELECT EVENTDATA()
DECLARE @Prefix AS nvarchar(256) = N'PleaseUseView_'
DECLARE @Event AS XML = EVENTDATA()
DECLARE @SchemaName AS nvarchar(255) = (@Event.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)'))
DECLARE @TableName AS nvarchar(255) = (@Event.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)'))
DECLARE @ObjectType AS nvarchar(255) = (@Event.value('(/EVENT_INSTANCE/ObjectType)[1]', 'NVARCHAR(255)'))
DECLARE @TableWithSchema AS nvarchar(512) = '[' + @SchemaName + '].[' + @TableName + ']'
CREATE TABLE #SchemaBindingDependencies
(
[id] int NOT NULL IDENTITY,
[schema] nvarchar(256) NOT NULL,
[name] nvarchar(256) NOT NULL
)
INSERT INTO #SchemaBindingDependencies([schema], [name])
SELECT DISTINCT s.name AS [schema], o.name
FROM sys.objects AS o
INNER JOIN sysdepends AS d
ON d.id = o.[object_id]
INNER JOIN sys.schemas AS s
ON s.[schema_id] = o.[schema_id]
WHERE o.type ='V' AND d.depid = OBJECT_ID(@TableWithSchema)
AND SUBSTRING(@TableName, LEN(@Prefix) + 1, 256) LIKE o.[name]
IF (EXISTS(SELECT 1 FROM #SchemaBindingDependencies))
BEGIN
DECLARE @Index AS int = (SELECT MAX(id) FROM #SchemaBindingDependencies)
WHILE (@Index > 0)
BEGIN
DECLARE @ViewName1 AS nvarchar(256) = (SELECT [name] FROM #SchemaBindingDependencies WHERE id = @Index)
IF (@ViewName1 IS NOT NULL)
BEGIN
DECLARE @SchemaName1 AS nvarchar(256) = (SELECT [schema] FROM #SchemaBindingDependencies WHERE id = @Index)
DECLARE @DropSchemaBoundViewQuery AS nvarchar(1000) = 'DROP VIEW [' + @SchemaName + '].[' + @ViewName1 + ']'
EXEC(@DropSchemaBoundViewQuery)
END
SET @Index = @Index - 1
END
END
IF (SUBSTRING(@TableName, 1, LEN(@Prefix)) <> @Prefix)
BEGIN
DECLARE @NewTableName AS nvarchar(512) = @Prefix + @TableName + ''
DECLARE @NewTableWithSchema AS nvarchar(512) = '[' + @SchemaName + '].[' + @NewTableName + ']'
EXEC sp_rename @TableWithSchema, @NewTableName
SET @TableName = @NewTableName
SET @TableWithSchema = '[' + @SchemaName + '].[' + @NewTableName + ']'
END
DECLARE @Columns AS nvarchar(max) = (STUFF((SELECT ',[' + x.[name] + ']' FROM (
SELECT c.[name]
FROM sys.columns AS c
INNER JOIN sys.tables AS t
ON t.[object_id] = c.[object_id]
INNER JOIN sys.schemas AS s
ON s.[schema_id] = t.[schema_id]
WHERE t.[name] = @TableName AND s.[name] = @SchemaName) AS x FOR XML PATH('')), 1, 1, ''))
DECLARE @ViewName AS nvarchar(256) = SUBSTRING(@TableName, LEN(@Prefix) + 1, 256)
DECLARE @ViewWithSchema AS nvarchar(512) = '[' + @SchemaName + '].[' + @ViewName + ']'
DECLARE @Query AS nvarchar(max) =
N'CREATE VIEW ' + @ViewWithSchema + N' ' + CHAR(10) + CHAR(13) +
N'WITH SCHEMABINDING ' + CHAR(10) + CHAR(13) +
N'AS ' + CHAR(10) + CHAR(13) +
N' SELECT ' + @Columns + ' ' + CHAR(10) + CHAR(13) +
N' FROM ' + @TableWithSchema + N' '
--SELECT @Query
EXEC(@Query)
SET @Query =
N'CREATE UNIQUE CLUSTERED INDEX [CIX_' + @ViewName + N'] ' + CHAR(10) + CHAR(13) +
N'ON ' + @ViewWithSchema + N'(' + @Columns + N')'
EXEC(@Query)
-- TODO: Use the below double commented to build a variable insert statement for the "Instead of TRIGGER"
--
----DECLARE @tv_source TABLE (id int)
----declare @XML xml;
----set @XML =
---- (
---- select top(0) *
---- from @tv_source
---- for XML RAW, ELEMENTS, XMLSCHEMA
---- );
----SELECT T.c.query('.'), T.c.value('@name', 'nvarchar(256)')
----FROM @XML.nodes('/*/*/*/*/*') AS T(c)
--
--SET @Query =
-- N'CREATE TRIGGER [Trigger_' + @ViewName + N'] ' + CHAR(10) + CHAR(13) +
-- N'ON ' + @ViewWithSchema + N' ' + CHAR(10) + CHAR(13) +
-- N'INSTEAD OF INSERT ' + CHAR(10) + CHAR(13) +
-- N'AS BEGIN ' + CHAR(10) + CHAR(13) +
-- N'BEGIN TRY ' + CHAR(10) + CHAR(13) +
-- N' INSERT INTO ' + @TableWithSchema + N'(' + @Columns + N')'
-- N' SELECT ' + @Columns +
--EXEC(@Query)
END
Ideally, you would use a different schema for tables. And use dbo or
the default for the view.
Once the Instead of TRIGGER
is working, you could wrap a TRY/CATCH around it. On the catch, check schema for truncation. And expand the column sizes if needed.
Incomplete solution. But it is the answer I will stick with for now.
If anyone has any better answers or a complete solution, please add it!
tl;dr
One interesting thing is the below query
DECLARE @tv_source TABLE (id int)
declare @XML xml;
select top(0) *
from @tv_source
for XML RAW, ELEMENTS, XMLSCHEMA
You can return schema. SOAP for the above XML. Or JsonSchema or Avro if using the SQL 2016+ Json version to build Restful API's with schema awareness. And with schema awareness, an application gateway could auto-grab many dispersed Micro-Rest API's into one seemly large Rest API.
Related Topics
First Two Salaries in Each Department
Select First Record If None Match
Using Dynamic in Clause in Mssql
Sql, SQLite Select with Inner Join
Row Num Is Not Displaying Any Rows When Using Between Keyword
How to Use Dynamic Column Names in an Update or Select Statement in a Function
How to Extend the Query to Add 0 in the Cell When No Activity Is Performed
How to Pass Column Name as Parameter in Select Statement SQL Server
Sql/Postgresql Left Join Ignores "On = Constant" Predicate, on Left Table
Help with SQL Server Trigger to Truncate Bad Data Before Insert
Rename a Column in All the Tables - SQL
Calculating Age from Birthday with Oracle Plsql Trigger and Insert the Age in Table
Elasticsearch Map Two SQL Tables with a Foreign Key
Need Oracle SQL to Split Up Date/Time Range by Day
Generally, Are String (Or Varchar) Fields Used as Join Fields