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
Related
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
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
I have a cursor that I'm using to find NULL columns in a database. I'm using this to eliminate NULL Columns from an upload of this data to Salesforce using dbAMP. I'd like to modify this to spool the results into a Table and include the Table Name and Column name.
declare #col varchar(255), #cmd varchar(max)
DECLARE getinfo cursor for
SELECT c.name FROM sys.tables t JOIN sys.columns c ON t.Object_ID =
c.Object_ID
WHERE t.Name = 'Account'
OPEN getinfo
FETCH NEXT FROM getinfo into #col
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #cmd = 'IF NOT EXISTS (SELECT top 1 * FROM Account WHERE [' + #col +
'] IS NOT NULL) BEGIN print ''' + #col + ''' end'
EXEC(#cmd)
FETCH NEXT FROM getinfo into #col
END
CLOSE getinfo
DEALLOCATE getinfo
I've have not had any success in modifying this cursor to put results in a table. Any guidance would be appreciated.
Make the Print a Select then Insert into (tbl with same column definition).
Create a table with the same columns in the same order.
Then put an Insert into yourtable(your columns in the same order as output from the exec().
Any change in table columns in the future may break this. The table and the query should have the same columns. If you are cautious and control the order of columns in the select and insert, it shouldn't matter about the table column order, but it is still good practice imho.
Example (insert into table with dynamic sql)
if object_id('dbo.ColumnMatch','U') is not null drop table dbo.ColumnMatch;
create table dbo.ColumnMatch (
id int identity(1,1) not null primary key
,column_name varchar(512)
);
declare #col varchar(256) = 'This Column Name'
declare #s varchar(max) = 'select ''' + #col + '''';
insert into ColumnMatch (column_name)
exec(#s);
select * from ColumnMatch;
Not Print but select and fix the Insert Into statement. :)
if object_id('dbo.ColumnMatch','U') is not null drop table dbo.ColumnMatch;
create table dbo.ColumnMatch (
id int identity(1,1) not null primary key
,column_name varchar(512)
);
declare #col varchar(255), #cmd varchar(max)
DECLARE getinfo cursor for
SELECT c.name FROM sys.tables t JOIN sys.columns c ON t.Object_ID =
c.Object_ID
WHERE t.Name = 'Account'
OPEN getinfo
FETCH NEXT FROM getinfo into #col
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #cmd = 'IF NOT EXISTS (SELECT top 1 * FROM Account WHERE [' + #col +
'] IS NOT NULL) BEGIN select ''' + #col + ''' column_name end'
Insert into ColumnMatch (column_name)
EXEC(#cmd)
FETCH NEXT FROM getinfo into #col
END
CLOSE getinfo
DEALLOCATE getinfo
select * from ColumnMatch;
All database tables have a UserId field of [uniqueidentifier] type.
I need to query the entire database and get the list of tables that have UserId set to a specific value.
Right now I achieved this by using cursor and the results are horrible and are difficult to read. How can I improve this query to retrieve back a clear list with tables and count of record that have UserId set to a specific value, instead of using this:
DECLARE #TableName VARCHAR(127);
DECLARE #Value VARCHAR(512);
DECLARE #SqlCommand varchar(1000)
--Use cursor to loop through database tables that contain UserId column
DECLARE db_cursor 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 = 'UserId';
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO #TableName;
WHILE ##FETCH_STATUS = 0
BEGIN
--Check if the next table has any UserId matching the where clause
EXEC('SELECT COUNT(UserId) , ''' + #TableName + ''' FROM ' + #TableName + ' WHERE UserId = ''FF13ACCA-022C-4296-AB3D-A35700E35BB3''');
FETCH NEXT FROM db_cursor INTO #TableName;
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;
You made all the difficult part, just put the value in a temp table and select them once you've finished.
DECLARE #TableName VARCHAR(127);
DECLARE #Value VARCHAR(512);
DECLARE #SqlCommand varchar(1000)
--Creta temp table
CREATE TABLE #Results (Number int, Tablename sysname)
--Use cursor to loop through database tables that contain UserId column
DECLARE db_cursor 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 = 'UserId';
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO #TableName;
WHILE ##FETCH_STATUS = 0
BEGIN
--Check if the next table has any UserId matching the where clause
EXEC('INSERT INTO #Results (Number, ''' + #TableName + ''') SELECT COUNT(UserId) , ''' + #TableName + ''' FROM ' + #TableName + ' WHERE UserId = ''FF13ACCA-022C-4296-AB3D-A35700E35BB3''');
FETCH NEXT FROM db_cursor INTO #TableName;
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;
SELECT * FROM #Results
DROP TABLE #Results
I cannot test it but this should be the way
I have an SQL Server 2008 database with many tables. I've been using the now lame datetime datatype and want to use the new and better datetime2. In most places where I have a datetime field, the corresponding column name is Timestamp. Is there anywhere to do a bulk change from datatime to datetime2?
Run this in Management Studio, copy the result and paste into new Query Window:
select 'ALTER TABLE ' + OBJECT_NAME(o.object_id) +
' ALTER COLUMN ' + c.name + ' DATETIME2 ' +
CASE WHEN c.is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
from sys.objects o
inner join sys.columns c on o.object_id = c.object_id
inner join sys.types t on c.system_type_id = t.system_type_id
where o.type='U'
and c.name = 'Timestamp'
and t.name = 'datetime'
order by OBJECT_NAME(o.object_id)
Data type alteration generally requires ALTER TABLE statements:
ALTER TABLE myTable ALTER COLUMN timestamp datetime2 [NOT] NULL
To change all the datetime columns into datetime2 in a given database & schema:
DECLARE #SQL AS NVARCHAR(4000)
DECLARE #table_name AS NVARCHAR(255)
DECLARE #column_name AS NVARCHAR(255)
DECLARE #isnullable AS BIT
DECLARE CUR CURSOR FAST_FORWARD FOR
SELECT c.table_name,
c.column_name,
CASE WHEN c.is_nullable = 'YES' THEN 1 ELSE 0 END AS is_nullable
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.data_type = 'datetime'
AND c.table_catalog = 'your_database'
AND c.table_schema = 'your_schema'
-- AND c.table_name = 'your_table'
OPEN CUR
FETCH NEXT FROM CUR INTO #table_name, #column_name, #isnullable
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #SQL = 'ALTER TABLE ' + #table_name + ' ALTER COLUMN ' + #column_name + ' datetime2' + (CASE WHEN #isnullable = 1 THEN '' ELSE ' NOT' END) + ' NULL;'
EXEC sp_executesql #SQL
FETCH NEXT FROM CUR INTO #table_name, #column_name, #isnullable
END
CLOSE CUR;
DEALLOCATE CUR;
This would be a bit of a brute-force method, but you could always look up all columns of datatype datetime using the sys.columns view, grab the table name and column name, iterate over that list with a cursor, and for each entry generate an ALTER TABLE statement like so:
ALTER TABLE #tablename ALTER COLUMN #columnname datetime2
Then run said statement with EXEC. Obviously, you'd need to have permissions both to query sys.columns and to ALTER all of those tables...
Apologies there isn't more code in this answer - don't have a copy of SSMS on this machine, and can't remember the syntax for all of that from memory. :)
I would use a query window, and output all of the ALTER TABLE statements you need to perform this. Once you have them all generated, you can run the result against the database.
if you select from SYSCOLUMNS the names of the tables and fields that you want, you can generate the statements you need to change all of the columns in the database to datetime2.
ALTER TABLE {tablename} ALTER COLUMN {fieldname} datetime2 [NULL | NOT NULL]
You can do something like this:
SELECT
'ALTER TABLE [' + Table_Schema+'].['+Table_Name
+'] Alter Column ['+Column_Name+'] datetime2;'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE='datetime';
Now you have all the scripts necessary to make your bulk type change.
Ref: https://www.sqlservercentral.com/forums/topic/how-to-change-column-type-on-all-tables-of-a-certain-database