Create trigger if column exists in SQL Server - sql

How do I go about looping through all the tables that have the updated_at column? I figured out how to find all the tables that have the column using information_schema.columns like this:
SELECT TABLE_NAME FROM information_schema.columns WHERE COLUMN_NAME = 'updated_at'
But I have no idea how I would go about looping through all the tables to create the trigger to update the updated_at column to the current time with SYSDATETIMEOFFSET() when the row is updated?
EDIT: So I managed to figure out the iterating now using a cursor. but now im kind of confused about updating the date. So if I set an after update trigger to update the updated_at date, wouldn't that trigger an infinite loop?

Learning how to use system tables to help you write code dynamically is going to be important.
Also looping to do this is the worst thing ever.
SELECT
'CREATE TRIGGER schema.triggername
ON schema.tablename
AFTER UPDATE
AS
IF TRIGGER_NESTLEVEL() > 1
RETURN
UPDATE schema.tablename
SET RowUpdated = SYSDATETIMEOFFSET()
WHERE UniqueIdentifier/PrimaryKey IN
(
SELECT UniqueIdentifier/PrimaryKey
FROM Inserted
)
;
GO'
, 'UPDATE '+t.name+' SET '+c.name+' = SYSDATETIMEOFFSET(); '
FROM
sys.Tables t
INNER JOIN sys.columns c ON t.object_id =c.object_id
WHERE
c.name = 'RowLoaded'

