Dynamic sql script to generate list of drop table statement - sql

I've list of tables (around 100++) that need to be dropped from SQL Server. Below is the sample code that I would use
IF OBJECT_ID('dbo.DS_Area_TBL', 'U') IS NOT NULL
drop table dbo.DS_Area_TBL
Print 'dbo.DS_Area_TBL has been dropped'
I need to replace table name 100++ time with other table name. How to write a dynamic sql script that can auto generate list of queries?

DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = (
SELECT '
IF OBJECT_ID(''' + obj + ''', ''U'') IS NOT NULL BEGIN
DROP TABLE ' + obj + '
PRINT ''' + obj + ' has been dropped''
END
'
FROM (
SELECT obj = QUOTENAME(s.name) + '.' + QUOTENAME(o.name)
FROM sys.objects o
JOIN sys.schemas s ON o.[schema_id] = s.[schema_id]
WHERE o.[type] = 'U'
--AND o.name LIKE 'table%'
--AND s.name IN ('dbo')
) t
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
PRINT #SQL
--EXEC sys.sp_executesql #SQL
Output -
IF OBJECT_ID('[dbo].[user_data]', 'U') IS NOT NULL BEGIN
DROP TABLE [dbo].[user_data]
PRINT '[dbo].[user_data] has been dropped'
END
IF OBJECT_ID('[dbo].[formatter_options]', 'U') IS NOT NULL BEGIN
DROP TABLE [dbo].[formatter_options]
PRINT '[dbo].[formatter_options] has been dropped'
END

You could first generate script then execute with dynamic sql:
CREATE TABLE a(a INT);
CREATE TABLE b(a INT);
CREATE TABLE c(a INT);
CREATE TABLE d(a INT);
CREATE TABLE e(a INT);
CREATE TABLE tab(tab_name SYSNAME); -- here are table names stored
INSERT INTO tab VALUES ('a'),('b'),('c'),('d'),('e');
-- main part
DECLARE #sql NVARCHAR(MAX);
SELECT #sql = STUFF((SELECT ' ' + FORMATMESSAGE(
'IF OBJECT_ID(''%s'', ''U'') IS NOT NULL
BEGIN
DROP TABLE %s;
PRINT ''%s has been dropped '';
END
', QUOTENAME(tab_name),QUOTENAME(tab_name),QUOTENAME(tab_name))
FROM tab
FOR XML PATH('')), 1, 1, '');
PRINT #sql; -- for debug
EXEC [dbo].[sp_executesql]
#sql;
If you use version of SQL Server lower than 2012 you need to change FORMATMESSAGE with string concatenation +.
You could easily extend this script with custom schema and so on by modifying template:
'IF OBJECT_ID(''%s'', ''U'') IS NOT NULL
BEGIN
DROP TABLE %s;
PRINT ''%s has been dropped '';
END
'
Output:
IF OBJECT_ID('[a]', 'U') IS NOT NULL
BEGIN
DROP TABLE [a];
PRINT '[a] has been dropped ';
END
IF OBJECT_ID('[b]', 'U') IS NOT NULL
BEGIN
DROP TABLE [b];
PRINT '[b] has been dropped ';
END
IF OBJECT_ID('[c]', 'U') IS NOT NULL
BEGIN
DROP TABLE [c];
PRINT '[c] has been dropped ';
END
IF OBJECT_ID('[d]', 'U') IS NOT NULL
BEGIN
DROP TABLE [d];
PRINT '[d] has been dropped ';
END
IF OBJECT_ID('[e]', 'U') IS NOT NULL
BEGIN
DROP TABLE [e];
PRINT '[e] has been dropped ';
END
EDIT:
How it works:
XML + STUFF for string concatenation is common idiom with SQL Server, works like GROUP_CONCAT in MySQL. You can think about it as a way to combine multiple IF BEGIN END chunks into one string.
FORMATMESSAGE will replace %s with actual table names(quoted to avoid SQL Injection attacks)
PRINT is for debug to check generated query, can be commented
sp_executesql will execute SQL string

Related

Remove all tables from schema except specific ones in SQL?

I have a SQL database and I would like to remove almost all tables related to a specific schema except a couple of them. Therefore I think I would need to edit the following sql query (which removes all tables of a specific schema):
EXEC sp_MSforeachtable
#command1 = 'DROP TABLE ?'
, #whereand = 'AND SCHEMA_NAME(schema_id) = ''your_schema_name'' '
Would you be able to suggest a smart and elegant way so that I can add a list of tables that I would like to keep and remove everything else?
If you want to keep using sp_msforeachtable, pass in the set of tables names to keep using a temp table. Here's an example using the boo schema:
create schema boo;
create table boo.t(i int);
create table #keep (name sysname);
insert #keep values ('myFirsttable'), ('mySecondTable'), ('myThirdTable');
exec sp_msforeachtable
#command1='drop table ?; print ''dropped ?''',
#whereand = 'and schema_name(schema_id) = ''your_schema_name'' and object_name(object_id) not in (select name from #keep)';
But personally, I'd probably just write my own stored procedure with a cursor. It's harder to mess up.
Note that this solution expects you to put the tables you want to keep into the temp table. Charlieface's solution expects you to put the names of tables you want to drop into the table variable.
You could place a list of tables you want to delete stored in a table variable or Table-Valued Parameter #tables then you can simply execute dynamic SQL with it.
DECLARE #tables TABLE (tablename sysname);
INSERT #tables (tablename)
SELECT t.name
FROM sys.tables t
WHERE t.schema_id = SCHEMA_ID('your_schema_name');
DECLARE #sql nvarchar(max) =
(
SELECT STRING_AGG(CAST(
'DROP TABLE ' + QUOTENAME('your_schema_name') + '.' + QUOTENAME(tablename) + ';'
AS nvarchar(max)), '
' )
FROM #tables
);
EXEC sp_executesql #sql;
Alternatively, select it directly from sys.tables
DECLARE #sql nvarchar(max) =
(
SELECT STRING_AGG(CAST(
'DROP TABLE ' + QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) + ';'
AS nvarchar(max)), '
' )
FROM sys.tables t
WHERE t.schema_id = SCHEMA_ID('your_schema_name')
);
EXEC sp_executesql #sql;

Data Base trigger after table create on mssql

I got a data base trigger after table create.
CREATE TRIGGER tr_Timestamps_TableTriggersCreation
ON DATABASE
AFTER CREATE_TABLE
AS
BEGIN
SET NOCOUNT ON
DECLARE #tableName SYSNAME
DECLARE #schemaName SYSNAME = NULL
DECLARE #totalRows INT = 0;
SELECT #tableName = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','SYSNAME')
SELECT TOP 1 #schemaName = s.name
FROM sys.tables t
JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE t.name = #tableName
AND c.name = 'ID'
IF (#schemaName != '')
BEGIN
EXEC u_general.sp_Timestamps_TriggerForTableCreation #tableName, #schemaName, 'I'
EXEC u_general.sp_Timestamps_TriggerForTableCreation #tableName, #schemaName, 'U'
END
The idea behind it is to create 2 trigger for each table which is being created on that database.
My problem is as follow,
When someone uses the MSSQL 'editor' and uses the 'Design' option instead of writing a script for making changes such as marking a column for 'Allow Null or not', change the data type etc...
My Data base trigger counts it as a new table creation and launches the procedure which create the triggers for that table.
The end result is that it tries to create those triggers again and i get an error message that those triggers already exist.
ALTER PROCEDURE [u_general].[sp_Timestamps_TriggerForTableCreation] ( #tableName sysname, #schemaName sysname, #actionType char(1))
AS
DECLARE #TrigerName NVARCHAR(50)
DECLARE #AfterActionName NVARCHAR(50)
IF #actionType = 'I'
BEGIN
SET #TrigerName = 'tr_Timestamps_CaptureAfterInsert_' + #tableName
SET #AfterActionName = 'INSERT'
END
ELSE -- 'U'
BEGIN
SET #TrigerName = 'tr_Timestamps_CaptureAfterUpdate_' + #tableName
SET #AfterActionName = 'UPDATE'
END
DECLARE #SQLCommand nvarchar(max)=
'CREATE TRIGGER ' + #TrigerName +'
ON ' + #schemaName + '.' + #tableName +
' AFTER '+ #AfterActionName +'
AS
DECLARE #auditBody XML
DECLARE #RowID int
SELECT #RowID = INSERTED.ID FROM INSERTED
SELECT #auditBody =
''<Timestamps_Request>
<DataBaseName>'' + DB_NAME() + ''</DataBaseName>
<SchemaName>'' + ''' + #schemaName + ''' + ''</SchemaName>
<TableName>'' + ''' + #tableName + '''+ ''</TableName>
<RowID>'' + CAST(#RowID AS NVARCHAR(30)) + ''</RowID>
<Action>'' + '''+#actionType+''' + ''</Action>
</Timestamps_Request>''
EXEC u_general.sp_Timestamps_SendBrokerMessage #FromService = ''Timestamps_RequestService'',
#ToService = ''Timestamps_ProcessingService'',
#Contract = ''Timestamps_Contract'',
#MessageType = ''Timestamps_Request'',
#MessageBody = #auditBody ';
EXEC sp_executeSQL #SQLCommand
My question is,
What can i do to make sure that it wont happen every time someone decides to use the designer for 'altering' a table?
P.S. From my understanding it has something to do with the SSMS (causes drop and create? ) and the way it works, and this is why i dont have this issue while using a script to modify the table instead of the MSSQL designer.
Quick Note before direct answer.
Use DDL Events with CREATE_TABLE and Confirm you Don't using Alter_TABLE within your Trigger definitions, because CREATE_TABLE and Alter_TABLE is totally separated events.
Follow the next Demo for more Details.
Demo:-
Create table table1 (col1 int, col2 nvarchar(10) not null )
go
Create TRIGGER NoCreateNewTables ON DATABASE
FOR CREATE_TABLE
AS
Print 'Prevent Table Creation'
BEGIN
ROLLBACK;
END
GO
Lets Alter Table1 Via next:
ALTER TABLE table1 ALTER COLUMN col2 nvarchar(10) NULL
Result:-
Lets Create new table Table2 Via next:
Create table table2 (col1 int, col2 nvarchar(10) not null )
Result:-
What about SSMS
The direct answer starts here
SQL Server drops and recreates the tables while using SSMS designer in some cases:
Common cases:-
Add a new column.
Change the Allow Nulls setting for a column.
Change the column order in the table.
Change the column data type.
After some investigation , I noticed the Object_Name that altered via SSMS designer Starts with Tmp_.
so according to this info we can prevent alter table via SSMS designer by Create Trigger for Preventing altering table from SSMS while table recreated.
Demo:-
/* Create Trigger for Preventing altering table Via SSMS while table recreated */
Create TRIGGER TrgPreventAlterTableIfTableRecreatedViaSSMSDesign ON DATABASE
FOR CREATE_TABLE
AS
BEGIN
DECLARE
#eventInfo XML,
#ObjectName varchar(100)
SET
#eventInfo = EVENTDATA()
select #ObjectName = CONVERT(SYSNAME, #eventInfo.query('data(/EVENT_INSTANCE/ObjectName)'))
if (Left(#ObjectName,3) = 'Tmp')
begin
exec sp_addmessage 50001, 16,
N'Cannot modify Table Via SSMS, use [Alter Table] Code instead';
RAISERROR(50001,16,1)
exec sp_dropmessage 50001
rollback;
end
END
Now Try alter Table1 Via SSMS designer from NULL to NOT NULL
The next pop messages will be raised:-

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

Drop/Create Table (SQL - Procedure)

I'm trying to create a very simple procedure (with a simple code - I'm a beginner and I want to keep my code as simple as possible) which can Drop a table named "Table1" and Create a table named "Students" containing 3 columns: ID, Name, Mark
This is my code and it doesn't work at all:
CREATE PROCEDURE Drop_Create
#Table1 VARCHAR(MAX),
#Students VARCHAR(MAX)
AS
BEGIN
DECLARE #SQL VARCHAR(MAX);
SELECT #SQL = 'CREATE TABLE ' + #Students + '('SELECT #SQL = #SQL + '[ID] [int] IDENTITY(1,1) NOT NULL,[Name] [NVARCHAR(50)] NOT NULL,[Mark] [INT])';
SET #SQL = 'IF EXISTS DROP TABLE [' + #Table1 + ']'
PRINT #SQL;
EXEC #SQL;
END
GO
Are you intentionally using dynamic SQL? If the table names are static, ditch that approach. Otherwise:
Issue 1, Your if exists.. was totally wrong.
Issue 2, You overwrote #SQL without executing the first task
Issue 3, You had square brackets around [Varchar(50)]
Even as you have it now, it will break the second time you run it (it will try to create #Students again). I suspect you're confusing yourself with the logic somehow.
CREATE PROCEDURE Drop_Create
#Table1 VARCHAR(MAX),
#Students VARCHAR(MAX)
AS
BEGIN
-- Drop #table1
DECLARE #SQL VARCHAR(MAX) = 'IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''' + #Table1 + ''') AND type in (N''U'')) DROP TABLE [' + #Table1 + ']'
PRINT #SQL;
EXEC (#SQL);
-- Create #Students
SET #SQL = 'CREATE TABLE ' + #Students + '('
SET #SQL = #SQL + '[ID] [int] IDENTITY(1,1) NOT NULL,[Name] NVARCHAR(50) NOT NULL,[Mark] [INT])';
PRINT #SQL;
EXEC (#SQL);
END
GO

Drop group of stored procedures by name

I have group of stored procedures with names like 'somename_%'. Are there any way to delete that SP with one query, forexample
DROP PROCEDURE where name like
'somename_%'
.
This works for MSSQL 2005 +
DECLARE #DropScript varchar(max)
set #DropScript = ''
SELECT #DropScript = #DropScript + 'DROP PROCEDURE [' + schema_name(schema_id)+ '].' + '[' + name + ']
' FROM sys.procedures
where name like 'somename_%'
exec (#DropScript)
You can generate the DDL by querying the data dictionary. For example, in Oracle:
SELECT 'DROP PROCEDURE "'||owner||'"."'||object_name||'";'
FROM all_procedures
WHERE procedure_name IS NULL
AND lower(object_name) LIKE 'somename_%';
The way I always tend to do these kind of things is just extract the list procedures from the system tables using my critierion and then create the command list - either direct in sql e.g. SELECT 'DROP PROCEDURE ' + procName FROM system_procedures_table WHERE procName like... or in Excel.
In MS_Sql-Server you cn create a Statement with all the relevant Procedures to drop through (ab)using the "FOR XML PATH ('')" clause...
BEGIN TRANSACTION;
GO
CREATE PROC Test_1 AS
BEGIN;
PRINT '1'
END;
GO
CREATE PROC Test_2 AS
BEGIN;
PRINT '2'
END;
GO
SELECT * FROM sys.objects WHERE name LIKE 'Test%' AND TYPE = 'P';
DECLARE #Stmt NVARCHAR(MAX);
SET #Stmt = ( SELECT 'DROP PROC ' + STUFF (x.Stmt, 1, 2, SPACE(0))
FROM (SELECT ', ' + SCHEMA_NAME(Obj.Schema_ID) + CHAR(46) + Obj.Name
FROM sys.objects AS Obj
WHERE Obj.name LIKE 'Test%'
AND obj.TYPE = 'P'
FOR XML PATH ('')
) AS X (Stmt)
);
SELECT #Stmt;
EXEC sp_ExecuteSQL #Stmt;
SELECT * FROM sys.objects WHERE name LIKE 'Test%' AND TYPE = 'P';
ROLLBACK;
Check this TSQL script that automatically drops a list of stored procedures.