Adding a column to all user tables in t-sql - sql

I need to add a delete flag column to all 40 user tables in a database. I could write a script to loop through sys.tables, but I thought I'd check and see if anyone has either a better solution, or pre-created sql for this scenario.

There is an undocumented but well known stored procedure sp_msforeachtable:
exec sp_msforeachtable 'alter table ? add flag bit not null default 0';

No, it's a manual loop.
Or you could build up a single SQL statement of course...
SELECT
'ALTER TABLE ' + T.name + ' ADD foo int NULL'
FROM
sys.tables AS T
WHERE
T.is_ms_shipped = 0
Or the undocumented
EXEC sys.sp_MSforeachtable 'ALTER TABLE ? ADD foo int NULL'

Related

Drop all constraints from tables with a certain prefix

I'm trying to delete certain tables from my database that have a certain prefix using SQL Server 2014, these tables have constraints of course, so I looked for the equivalent of SET foreign_key_checks = 0 in MySQL and so far i tried this :
Attempt
Running
EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'
then drop all tables with this generated script :
SELECT 'drop table ' + table_name
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'myprefix%'
then
EXEC sp_msforeachtable 'ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL'
Result
Some tables were deleted, and some threw an error
... is referenced by a FOREIGN KEY constraint.
which is weird because I thought that the stored procedure I ran disabled them.
Is there a way to delete these constraints all-together? Since I have the scripts to create them again, is there a way I can delete the constraints from sys.foreign_keys but only from the tables that have the prefix?
Edit - Context
I was working in isolation on an existing module that's part of our huge web app, now it's time to include the changes I made and sync them with the dev database, I was asked to generate the scripts to update it for all the tables, views, stored procedures.. etc that I created.
So, instead of doing scripts that add columns to existing tables, I though it'd be much easier to just drop the tables, and then re-create them, data is not important here, it's all dummy data I used while testing.
Since you mentioned already having the scripts required to regenerate the foreign keys, here's a way to generate and execute the drops. You might want to add some error handling.
declare #sql varchar(max);
declare c cursor local fast_forward for
select concat
(
'alter table ',
quotename(s.name),
'.',
quotename(t.name),
' drop constraint ',
quotename(fk.name)
)
from sys.foreign_keys fk
join sys.tables t on t.object_id = fk.parent_object_id
join sys.schemas s on t.schema_id = s.schema_id
where t.is_ms_shipped = 0
and t.name like 'myprefix%';
open c;
fetch next from c into #sql;
while (##fetch_status = 0)
begin
print #sql;
--exec(#sql); uncomment after checking output and run again (or just run output manually)
fetch next from c into #sql;
end

Indexing same table in multiple databases

I have almost 150 databases with all the same tables. I know its bad but I don't have control over it. I'm trying to improve performance with some indexes. I know what the indexes should be but I need to build them on the same tables in every database. Is there a way to do this bsides creating them all separately?
I had a similar situation a while back so I came up with this code. You can use dynamic SQL with sp_MSforeachdb to loop through your databases. I've excluded the system databases below but you can include/exclude databases as you like in that first IF.
This code will check each database for your specific table as well as checking to see if that index already exists on that table. If not, it creates it. I included a RAISERROR to show the progress through the databases in SSMS messages. Just change the table/index names below and update the CREATE INDEX statement as appropriate for you.
DECLARE #command varchar(1000)
SELECT #command = 'IF ''?'' NOT IN(''master'', ''model'', ''msdb'', ''tempdb'')
BEGIN USE ?
EXEC(''
DECLARE #DB VARCHAR(200)
SET #DB = DB_NAME()
RAISERROR (#DB, 10, 1) WITH NOWAIT
IF OBJECT_ID(''''dbo.TableName'''', ''''U'''') IS NOT NULL
BEGIN
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name=''''IX_TableName'''' AND object_id = OBJECT_ID(''''TableName''''))
BEGIN
CREATE INDEX [IX_TableName] ON TableName (indexColumn)
END
END
'') END'
EXEC sp_MSforeachdb #command

Delete all tables in SQL Server database except few

I have around 50+ table in my database now what I want is drop all the tables in database except few.
now what I know is sys.tables is a table that list all tables so initially I ran a query like this
delete from sys.tables where name like '%DynamicSurgery' (or any other condition)
thinking that it might work. But as I expected it throws an error as
Ad hoc updates to system catalogs are not allowed.
Please tell me if there is a way to delete multiples in SQL Server?
You can use dynamic query to DROP the required tables:
DECLARE #ExecSQL AS NVARCHAR (MAX) = '';
SELECT #ExecSQL = #ExecSQL +
'DROP TABLE ' + QUOTENAME(S.name) + '.' + QUOTENAME(T.name) + '; '
FROM sys.tables T
JOIN sys.schemas S ON S.schema_id = T.schema_id
WHERE T.name LIKE '%DynamicSurgery'
--PRINT #ExecSQL
EXEC (#ExecSQL)
EXEC sys.sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL';
EXEC sp_MSforeachtable 'IF OBJECT_ID(''?'') NOT IN (
ISNULL(OBJECT_ID(''[dbo].[Table1]''),0),
ISNULL(OBJECT_ID(''[dbo].[Table2]''),0)
)
DELETE FROM ?';
EXEC sys.sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL';
select the table by clicking on it
press the delete button and hit enter.
Take note : if there is any dependencies(Foreign Key), the table will not be deleted

MSSQL Loop through list of tables to perform alter statement

My SQL is quite limited and I have a number of databases within my server, I'm wondering whether its possible to write an SQL Query to loop through a listing of table names and then alter a particular table within the database name to modify a table in that database ?
Im simply wishing to add a new column to a table called site_settings.
Does MSSQL have this ability ?
You can use the script below. It returns an alter statement for each user table (you need to change your new column type as you didn't specify it) and then executes the query.
declare #sql nvarchar(max) = ''
select #sql = #sql + 'alter table ' + name + ' add site_settings int null;'
from sys.tables where type ='U'
exec sp_executesql #sql

SQL Server 2008: create trigger across all tables in db

Using SQL Server 2008, I've created a database where every table has a datetime column called "CreatedDt". What I'd like to do is create a trigger for each table so that when a value is inserted, the CreatedDt column is populated with the current date and time.
If you'll pardon my pseudocode, what I'm after is the T-SQL equivalent of:
foreach (Table in MyDatabase)
{
create trigger CreatedDtTrigger
{
on insert createddt = datetime.now;
}
}
If anyone would care to help out, I'd greatly appreciate it. Thanks!
As #EricZ says, the best thing to do is bind a default for the column. Here's how you'd add it to every table using a cursor and dynamic SQL:
Sure, You can do it with a cursor:
declare #table sysname, #cmd nvarchar(max)
declare c cursor for
select name from sys.tables where is_ms_shipped = 0 order by name
open c; fetch next from c into #table
while ##fetch_status = 0
begin
set #cmd = 'ALTER TABLE ' + #table + ' ADD CONSTRAINT DF_' + #table + '_CreateDt DEFAULT GETDATE() FOR CreateDt'
exec sp_executesql #cmd
fetch next from c into #table
end
close c; deallocate c
No need to go for Cursors. Just copy the result of below Query and Execute.
select distinct 'ALTER TABLE '+ t.name +
' ADD CONSTRAINT DF_'+t.name+'_crdt DEFAULT getdate() FOR '+ c.name
from sys.tables t
inner join sys.columns c on t.object_id=c.object_id
where c.name like '%your column name%'
Here's another method:
DECLARE #SQL nvarchar(max);
SELECT #SQL = Coalesce(#SQL + '
', '')
+ 'ALTER TABLE ' + QuoteName(T.TABLE_SCHEMA) + '.' + QuoteName(T.TABLE_NAME)
+ ' ADD CONSTRAINT ' + QuoteName('DF_'
+ CASE WHEN T.TABLE_SCHEMA <> 'dbo' THEN T.Table_Schema + '_' ELSE '' END
+ C.COLUMN_NAME) + ' DEFAULT (GetDate()) FOR ' + QuoteName(C.COLUMN_NAME)
+ ';'
FROM
INFORMATION_SCHEMA.TABLES T
INNER JOIN INFORMATION_SCHEMA.COLUMNS C
ON T.TABLE_SCHEMA = C.TABLE_SCHEMA
AND T.TABLE_NAME = C.TABLE_NAME
WHERE
C.COLUMN_NAME = 'CreatedDt'
;
EXEC (#SQL);
This yields, and runs, a series of statements similar to the following:
ALTER TABLE [schema].[TableName] -- (line break added)
ADD CONSTRAINT [DF_schema_TableName] DEFAULT (GetDate()) FOR [ColumnName];
Some notes:
This uses the INFORMATION_SCHEMA views. It is best practice to use these where possible instead of the system tables because they are guaranteed to not change between versions of SQL Server (and moreover are supported on many DBMSes, so all things being equal it's best to use standards-compliant/portable code).
In a database with a case-sensitive default collation, one MUST use upper case for the INFORMATION_SCHEMA view names and column names.
When creating script it's important to pay attention to schema names and proper escaping (using QuoteName). Not doing so will break in someone's system some day.
I think it is best practice to put the DEFAULT expression inside parentheses. While no error is received without it in this case, with it, if the function GetDate() is parameterized and/or ever changed to a more complex expression, nothing will break.
If you decide that column defaults are not going to work for you, then the triggers you imagined are still possible. But it will take some serious work to manage whether the trigger already exists and alter or create it appropriately, JOIN to the inserted meta-table inside the trigger, and do it based on the full list of primary key columns for the table (if they exist, and if they don't, then you're out of luck). It is quite possible, but extremely difficult--you could end up with nested, nested, nested dynamic SQL. I have such automated object-creating script that contains 13 quote marks in a row...