Multi table Triggers SQL Server noob - sql-server-2005

I have a load of tables all with the same 2 datetime columns (lastModDate, dateAdded).
I am wondering if I can set up global Insert Update trigger for these tables to set the datetime values. Or if not, what approaches are there?
Any pointers much appreciated

I agree there is no such Global Trigger, but we can certainly reduce our efforts by creating script which will generate triggers for the tables.
Something like: http://weblogs.sqlteam.com/brettk/archive/2006/11/29/35816.aspx

No, there's no such thing as a "global" trigger or a multi-table triggers. Triggers are by design bound to a table, so if you need to have triggers on a load of tables, you need to create a load of triggers, one for each table, and deploy them. No way around that, I'm afraid.

since the code will be same and only table_name will be changed...
i think that best is to create procedure and then call this procedure from every trigger

You can use DEFAULT values for the inserts (dateAdded) and a TABLE trigger for the UPDATE.
Something like
CREATE TABLE MyTable (
ID INT,
Val VARCHAR(10),
lastModDate DATETIME DEFAULT CURRENT_TIMESTAMP,
dateAdded DATETIME DEFAULT CURRENT_TIMESTAMP
)
GO
CREATE TRIGGER MyTableUpdate ON MyTable
FOR UPDATE
AS
UPDATE MyTable
SET lastModDate = CURRENT_TIMESTAMP
FROM MyTable mt INNER JOIN
inserted i ON mt.ID = i.ID
GO
INSERT INTO MyTable (ID, Val) SELECT 1, 'A'
GO
SELECT *
FROM MyTable
GO
UPDATE MyTable
SET Val = 'B'
WHERE ID = 1
GO
SELECT *
FROM MyTable
GO
DROP TABLE MyTable
GO