#DougCoat's answer helped me come up with the create trigger and the looping was from this answer
DECLARE #TableName varchar(30)
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT TABLE_NAME FROM information_schema.columns WHERE COLUMN_NAME = 'updated_at'
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC(
'CREATE TRIGGER tr_update_timestamp_' + #TableName + ' ON [' + #TableName +
'] AFTER UPDATE
AS
BEGIN
UPDATE [' + #TableName +
'] SET updated_at = SYSDATETIMEOFFSET()
WHERE [' + #TableName + '].id IN
(
SELECT i.id
FROM Inserted i
)
END'
)
FETCH NEXT FROM MY_CURSOR INTO #TableName
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR

Related

Stored procedure to drop the column in SQL Server

I created many tables and I have noticed that I have created one useless column in all the tables. I want to create a stored procedure which will drop one specific column and can be useful in all the column.
I created this stored procedure but I'm getting an error. Help me please
You cannot parametrize table and column names with parameters - those are only valid for values - not for object names.
If this is a one-time operation, the simplest option would be to generate the ALTER TABLE ... DROP COLUMN ... statements in SSMS using this code:
SELECT
'ALTER TABLE ' + SCHEMA_NAME(t.schema_id) + '.' + t.Name +
' DROP COLUMN Phone;'
FROM
sys.tables t
and then execute this code in SSMS; the output from it is a list of statement which you can then copy & paste to a new SSMS window and execute.
If you really want to do this as a stored procedure, you can apply the same basic idea - and then just use code (a cursor) to iterate over the commands being generated, and executing them - something like this:
CREATE PROCEDURE dbo.DropColumnFromAllTables (#ColumnName NVARCHAR(100))
AS
BEGIN
DECLARE #SchemaName sysname, #TableName sysname
-- define cursor over all tables which contain this column in question
DECLARE DropCursor CURSOR LOCAL FAST_FORWARD
FOR
SELECT
SchemaName = s.Name,
TableName = t.Name
FROM
sys.tables t
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
WHERE
EXISTS (SELECT * FROM sys.columns c
WHERE c.object_id = t.object_id
AND c.Name = #ColumnName);
-- open cursor and start iterating over the tables found
OPEN DropCursor
FETCH NEXT FROM DropCursor INTO #SchemaName, #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Stmt NVARCHAR(1000)
-- generate the SQL statement
SET #Stmt = N'ALTER TABLE [' + #SchemaName + '].[' + #TableName + '] DROP COLUMN [' + #ColumnName + ']';
-- execute that SQL statement
EXEC sp_executeSql #Stmt
FETCH NEXT FROM DropCursor INTO #SchemaName, #TableName
END
CLOSE DropCursor
DEALLOCATE DropCursor
END
This procedure should work.
It loops through all cols and then deletes the column where sum(col) is zero.
Take a Backup of the Table
alter procedure deletecolumnsifzero #tablename varchar(1000)
as
set nocount on
declare #n int
declare #sql nvarchar(1000)
declare #sum_cols nvarchar(1000)
declare #c_id nvarchar(100)
set #n = 0
declare c1 cursor for
select column_name from information_schema.columns
where
table_name like #tablename
--Cursor Starts
open c1
fetch next from c1
into #c_id
while ##fetch_status = 0
begin
set #sql=''
set #sql='select #sum_cols = sum('+#c_id+') from ['+#tablename+']'
exec sp_Executesql #sql,N'#sum_cols int out,#tablename nvarchar(100)',#sum_cols out,#tablename
if(#sum_cols = 0)
begin
set #n=#n+1
set #sql=''
set #sql= #sql+'alter table ['+#tablename+'] drop column ['+#c_id+']'
exec sp_executesql #sql
end
fetch next from c1
into #c_id
end
close c1
deallocate c1

SQL Server - changing definition of column that appears in multi tables

many of tables in my DB have a Boolean column 'IsDeleted'.
I need to alter the column in all tables, that the default value will be zero, and then update all old records with value null, to be with value zero.
There is a way to do it beside writing a script for every table?
Thanks,
This would be a good starting point to generate the CReate, Update and Rename scripts required. Advisory: TEST ON BACKUP OF DATABASE FIRST.
select
'ALTER TABLE dbo.' + O.Name + ' ADD IsDeletedNew bit default 0;
UPDATE dbo.' + O.Name + ' SET IsDeletedNew = 1 WHERE IsDeleted = 1;
UPDATE dbo.' + O.Name + ' SET IsDeletedNew = 0 WHERE IsDeleted = 0 OR IsDeleted IS NULL;
ALTER TABLE dbo.' + O.Name + ' DROP COLUMN IsDeleted;
EXECUTE sp_rename N''dbo.' + O.Name + '.IsDeletedNew'', N''Tmp_IsDeleted_1'', ''COLUMN''
EXECUTE sp_rename N''dbo.' + O.Name + '.Tmp_IsDeleted_1'', N''IsDeleted'', ''COLUMN'' '
from syscolumns C
Inner join sysobjects o on C.ID = O.ID
where c.name = 'IsDeleted'
First, I can set a default value for a boolean filed. It worked for me.
ALTER TABLE [dbo].<TableName> ADD DEFAULT 0 FOR IsDeleted
This is my script that sets default value for every 'IsDeleted' field that doesn't have a defualt value. It worked for me.
DECLARE #NAME VARCHAR(100)
DECLARE #SQL NVARCHAR(300)
DECLARE CUR CURSOR
FOR
SELECT t.name AS 'TableName'
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name = 'IsDeleted'
AND (SELECT object_definition(default_object_id) AS definition
FROM sys.columns
WHERE name ='IsDeleted'
AND object_id = object_id(t.name)) is null
OPEN CUR
FETCH NEXT FROM CUR INTO #NAME
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'ALTER TABLE [dbo].'+#NAME+' ADD DEFAULT 0 FOR IsDeleted'
--PRINT #SQL -- will print all the update scripts
EXEC Sp_executesql #SQL
FETCH NEXT FROM CUR INTO #NAME
END
CLOSE CUR
DEALLOCATE CUR
With so many tables, do the alter using dynamic SQL
declare #tab_name varchar(120)
declare #the_sql varchar(1000)
declare MyCursor cursor
for
select distinct table_name
from INFORMATION_SCHEMA.COLUMNS
where column_name = 'IsNumeric'
open MyCursor
fetch next from MyCursor into #tab_name
while ##fetchstatus = 0
begin
set #the_sql = 'alter table ' + #tab_name + ' add NewNumeric bit default 0'
execute (#the_sql)
fetch next from MyCursor into #tab_name
end
close MyCursor
deallocate MyCursor
rinse and repeat to updatethe values, delete the old column and then update the new column

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

I have the same column in multiple tables, and want to update that column in all tables to a specific value. How can I do this?

The column is "CreatedDateTime" which is pretty self-explanatory. It tracks whatever time the record was commited. I need to update this value in over 100 tables and would rather have a cool SQL trick to do it in a couple lines rather than copy pasting 100 lines with the only difference being the table name.
Any help would be appreciated, having a hard time finding anything on updating columns across tables (which is weird and probably bad practice anyways, and I'm sorry for that).
Thanks!
EDIT: This post showed me how to get all the tables that have the column
I want to show all tables that have specified column name
if that's any help. It's a start for me anyways.
If that's a one time task, just run this query, copy & paste the result to query window and run it
Select 'UPDATE ' + TABLE_NAME + ' SET CreatedDateTime = ''<<New Value>>'' '
From INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = 'CreatedDateTime'
You could try using a cursor : like this
declare cur cursor for Select Table_Name From INFORMATION_SCHEMA.COLUMNS Where column_name = 'CreatedDateTime'
declare #tablename nvarchar(max)
declare #sqlstring nvarchar(max)
open cur
fetch next from cur into #tablename
while ##fetch_status=0
begin
--print #tablename
set #sqlstring = 'update ' + #tablename + ' set CreatedDateTime = getdate()'
exec sp_executesql #sqlstring
fetch next from cur into #tablename
end
close cur
deallocate cur
You can use the Information_Schema.Columns to build update scripts for you.
Declare #ColName as nVarchar(100), #NewValue as nVarchar(50)
Set #ColName = 'Modified' -- 'your col name'
Set #NewValue = '2013-11-04 15:22:31' -- your date time value
Select 'Update ' + TABLE_NAME + ' set ' + COLUMN_NAME + ' = ''' + #NewValue + '''' From INFORMATION_SCHEMA.COLUMNS Where column_name = 'modified'

SQL Query to get table name and number of rows based on column name"

I have declared a Cursor to get table names and no of columns in that tables based on column names.Please find the below query table name is not get inserted.Please suggest.
Create table #t
(
tabname varchar(500),
NoOfRows bigint,
)
Declare #Namee Varchar(500)
Declare #GetName Cursor
Set #Getname = Cursor for
Select table_name from information_Schema.columns
where column_name='isactive'Open #Getname
Fetch Next From #Getname into #Namee
While ##Fetch_Status=0
Begin
--Print #Namee
insert into #t(tabname) SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME =' + #Namee + '
exec ('insert into #t(NoOfRows) Select count(*) from ' + #Namee + ' where isactive=0')
Fetch Next From #Getname into #Namee
End
Close #GetName
Deallocate #GetName
select * from #t
You can insert the table name and number of rows in a single INSERT:
EXEC('INSERT INTO #t
(tabname, NoOfRows)
SELECT '''+ #Namee +''', COUNT(*)
FROM ' + #Namee + '
WHERE isactive = 0')
What you have makes no link between the table name and the count, so it's unlikely you're missing a table but it is doubtful that the NoOfRows was actually associated with the table name in the record.
Here is a better way to get the tables you want (won't have some issues with catalog and schema overlap)
declare #colname varchar(max)
set #colname = 'isactive'
SELECT table_name from information_schema.tables t
join information_schema.columns c on t.table_catalog = c.table_catalog and
t.table_schema = c.table_schema and
t.table_name = c.table_name and
column_name = #colname
You are doing two inserts into your temporary table, one for the table name (With no count) and one for the count with no table name.
See OMG Ponies for the SQL to replace yours with and remove the insert with just a table name