Creating and inserting into a DB using Dynamic SQL - tsql

To whoever reads this,
Basically using dynamic SQL, i am trying to create a database and then insert into it. Problem is that I cant find an alternative to 'GO' since when i run it i get an error saying the newly created DB doesn't exist. I know that it cant be used in dynamic sql as it is not recognized as T-SQL. I've also tried adding ";" but error still persists.
DECLARE #TargetDB sysname
DECLARE #TargetSchema sysname
DECLARE #TargetTable sysname
DECLARE #SourceDB sysname
DECLARE #SourceSchema sysname
DECLARE #SourceTable sysname
DECLARE #sql NVARCHAR(max)
SET #SourceDB = 'AYOOO'
SET #TargetDB = #SourceDB + 'SandBox'
SET #SourceTable = 'GUCCI'
SET #TargetTable = #SourceTable + 'SandBox'
SET #sql = N'CREATE DATABASE ' + #TargetDB + '; ' --create new db
SET #sql = #sql + N' SELECT * INTO ' + #TargetDB +'.dbo.'+#TargetTable+' FROM ' + #SourceDB+'.dbo.'+#SourceTable; --these 2 lines are for copying data into new tables
PRINT #sql
EXEC sys.sp_executesql #sql
Error:
Database 'AYOOOSandBox' does not exist.
I know its a stupid question but I'd like to find a good alternative to "GO" or a better practice for Dynamic SQL.
Thanks