Generate Triggers for all Tables
Well, I did this originally to generate triggers for all tables in a database to audit data changes, and that is simple enough, just move the entire row from the deleted table to a mirrored audit table.
But someone wanted to track activity on tables, so it's a little more simple. Here we create one log table, and any time a dml operation occurs, it is written there.
Enjoy
USE Northwind GO
CREATE TABLE LOG_TABLE (Add_dttm datetime DEFAULT (GetDate()),
TABLE_NAME sysname, Activity char(6)) GO
DECLARE #sql varchar(8000), #TABLE_NAME sysname SET NOCOUNT ON
SELECT #TABLE_NAME = MIN(TABLE_NAME) FROM INFORMATION_SCHEMA.Tables
WHILE #TABLE_NAME IS NOT NULL BEGIN SELECT #sql = 'CREATE TRIGGER
[' + #TABLE_NAME + '_Usage_TR] ON [' + #TABLE_NAME +'] ' + 'FOR
INSERT, UPDATE, DELETE AS ' + 'IF EXISTS (SELECT * FROM inserted)
AND NOT EXISTS (SELECT * FROM deleted) ' + 'INSERT INTO LOG_TABLE
(TABLE_NAME,Activity) SELECT ''' + #TABLE_NAME + ''', ''INSERT''' + '
' + 'IF EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM
deleted) ' + 'INSERT INTO LOG_TABLE (TABLE_NAME,Activity) SELECT '''
+ #TABLE_NAME + ''', ''UPDATE''' + ' ' + 'IF NOT EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted) ' + 'INSERT INTO
LOG_TABLE (TABLE_NAME,Activity) SELECT ''' + #TABLE_NAME + ''',
''DELETE''' + ' GO' SELECT #sql EXEC(#sql) SELECT #TABLE_NAME =
MIN(TABLE_NAME) FROM INFORMATION_SCHEMA.Tables WHERE TABLE_NAME >
#TABLE_NAME END SET NOCOUNT OFF

Related

Multiple IF NOT EXISTS with one wrapper?

I am wondering if there is any way to do this. We have a program that does data modeling for us (IDA), and it will generate at times hundreds of different alter/delete/update statements for us. The script works, except it does not meet the requirement of being able to be ran multiple times, which we sometimes need due to devops work. I haven't been able to find a way for the modeler to automatically add an IF NOT EXISTS to each statement, and so this means manually needing to add it to each one.
Is there a way to wrap the entire script in one IF NOT EXISTS? or handle this with some other kind of loop or flag I don't know about?
Example:
Currently we would have to do this:
IF NOT EXISTS (
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'USERS' AND COLUMN_NAME = 'LASTNAME'
)
BEGIN
ALTER TABLE DBO.USERS ADD LASTNAME CHAR(2) NULL
END;
GO
IF NOT EXISTS (
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'ASSETS' AND COLUMN_NAME = 'ASSETTYPE'
)
BEGIN
ALTER TABLE DBO.ASSETS ADD ASSETTYPE CHAR(2) NULL
END;
GO
IF NOT EXISTS (
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'ADDRESS' AND COLUMN_NAME = 'LINE3'
)
BEGIN
ALTER TABLE DBO.ADDRESS ADD LINE3 CHAR(2) NULL
END;
GO
Whereas I'd like to be able to not need to add the IF NOT EXISTS to every select, just something to indicate for the script to automatically check if it exists first.
Any thoughts? Thanks.
You could try:
IF COL_LENGTH('table_name','column_name') IS NULL
BEGIN
/* ALTER TABLE .... */
END
This was obtained from this forum: How to check if a column exists in a SQL Server table? (Got 958 up votes) or
Add a column to a table, if it does not already exist
You would need to load all the TableName and ColumnName combination into the #TEMP1
through which you need to loop it. Also you can change the data type of the column to a variable as well. Let me know if you need help adapting the code to your needs. You can scale that code to add DB_NAME and different datatypes. But you would need different versions of the code for update and delete
SELECT TABLENAME, COLNAME
INTO #TEMP1
FROM MYTABLE
DECLARE #TABLE_NAME VARCHAR(100)
DECLARE #COL_NAME VARCHAR(100)
DECLARE #SQL VARCHAR(MAX)
WHILE EXISTS (SELECT * FROM #TEMP1)
BEGIN
SELECT TOP 1 #TABLE_NAME = TABLENAME,
#COL_NAME = COLNAME
FROM #TEMP1
SET #SQL = '
IF NOT EXISTS (
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '+#TABLE_NAME+' AND COLUMN_NAME = '+#COL_NAME+'
)
BEGIN
ALTER TABLE DBO.'+#TABLE_NAME+' ADD '+#COL_NAME+' CHAR(2) NULL
END'
EXEC #SQL
DELETE FROM #TEMP1
WHERE TABLENAME = #TABLE_NAME AND COLNAME = #COL_NAME
END

SQL: Looping through a column, stored the value as a variable, run SQL, then move on to the next line?

I'm currently shifting roles at my job and trying to teach myself some SQL Skills.
Scenario: I'm in charge of 1 database - 10 tables with 10 Primary Keys. Every month, our code team publishes updates to the tables. I am suppose to drop the tables and generate scripts to create the updated tables.
Rather than just drop the old tables and stored procedures, I want to rename my current tables to preserve the structure/data for whatever reason.
In my database, I have an additional table called "TableUpdateList" with 1 column "TableName" and 10 rows - each row containing the name of the updated column (Row 1 = TableName1, Row 2 = TableName2, Row 3 = TableName3)
I would like to be able to "loop" through the TableUpdateList Table and insert each value into a set of SQL statements.
For Example, here are the SQL statements I want to run:
--drop the previous backup table
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME = '*TableName1*'+'_Old') DROP TABLE TableName1_Old
-- rename the current tables to _old
EXEC sp_rename *TableName1*, TableName1_Old;
I'm trying to find a way to scroll through the column of my TableUpdateList and run the above two statements filling in where I've italicized with whatever value is present in that row.
Just taking a wild stab because I think in order to get an answer here, you have to try something so here is my pseudo-code:
Declare #TableNames as List
For i in #TableNames
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME = '*i*'+'_Old') DROP TABLE TableName1_Old
-- rename the current tables to _old
EXEC sp_rename *i*, TableName1_Old;
Oi, thanks in advance for any help or a point in the right direction to where I could do some further reading about the above online.
You can use sp_executesql with CURSORS for such type of work. Here is what i think you need:
Test objects:
CREATE TABLE TableName1 ( ID INT )
GO
CREATE TABLE TableName2 ( ID INT )
GO
CREATE TABLE TableNames ( Name NVARCHAR(MAX) )
GO
INSERT INTO TableNames
VALUES ( 'TableName1' ),
( 'TableName2' )
Script itself:
DECLARE #name NVARCHAR(MAX) ,
#dropStatement NVARCHAR(MAX),
#renameStatement NVARCHAR(MAX)
DECLARE cur CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT Name
FROM dbo.TableNames
OPEN cur
FETCH NEXT FROM cur INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS ( SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #name + '_Old' )
BEGIN
SET #dropStatement = 'DROP TABLE ' + #name + '_Old'
EXEC sp_executesql #dropStatement
END
SET #renameStatement = 'sp_rename ' + #name + ', ' + #name + '_Old';
EXEC sp_executesql #renameStatement
FETCH NEXT FROM cur INTO #name
END
CLOSE cur
DEALLOCATE cur
After this you should add TableName1 and TableName2 again.
Cursors must be avoided as long as possible.
--Preparing script which would check if the old tables exists. If it does,
--it drops the old table
--e.g. first the value 'Table1' is found in TableUpdateList table.
--Then, Table1_Old is deleted and Table1 is renamed to Table1_Old
SELECT 'DROP TABLE ' + b.name + '_Old; EXEC sp_rename ''' + b.name+ ''', ''' + b.name+ '_Old;''' AS [Action]
INTO #Action
FROM INFORMATION_SCHEMA.TABLES A JOIN TableUpdateList B ON A.TABLE_NAME = b.NAME + '_Old'
DECLARE #sql VARCHAR(8000)
SELECT #sql = COALESCE(#sql + ' ', '') + [Action]
FROM #Action
select #sql
--EXEC (#sql)
First verify the value of variable #sql. Then, uncomment the last line to execute the code.
SQL fiddle

Fastest way to copy sql table

Im looking for the fastest way to copy a table and its contents on my sql server just simple copy of the table with the source and destination on the same server/database.
Currently with a stored procedure select * into sql statement it takes 6.75 minutes to copy over 4.7 million records. This is too slow.
CREATE PROCEDURE [dbo].[CopyTable1]
AS
BEGIN
DECLARE #mainTable VARCHAR(255),
#backupTable VARCHAR(255),
#sql VARCHAR(255),
#qry nvarchar(max);
SET NOCOUNT ON;
Set #mainTable='Table1'
Set #backupTable=#mainTable + '_Previous'
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#backupTable) AND type in (N'U'))
BEGIN
SET #Sql = 'if exists (select * from sysobjects '
SET #Sql = #Sql + 'where id = object_id(N''[' + #backupTable + ']'') and '
SET #Sql = #Sql + 'OBJECTPROPERTY(id, N''IsUserTable'') = 1) ' + CHAR(13)
SET #Sql = #Sql + 'DROP TABLE [' + #backupTable + ']'
EXEC (#Sql)
END
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#mainTable) AND type in (N'U'))
SET #Sql = 'SELECT * INTO dbo.[' + #backupTable + '] FROM dbo.[' + #mainTable + ']'
EXEC (#Sql)
END
If you are concerned about speed, it seems you have two alternatives; copying by block or the BCP/Bulk insert method.
Block Transfer
DECLARE
#CurrentRow bigint, #RowCount bigint, #CurrentBlock bigint
SET
#CurrentRow = 1
SELECT
#RowCount = Count(*)
FROM
oldtable
WITH (NOLOCK)
WHILE #CurrentRow < #RowCount
BEGIN
SET
#CurrentBlock = #CurrentRow + 1000000
INSERT INTO
newtable
(FIELDS,GO,HERE)
SELECT
FIELDS,GO,HERE
FROM (
SELECT
FIELDS,GO,HERE, ROW_NUMBER() OVER (ORDER BY SomeColumn) AS RowNum
FROM
oldtable
WITH (NOLOCK)
) AS MyDerivedTable
WHERE
MyDerivedTable.RowNum BETWEEN #startRow AND #endRow
SET
#CurrentRow = #CurrentBlock + 1
end
How to copy a huge table data into another table in SQL Server
BCP/Bulk Insert
SELECT
*
INTO
NewTable
FROM
OldTable
WHERE
1=2
BULK INSERT
NewTable
FROM
'c:\temp\OldTable.txt'
WITH (DATAFILETYPE = 'native')
What is the fastest way to copy data from one table to another
http://www.databasejournal.com/features/mssql/article.php/3507171/Transferring-Data-from-One-Table-to-Another.htm
You seem to want to copy a table that is a heap and has no indexes. That is the easiest case to get right. Just do a
insert into Target with (tablock) select * from Source
Make sure, that minimal logging for bulk operations is enabled (search for that term). Switch to the simple recovery model.
This will take up almost no log space because only allocations are logged with minimal logging.
This just scans the source in allocation order and append new bulk-allocated pages to the target.
Again, you have asked about the easiest case. Things get more complicated when indexes come into play.
Why not insert in batches? It's not necessary. Log space is not an issue. And because the target is not sorted (it is a heap) we don't need sort buffers.

Delete Record By ID From All Tables

I want a quick way to delete all records relating to a particular 'master' record from both the 'master' table and all associated tables. This is primarily so that I can easily enter test records and then delete all trace of them, which would take a long time manually given that there are dozens of tables where the record could be referenced.
So, in summary, in any table which contains a column called AdmissionID, I want to delete all records where the AdmissionID is equal to a value I will specify. I thought I could do this quite easily. Here's my stored procedure:
ALTER PROCEDURE [Admin].[sp_RemoveByAdmissionID]
#admissionID int
AS
--Removes records from all tables relating the AdmissionID entered
DECLARE #loop INT
DECLARE #object sysname
DECLARE #cmdstring varchar(500)
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
BEGIN
DROP TABLE #TEMP
END
SELECT
TABLE_NAME
,ROW_NUMBER() OVER (ORDER BY TABLE_NAME) ROWID
INTO #TEMP
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
WHILE #loop <= (SELECT MAX(ROWID) FROM #TEMP)
BEGIN
SELECT #object = TABLE_NAME FROM #TEMP WHERE ROWID = #loop
IF EXISTS (SELECT * FROM SYS.COLUMNS WHERE Name = 'AdmissionID' AND Object_ID = Object_ID(#object))
BEGIN
SET #cmdstring = 'DELETE FROM ' + #object + ' WHERE AdmissionID = ' + #admissionID
Exec(#cmdstring)
END
SET #loop += 1
END
Unfortunately, executing this procedure doesn't delete any records from anywhere. I suspected the problem might be to do with my parameter not being enclosed in quotation marks in the line where I build up the cmdstring string, but even attempting to mitigate for this doesn't seem to have worked.
Any advice on where I'm going wrong, or if there's a simpler way to do this? I'd prefer not to use cascading deletes on my PK-FK relationships.
UPDATE
Following the advice of Luv I found that his query does return a very nice list of the exact commands I want to use. I thought it'd be easy from there, but I still can't make this work. The new SP I'm trying is as follows:
ALTER PROCEDURE TEST
#admissionID VARCHAR(10)
AS
DECLARE #loop INT
DECLARE #cmd VARCHAR(500)
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
BEGIN
DROP TABLE #TEMP
END
SELECT 'DELETE FROM '+ C.TABLE_NAME +' WHERE AdmissionID = '+#admissionID AS cmd
,ROW_NUMBER() OVER (ORDER BY 'DELETE FROM '+ C.TABLE_NAME +' WHERE AdmissionID = '+#admissionID) ROWID
INTO #TEMP
FROM INFORMATION_SCHEMA.COLUMNS C
INNER JOIN INFORMATION_SCHEMA.TABLES T on C.TABLE_NAME=T.TABLE_NAME
WHERE C.COLUMN_NAME='AdmissionID'
AND T.TABLE_TYPE='BASE TABLE'
WHILE #loop <= (SELECT MAX(ROWID) FROM #TEMP)
BEGIN
SELECT #cmd = cmd
FROM #TEMP
WHERE ROWID = #loop
EXEC(#cmd)
SET #loop += 1
END
Copy the result from the Below Query and Run the Query.
declare #yourvalue varchar
set #yourvalue=''
select 'DELETE FROM '+ C.TABLE_NAME +' WHERE AdmissionID = '+#yourvalue 
from INFORMATION_SCHEMA.COLUMNS C
INNER JOIN INFORMATION_SCHEMA.TABLES T on C.TABLE_NAME=T.TABLE_NAME
where C.COLUMN_NAME='AdmissionID'
and T.TABLE_TYPE='BASE TABLE'
You can even make an SP and execute Dynamically too

Trigger with multiple tables

I am trying to make a trigger that are going to take a value from one table and then make a subtraction with an other value in another table.
The trigger are going to operate in a table could (deposit), and then I would like to take the value that the user just inserted to the deposit table and subtract it with a value in another table(account). I have no problem to get following code to compile:
CREATE OR REPLACE TRIGGER aifer_insättning
AFTER INSERT
ON deposit
FOR EACH ROW
WHEN (new.belopp is not null)
BEGIN
UPDATE account SET SALDO = saldo + :new.belopp;
end;
/
But of course this update al the rows in the table account. I have tried everything to make it work with only one row, but I can´t get it. Does anyone have some suggestion hove I can resolve this?
Here is my tables:
SQL> desc account
Name Null? Type
----------------------------------------- -------- ------------
KNR NOT NULL NUMBER(8)
KTNR NOT NULL NUMBER(6)
REGDATUM NOT NULL DATE
SALDO NUMBER(10,2)
SQL> desc deposit
Name Null? Type
----------------------------------------- -------- ------------
RADNR NOT NULL NUMBER(9)
PNR NOT NULL VARCHAR2(11)
KNR NOT NULL NUMBER(8)
BELOPP NUMBER(10,2)
DATUM NOT NULL DATE
Presuming that the common column KNR is the primary key of ACCOUNT, I think the code you want is:
CREATE OR REPLACE TRIGGER aifer_insättning
AFTER INSERT
ON deposit
FOR EACH ROW
WHEN (new.belopp is not null)
BEGIN
UPDATE account a
SET a.SALDO = a.saldo + :new.belopp
WHERE a.knr - :new.knr;
end;
/
Note that I have added a table alias to make the code clearer.
Wouldn't you just need to add a where clause to the update statement in the trigger body? Or am I missing something?
delimiter $$
create trigger ss_ss before update on employee
for each row
begin
update consignee set
created_by = NEW.employee_pin
where created_by = OLD.employee_pin;
end
$$
delimiter ;
NOTE-> employee and consignee are two tables
**
Generate Triggers for all Tables
**
Well, I did this originally to generate triggers for all tables in a database to audit data changes, and that is simple enough, just move the entire row from the deleted table to a mirrored audit table.
But someone wanted to track activity on tables, so it's a little more simple. Here we create one log table, and any time a DML operation occurs, it is written there.
Enjoy
USE Northwind GO
CREATE TABLE LOG_TABLE (
Add_dttm datetime DEFAULT (GetDate()),
TABLE_NAME sysname,
Activity char(6)
);
GO
DECLARE
#sql varchar(8000),
#TABLE_NAME sysname SET NOCOUNT ON
SELECT #TABLE_NAME = MIN(TABLE_NAME)
FROM INFORMATION_SCHEMA.Tables
WHILE #TABLE_NAME IS NOT NULL
BEGIN
SELECT #sql = 'CREATE TRIGGER
[' + #TABLE_NAME + '_Usage_TR]
ON [' + #TABLE_NAME +'] '
+ 'FOR INSERT, UPDATE, DELETE AS '
+ 'IF EXISTS (SELECT * FROM inserted)
AND NOT EXISTS (SELECT * FROM deleted) '
+ 'INSERT INTO LOG_TABLE
(TABLE_NAME,Activity)
SELECT ''' + #TABLE_NAME + ''', ''INSERT''' + ' '
+ 'IF EXISTS (SELECT * FROM inserted)
AND EXISTS (SELECT * FROM deleted) '
+ 'INSERT INTO LOG_TABLE (TABLE_NAME,Activity) SELECT '''
+ #TABLE_NAME + ''', ''UPDATE''' + ' '
+ 'IF NOT EXISTS (SELECT * FROM inserted)
AND EXISTS (SELECT * FROM deleted) '
+ 'INSERT INTO LOG_TABLE (TABLE_NAME,Activity)
SELECT ''' + #TABLE_NAME + ''',
''DELETE''' + ' GO'
SELECT #sql EXEC(#sql)
SELECT #TABLE_NAME = MIN(TABLE_NAME)
FROM INFORMATION_SCHEMA.Tables
WHERE TABLE_NAME > #TABLE_NAME
END
SET NOCOUNT OFF