Dynamic SQL Query to Drop table - sql-server-2008-r2

Looking for way to create some dynamic SQL to drop a table if it exists. However I can not seem to get the syntax correct. Here is the query so far (renamed fields for security)
DECLARE #TableNameNew NVARCHAR(MAX)
DECLARE #DynamicSQL2 NVARCHAR(MAX)
SET #TableNameNew = (SELECT 'tbl1_' + REPLACE(StaffCode,'.','') AS TableName
FROM tblEmployee WHERE (PCLogin = REPLACE(SYSTEM_USER, 'DOMAIN\', '')))
SET #DynamicSQL2 = 'IF OBJECT_ID(' + '''' + #TableNameNew + '''' + +','+'''U''' + ') IS NOT NULL DROP TABLE ' + #TableNameNew
EXEC #DynamicSQL2
This returns an error:
Could not find stored procedure 'IF OBJECT_ID('tbl1_ghewitt','U') IS NOT NULL DROP TABLE tbl1_ghewitt'.

Try
EXECUTE sp_executesql #DynamicSQL2
instead of
EXEC #DynamicSQL2

Related

SQL Server 2012: Dynamic crosstab in stored procedure

Im trying to use a stored procedure to create a pivot table between two declared variables #rvar (as rowvariable) and #cvar (as columnvariable). The point is to call the stored procedure from VBA using these two as dynamic input when executing the stored procedure.
My code has three parts:
creating test-data
declaring locals
finding names of columns in crosstab an storing in new local #sql1
executing crosstable with the pivotfunction using the names stored in #sql1.
My problem: The code below works but I would like to make it dynamic regarding the variable defining the column structure - currently set to "q10_1_resp" - so that I only have to declare the local #cvar and use that in part 3 (like in part 4). I have succeeded in making part 3 into a sql-string with subsequent execution but then the column names stored in #sql1 cannot be used in the code in part 4 (I guess it is a scope thing).
--Part 1
create table [user].[test]
(rowvar nvarchar(max),
q10_1_resp int,
q10_2_resp int)
GO
INSERT [user].[test]
VALUES ('PH',1,2),
('PH',2,3),
('EA',1,5),
('EA',5,4),
('PH',3,4),
('PH',6,6),
('EA',4,1),
('PH',5,3),
('PH',2,1)
GO
-- Part 2
declare #rvar as nvarchar(max) = 'rowvar'
declare #cvar as nvarchar(max) = 'q10_1_resp' --this input should be dynamic as well
declare #sql1 as nvarchar(max)= ''
declare #sql2 as nvarchar(max)= ''
-- Part 3
select #sql1 = #sql1 + [a].[col] + char(44)
from
(select distinct QUOTENAME(q10_1_resp) as [col]
from [user].[test]
group by q10_1_resp) as a
SET #sql1 = left(#sql1, len(#sql1) - 1)
-- Part 4
SET #sql2 = 'select ' +
+ #rvar + ','
+ #sql1
+ ' from (Select '
+ #rvar + ', '
+ #cvar
+ ' from [user].[test]) sq pivot(count('
+ #cvar
+ ') for '
+ #cvar + ' IN ('
+ #sql1
+ ')) as pt'
exec sp_executesql #sql2
After a great deal of trying to globalize the scalarvariable without success using a temporary table to store the string was the key. The temporary table created at the beginning of the stored procedure can be assigned and referenced the entire time of the procedure. Thus assigning within execution of #sql and then referencing the string at execution of #sql2. I hope it makes sense.
CREATE PROCEDURE [dbo].[sp_crosstab]
-- Add the parameters for the stored procedure here
#rvar nvarchar(max) = '',
#cvar nvarchar(max) = '',
#data nvarchar(max) = '',
#sql nvarchar(max) = '',
#sql2 nvarchar(max) = '',
#sql3 nvarchar(max)=''
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
create table #temp_crosstab
(
sqlstr nvarchar(max)
)
set #sql ='
declare #sql1 nvarchar(max) = char(00)
select #sql1 = #sql1 + [a].[col] + char(44)
from
(select distinct QUOTENAME(' + #cvar + ') as [col]
from ' + #data + '
group by ' + #cvar + ') as a
SET #sql1 = left(#sql1, len(#sql1) - 1)
insert into #temp_crosstab values (#sql1)'
execute sp_executesql #sql
select #sql3 = [sqlstr] from #temp_crosstab
set #sql2 = '
select ' + #rvar + char(44) +
#sql3 + 'from (Select '
+ #rvar + char(44) + ' '
+ #cvar
+ ' from ' + #data + ') sq pivot(count('
+ #cvar
+ ') for '
+ #cvar + ' IN ('+#sql3+')) as pt'
exec sp_executesql #sql2
END
GO

Listing all databases, all tables, and all columns on a SQL Server instance

I am trying to write a query that for a single SQL Server instance, lists all columns in all tables in all databases on the server instance. I have found examples that list all columns in all tables but you have to know the database name. I have found examples that list databases on a SQL Server instance but not the tables in the databases. Now I am trying to find something that combines the two, but I am not having much luck.
Would anyone know if such a thing exists or is it a manual process to bridge the gap?
Thanks,
Tom
You can use dynamic query. I did not find any other way.
declare #str varchar(max) = ''
;with dbs as (
select *
from sys.databases
where dbs.name not in ('master', 'tempdb', 'model', 'msdb')
)
select #str = #str + 'select ''' + dbs.name + ''', tbl.name, col.name from ' +
dbs.name + '.sys.tables tbl inner join ' +
dbs.name + '.sys.columns col ON col.object_id = tbl.object_id; '
from dbs
print #str
exec(#str)
Please try this and feed back with comments.
The requirement was to find out one particular table from all the database. This was not possible by visual inspection as it might take lots of time and human error was possible. She was aware of the system view sys.tables.
SELECT *
FROM sys.Tables
WHERE name LIKE '%Address%'
The limitation of query mentioned above is that it only searches in one database and user has to keep on changing database manually and run the query again. I wrote down following quick script which looks into all the database on the server and provides the database name, schema name and table containing searched word in its name.
CREATE PROCEDURE usp_FindTableNameInAllDatabase
#TableName VARCHAR(256)
AS
DECLARE #DBName VARCHAR(256)
DECLARE #varSQL VARCHAR(512)
DECLARE #getDBName CURSOR
SET #getDBName = CURSOR FOR
SELECT name
FROM sys.databases
CREATE TABLE #TmpTable (DBName VARCHAR(256),
SchemaName VARCHAR(256),
TableName VARCHAR(256))
OPEN #getDBName
FETCH NEXT
FROM #getDBName INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #varSQL = 'USE ' + #DBName + ';
INSERT INTO #TmpTable
SELECT '''+ #DBName + ''' AS DBName,
SCHEMA_NAME(schema_id) AS SchemaName,
name AS TableName
FROM sys.tables
WHERE name LIKE ''%' + #TableName + '%'''
EXEC (#varSQL)
FETCH NEXT
FROM #getDBName INTO #DBName
END
CLOSE #getDBName
DEALLOCATE #getDBName
SELECT *
FROM #TmpTable
DROP TABLE #TmpTable
GO
EXEC usp_FindTableNameInAllDatabase 'Address'
GO

Subtleties of SQL Server Variables

I have the following SQL Server stored procedure:
CREATE PROCEDURE ispsDcOhcAgg #TmpTableName NVARCHAR(50), #ListItem NVARCHAR(50)
AS
IF EXISTS (SELECT name
FROM sys.tables
WHERE name = #TmpTableName)
DROP TABLE #TmpTableName; -- This will not work.
GO
This will clearly not work (see the comment in the above snippit). The only (and very ugly) way I have found to get around this problem is to do the following
CREATE PROCEDURE ispsDcOhcAgg #TmpTableName NVARCHAR(50), #ListItem NVARCHAR(50)
AS
DECLARE #SQL NVARCHAR(4000)
SET #SQL = N'IF EXISTS (SELECT name ' +
N'FROM sys.tables ' +
N'WHERE name = N' + N'''' + #TmpTableName + N''') ' +
N'DROP TABLE ' + #TmpTableName + N';'
EXEC sp_executesql #SQL;
GO
which truly stinks and for large stored procedures, it's horrendous!
Is there another way of doing this that I don't know about?
Thanks for your time.
No, if you want to use a table name dynamically like this, you need to use dynamic SQL.
So you should make sure you don't open yourself up to nasty SQL injection risks!
Try something like this:
SET #SQL = 'IF EXISTS (SELECT name ' +
N'FROM sys.tables ' +
N'WHERE name = #TableName) ' +
N'DROP TABLE ' + QUOTENAME(#TmpTableName) + ';'
EXEC sp_executesql #SQL, N'#TableName sysname', #TmpTableName;
No, if you want to determine the table to be dropped at runtime, there is no alternative to dynamic SQL.
There is a slightly less ugly way: you only use dynamic SQL for the command that needs to be dynamic (the DROP command):
DECLARE #SQL NVARCHAR(100)
IF EXISTS (SELECT name
FROM sys.tables
WHERE name = #TmpTableName)
BEGIN
SET #SQL = N'DROP TABLE ' + #TmpTableName + N';'
EXEC sp_executesql #SQL;
END

Is there a better way to programatically access tables using SQL

Using MS SQL2000 at present if that makes any difference.
Is there a better way than the method below to be able to programatically access a table ?
declare #tableName as varchar(50)
declare #sql varchar(4000)
set #tableName = 'User'
print #tableName
If EXISTS(
select TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE table_name = ''+#TableName+''
)
BEGIN
set #sql = 'select * from [' + #tableName + ']'
exec(#sql)
end
Essentially Im trying to create a simple Mapping tool for CRUD operations so that I need only one Sproc for each operation, and I can pass in my parameterised object, a table name and let the database do the rest. This is purely for my own personal education, hence why Im not using an established framework, so if there are any major gotcha's with my idea or the code above, I'd appreciate knowing as well.
Thanks
This is complete example to create a SP by follow your initial code:
CREATE PROCEDURE dbo.CustomSelect (#tableName as varchar(50))
AS
SET NOCOUNT ON
DECLARE #sql varchar(4000)
If EXISTS(
select TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE table_name = #tableName
)
BEGIN
set #sql = 'select 1 as Found, * from [' + #tableName + ']'
exec(#sql)
END
ELSE
BEGIN
select 0 as Found
END
This SP always return a recordset so you can check the value of the field FOUND to know if the table exist or not
usage:
EXEC CustomSelect 'User'
Hope it helps

How to execute the stored procs from a dynamically generated query + sql server

I have a query that is dynamically fetching the stored proc names from all the databases.
Now I want to execute the stored procs and store the result in a temptable or table variable.
How to do that. Here is my SP so far
Declare #GetDBNames sysname
Declare #DynSql nvarchar(max)
Declare DBNames cursor for
Select '['+name+']' from master.dbo.sysdatabases
open DBNames
FETCH NEXT FROM DBNames into #GetDBNames
WHILE ##FETCH_STATUS=0
BEGIN
SET #DynSql = '
Select Specific_Catalog as Database_Name, Routine_Name as ''Stored Procedure Name''
From '+ #GetDBNames+'.Information_Schema.Routines '
EXEC (#DynSql)
FETCH NEXT FROM DBNames into #GetDBNames
END
Close DBNames
Deallocate DBNames
Please help.
Thanks in advance
Here's my soluton. Cursors are evil :)
Declare #DynSql nvarchar(max)
declare #result table ([Database_Name] nvarchar(128), [Stored Procedure Name] sysname)
SET #DynSql = ''
select #DynSql = #DynSql + '
Select SPECIFIC_CATALOG COLLATE DATABASE_DEFAULT as Database_Name, ROUTINE_NAME COLLATE DATABASE_DEFAULT as [Stored Procedure Name]
From ' + NAME + '.INFORMATION_SCHEMA.ROUTINES' + CHAR(13) + 'union all' + CHAR(13)
from master.dbo.sysdatabases
--remove last "union all"
SET #DynSql = left(#DynSql, LEN(#DynSql) - 10)
insert #result
exec sp_executesql #DynSql
select * from #result
INSERT [TableName] EXEC (#DynSql)
Note that if you introduce data you may want to use sp_ExecuteSQL, and you probably want to use [/] object-name escaping.