After fixing your SQL to not be a huge injection issue, by properly quoting your objects, you need to separate the statements into 2 commands. Then you can CREATE your database in one command, and then INSERT in another.
DECLARE #TargetDB sysname,
#TargetSchema sysname,
#TargetTable sysname,
#SourceDB sysname,
#SourceSchema sysname,
#SourceTable sysname,
#sql nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SourceDB = 'AYOOO';
SET #TargetDB = #SourceDB + 'SandBox';
SET #SourceTable = 'GUCCI';
SET #TargetTable = #SourceTable + 'SandBox';
SET #sql = N'CREATE DATABASE ' + QUOTENAME(#TargetDB) + N';'; --create new db
EXEC sys.sp_executesql #sql;
SET #sql = N'SELECT *' + #CRLF +
N'INTO ' + QUOTENAME(#TargetDB) + N'.dbo.' + QUOTENAME(#TargetTable) + #CRLF +
N'FROM ' + QUOTENAME(#SourceDB) + N'.dbo.' + QUOTENAME(#SourceTable) + N';'; --copying data into new tables
EXEC sys.sp_executesql #sql;

Related

result of sp_executesql in a variable

How can I get the output of the below query in a variable without temp table?
DECLARE #Q1 NVARCHAR(300)
DECLARE #Q2 NVARCHAR(300)
DECLARE #Q3 NVARCHAR(300)
SET #Q1 = 'SELECT ' +' ' + #formfieldpath
SET #Q2 = 'FROM [TestDatabase].[details] WHERE id ' + '=''' + CAST(#id AS VARCHAR(10)) + '''';
SET #Q3 = #Q1 +' '+ #Q2
PRINT #Q3
EXEC sp_executesql #Q3
Tried 'How to get sp_executesql result into a variable?' and not able to get the results.
Assuming that you get a singleton value from your dynamic statement:
DECLARE #ID int, --Is set somewhere
#YourVariable nvarchar(30), --Use an appropriate data type
#formfieldpath sysname; --Is set somewhere
DECLARE #SQL nvarchar(MAX);
--I assume that the name [TestDatabase].[details] is wrong, as [TestDatabase] would be the name of the schema, not the database,
--and I ASSUME you haven't foolishy created a schema called "TestDatabase"
SET #SQL = N'SELECT #YourVariable = ' + QUOTENAME(#formfieldpath) + N' FROM dbo.Details WHERE id = #id';
--Use the correct datatype for #YourVariable
EXEC sys.sp_executesql #SQL, N'#id int, #YourVariable nvarchar(30) OUTPUT', #id, #YourVariable OUTPUT;
Never inject unsanitised values into a dynamic SQL statement. SQL injection is a huge problem that should have stopped existing over a decade ago. Dos and Don'ts of Dynamic SQL

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

Altering Multiple Tables at once

I'm trying to alter multiple SQL Server 2008 R2 tables at one time.
This is my code:
use DatabaseName
go
Declare #SchemaUsed varchar(20) = 'dbo'
create table #Tables
(
TableName varchar(100), Processed int
)
insert into #Tables
select top 1 table_name, 0
from INFORMATION_SCHEMA.TABLES
where TABLE_SCHEMA = #SchemaUsed
and table_type = 'Base Table'
and (TABLE_NAME like 'PM%' )
ORDER BY TABLE_NAME
DECLARE #TableName varchar(max)
DECLARE #SQL varchar(max)
WHILE EXISTS (select top 1 'x' from #Tables where Processed = 0)
BEGIN
SET #TableName = (select top 1 TableName from #Tables where Processed = 0)
Set #SQL = 'ALTER TABLE ' + #SchemaUsed + '.' + #TableName + ' ADD [identityID] bigint IDENTITY(1, 1) NOT NULL '
-- Set #SQL = '''' + #SQL + ''''
Print #SQL
EXEC #SQL;
update #Tables
set Processed = 1
where TableName = #TableName
END
drop table #Tables
I can't get this to work to save my life and get the following error:
Lookup Error - SQL Server Database Error: The name 'ALTER TABLE
dbo.PM1GTVLV ADD [identityID] bigint IDENTITY(1, 1) NOT NULL ' is not
a valid identifier.
I've also tried multiple string variations and using sp_executesql as well.
Can someone point out where I've gone wrong?
Try
DECLARE #SQL NVARCHAR(MAX);
EXEC sp_executesql #SQL;
Instead of EXEC #sql.
As an aside, this is a much more usable version of the same code IMHO:
DECLARE #SchemaUsed VARCHAR(20) = 'dbo';
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) + N'ALTER TABLE '
+ QUOTENAME(#SchemaUsed) + '.'
+ QUOTENAME(name) + ' ADD [identityID]
BIGINT IDENTITY(1,1) NOT NULL;'
FROM sys.tables
WHERE SCHEMA_NAME([schema_id]) = #SchemaUsed
AND name LIKE 'PM%';
PRINT #sql;
--EXEC sp_executesql #sql;
Or even better:
DECLARE #SchemaUsed VARCHAR(20) = 'dbo';
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) + N'ALTER TABLE '
+ QUOTENAME(#SchemaUsed) + '.'
+ QUOTENAME(name) + ' ADD [identityID]
BIGINT IDENTITY(1,1) NOT NULL;'
FROM sys.tables AS t
WHERE SCHEMA_NAME([schema_id]) = #SchemaUsed
AND name LIKE 'PM%'
AND NOT EXISTS (SELECT 1 FROM sys.columns AS c
WHERE [object_id] = t.[object_id]
AND c.is_identity = 1);
PRINT #sql;
--EXEC sp_executesql #sql;
To execute a character string, EXEC requires parenthesis around the string (or character variable) as shown in the BOL syntax:
EXEC (#SQL);

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

Adding GUID parameter to a sql string

I am having complications adding a guid type to a sql string and getting the query to go. I have to write the sql this because I need to pass the name database as a parameter.
The following code is what I'm trying to do but it doesnt work. No matter what I do it doesn't read the GUID correctly. It gives me the following error when executing the stored proc. Probably because guid has "-" in them
Incorrect syntax near '-'.
Here's the code:
ALTER PROCEDURE [dbo].[templatesAndBaskets_Select]
#guid uniqueidentifier,
#DatabaseName varchar(50)
AS
BEGIN
Declare #sql varchar(max)
Set #sql=
'select soldtoid
from ' + #DatabaseName + '.templatesAndBaskets' +
' where ordergroupid = ' + CONVERT(varchar(max),#guid)
print #sql
EXECUTE SP_EXECUTESQL #sql
END
You probably just simply need to put single quotes around the GUID :
SET #sql =
N'SELECT soldtoid FROM ' + #DatabaseName + N'.templatesAndBaskets' +
N' WHERE ordergroupid = ''' + CONVERT(nvarchar(max), #guid) + N''''
That should result in a SQL statement something like this:
SELECT soldtoid FROM database.templatesAndBaskets
WHERE ordergroupid = '5E736CE7-5527-40ED-8499-2CA93FC7BC9C'
which is valid - yours without the single quotes around the GUID isn't valid...
Update: you also need to change your #sql variable to NVARCHAR for sp_Executesql - try this:
ALTER PROCEDURE [dbo].[templatesAndBaskets_Select]
#guid uniqueidentifier,
#DatabaseName NVARCHAR(50)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX)
SET #sql =
N'SELECT soldtoid FROM ' + #DatabaseName + N'.templatesAndBaskets' +
N' WHERE ordergroupid = ''' + CONVERT(nvarchar(max), #guid) + N''''
PRINT #sql
EXECUTE sp_ExecuteSQL #sql
END
Does that work??
Put the #guid between '
ALTER PROCEDURE [dbo].[templatesAndBaskets_Select]
#guid uniqueidentifier,
#DatabaseName varchar(50)
AS
BEGIN
Declare #sql varchar(max)
Set #sql=
"select soldtoid from " + #DatabaseName + ".templatesAndBaskets" +
" where ordergroupid = '" + CONVERT(varchar(max),#guid)+"'"
print #sql
EXECUTE SP_EXECUTESQL #sql
END