I’m trying to write a script to purge all the tables (and views and functions and procedures) from the current dbo schema in the current database.
Bolting together what I have learned from other sources, I get something like this:
USE whatever_db;
DECLARE @object NVARCHAR(255);
DECLARE curses CURSOR FOR
SELECT name
FROM sysobjects
WHERE type = 'U';
DECLARE @sql NVARCHAR(MAX);
OPEN curses;
FETCH NEXT FROM curses INTO @object;
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT N'SELECT * FROM '+@object;
SET @sql=N'DROP TABLE IF EXISTS @thing;';
EXECUTE sp_executesql @sql, N'@thing VARCHAR(MAX)', @thing=@object;
FETCH NEXT FROM curses INTO @object;
END;
CLOSE curses;
DEALLOCATE curses;
I understand that sysobjects has a list of objects, with type U naturally being the tables.
Using a cursor, I loop through these objects and try to execute a dynamic statement in the form:
DROP TABLE IF EXISTS something;
From Microsoft’s support site https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql?view=sql-server-ver16 I understand that we can execute a parameterised string with sp_executesql, but it’s not exactly clear exactly how the following parameters work, though it appears that I follow the SQL string with a declaration and an assignment.
Except this doesn’t seem to work. I get a list of messages such as:
Msg 0, Level 0, State 1, Line 9
SELECT * FROM thingsMsg 102, Level 15, State 1, Line 1
Incorrect syntax near '@thing'
The PRINT statement outputs what I’m expecting, but the rest is, apparently, wrong.
What is the correct way to use sp_executesql with parameters to drop tables?
Update
From the comments below I gather that the parameterised version is for DML or DSL statements and not DDL. Fair enough, since, in real life it’s the DML and DSL where you might attempt to insert values from outside.
It appears that the solution is simply to concatenate the value:
BEGIN
SET @sql = N'DROP TABLE IF EXISTS ' + quotemane(@object, '"');
EXECUTE sp_executesql @sql;
FETCH NEXT FROM curses INTO @object;
END;
I’ve also noted that using sys.objects is better than sysobjects, and I also reverse the order to drop dependent tables before the others:
DECLARE curses CURSOR FOR
SELECT name
FROM sys.objects
WHERE schema_id=1 AND is_ms_shipped=0 AND type='U'
ORDER BY object_id DESC;
SET @sql=N'DROP TABLE IF EXISTS @thing;';should be thisSET @sql=N'DROP TABLE IF EXISTS ' + @thing;i.e. the dynamic string you create must now be static SQL.QUOTENAME(@thing)