APOSTROPHE DYNAMIC SQL - sql-server

DECLARE #SQL Varchar(Max)
DECLARE #DESCR Varchar(Max)
-- Customer enters description into #Descr
SET #SQL = 'Update TableName SET FieldName='''
+ #DESCR
+ ''' WHERE ID=123'
The problem is when the customer enters an apostrophe into the #Descr variable.
Q: In Microsoft SQL Server 2005, how do I replace all apostrophies with double apostrophe?

If this even needs to be dynamic SQL at all (the code you have shown doesn't) then use parameterised SQL and sp_executesql for this to avoid SQL injection possibilities.
DECLARE #SQL NVarchar(Max)
DECLARE #DESCR NVarchar(Max)
-- Customer enters description into #Descr
SET #SQL = 'Update TableName SET FieldName=#DESCR WHERE ID=123'
exec sp_executesql #SQL, N'#DESCR NVarchar(Max)', #DESCR =#DESCR

Not recommended for production, but will work.
SET #DESCR = REPLACE(#DESCR, '''', '''''')

Related

Running dynamic queries on linked server

I wonder, is it possible to make this work?
declare #SQL varchar(max),
#query varchar(max) ='select * from Table'
select #SQL='USE LinkedServer.DBName ' + #query'
exec (#SQL)
It works just fine if you use 'USE' to run queries on the same server, but not on Linked ones.
What I want is to create procedure which will run some dynamic queries on different servers and dbs, and I want to pass ServerName and DBName as a parameter to that sp.
NOTE: I don't want to use it this way:
declare #SQL varchar(max)
select #SQL='
select * from LinkedServer.DBName..Table'
exec (#SQL)
Since that select statement will be retrieved from table
Sure, little-known trick to execute the remote database's sp_executesql:
DECLARE #LinkedServer sysname = N'LinkedServer',
#DatabaseName sysname = N'DBName';
-- above are input params
DECLARE #exec nvarchar(4000), #sql nvarchar(max);
SET #sql = N'SELECT * FROM dbo.Table;';
-- or SELECT #sql = DynamicSQL FROM dbo.SomeLocalTable WHERE...
SET #exec = QUOTENAME(#LinkedServer) + N'.'
+ QUOTENAME(#DatabaseName)
+ N'.sys.sp_executesql';
EXEC #exec #sql;
Don't use parentheses. EXEC('string') is evil, evil, evil.

how to parametrize a database in a distributed query

I need to define the database as a variable in a query like this:
insert into Database1.dbo.Table1
select * from Database2.dbo.Table1
In my case all the databases have the same schema.
I can do with dynamic sql of course, but is it a way to have a syntax like:
insert into Database1.dbo.Table1
select * from #ChosenDatabase.dbo.Table1
?
Thanks
What you are trying to achieve is not possible; You have to use Dynamic SQL :
DECLARE #SQLQuery varchar(300)
DECLARE #TableName varchar(100)
SET #TableName = ChosenDatabase.dbo.Table1
SET #SQLQuery = 'INSERT INTO Database1.dbo.Table1 SELECT * FROM ' + #TableName
EXEC(#SQLQuery);
Declare #dbname varchar(200)
Declare #sql varchar(1000)
now comes dynamic sql
set #sql='select * from '#dbname+'dbo.yourtable';
Try to use dynamic sql
declare #ChosenDatabase varchar(100)='dbname'
DECLARE #SQL NVARCHAR(MAX)
SET #SQL= 'select * from '+ #ChosenDatabase+'.dbo.Customer'
--SELECT #SQL
EXEC SP_EXECUTESQL #SQL

Accessing variable in SQL in a dynamic query

I am very new to SQL. I want to access a variable dynamically in a select statement.
declare #sql NVARCHAR(MAX)
declare #tableName varchar(100)
set #tableName='xxxx'
set #sql='select * from ' +#tableName+
EXEC sys.sp_executesql #sql
But every time I am executing the above query I am getting an error:
Incorrect syntax near the keyword 'EXEC'.
declare #sql NVARCHAR(MAX);
declare #tableName NVARCHAR(128);
set #tableName='xxxx';
SET #sql = N'select * from ' + QUOTENAME(#tableName)
EXECUTE sp_executesql #sql
Use QUOTENAME() Function when concertinaing passed variables from users to your dynamic sql. It protects you against possible sql injection attack.
You've got an extra plus sign after #tableName - remove it:
set #sql='select * from ' +#tableName /*+ */
EXEC sys.sp_executesql #sql
You had one too many plus(+) signs around #table name.
Please note that this method is wide open to an injection attack though.
declare #sql NVARCHAR(MAX)
declare #tableName varchar(100)
set #tableName='xxxx'
set #sql='select * from ' +#tableName
EXEC sys.sp_executesql #sql

sql use statement with variable

I'm trying to switch the current database with a SQL statement.
I have tried the following, but all attempts failed:
USE #DatabaseName
EXEC sp_sqlexec #Sql -- where #Sql = 'USE [' + #DatabaseName + ']'
To add a little more detail.
EDIT: I would like to perform several things on two separate database, where both are configured with a variable. Something like this:
USE Database1
SELECT * FROM Table1
USE Database2
SELECT * FROM Table2
The problem with the former is that what you're doing is USE 'myDB' rather than USE myDB.
you're passing a string; but USE is looking for an explicit reference.
The latter example works for me.
declare #sql varchar(20)
select #sql = 'USE myDb'
EXEC sp_sqlexec #Sql
-- also works
select #sql = 'USE [myDb]'
EXEC sp_sqlexec #Sql
exec sp_execsql #Sql
The DB change only lasts for the time to complete #sql
http://blog.sqlauthority.com/2007/07/02/sql-server-2005-comparison-sp_executesql-vs-executeexec/
I have the same problem, I overcame it with an ugly -- but useful -- set of GOTOs.
The reason I call the "script runner" before everything is that I want to hide the complexity and ugly approach from any developer that just wants to work with the actual script. At the same time, I can make sure that the script is run in the two (extensible to three and more) databases in the exact same way.
GOTO ScriptRunner
ScriptExecutes:
--------------------ACTUAL SCRIPT--------------------
-------- Will be executed in DB1 and in DB2 ---------
--TODO: Your script right here
------------------ACTUAL SCRIPT ENDS-----------------
GOTO ScriptReturns
ScriptRunner:
USE DB1
GOTO ScriptExecutes
ScriptReturns:
IF (db_name() = 'DB1')
BEGIN
USE DB2
GOTO ScriptExecutes
END
With this approach you get to keep your variables and SQL Server does not freak out if you happen to go over a DECLARE statement twice.
Just wanted to thank KM for his valuable solution.
I implemented it myself to reduce the amount of lines in a shrinkdatabase request on SQLServer.
Here is my SQL request if it can help anyone :
-- Declare the variable to be used
DECLARE #Query varchar (1000)
DECLARE #MyDBN varchar(11);
-- Initializing the #MyDBN variable (possible values : db1, db2, db3, ...)
SET #MyDBN = 'db1';
-- Creating the request to execute
SET #Query='use '+ #MyDBN +'; ALTER DATABASE '+ #MyDBN +' SET RECOVERY SIMPLE WITH NO_WAIT; DBCC SHRINKDATABASE ('+ #MyDBN +', 1, TRUNCATEONLY); ALTER DATABASE '+ #MyDBN +' SET RECOVERY FULL WITH NO_WAIT'
--
EXEC (#Query)
try this:
DECLARE #Query varchar(1000)
DECLARE #DatabaseName varchar(500)
SET #DatabaseName='xyz'
SET #Query='SELECT * FROM Server.'+#DatabaseName+'.Owner.Table1'
EXEC (#Query)
SET #DatabaseName='abc'
SET #Query='SELECT * FROM Server.'+#DatabaseName+'.Owner.Table2'
EXEC (#Query)
I case that someone need a solution for this, this is one:
if you use a dynamic USE statement all your query need to be dynamic, because it need to be everything in the same context.
You can try with SYNONYM, is basically an ALIAS to a specific Table, this SYNONYM is inserted into the sys.synonyms table so you have access to it from any context
Look this static statement:
CREATE SYNONYM MASTER_SCHEMACOLUMNS FOR Master.INFORMATION_SCHEMA.COLUMNS
SELECT * FROM MASTER_SCHEMACOLUMNS
Now dynamic:
DECLARE #SQL VARCHAR(200)
DECLARE #CATALOG VARCHAR(200) = 'Master'
IF EXISTS(SELECT * FROM sys.synonyms s WHERE s.name = 'CURRENT_SCHEMACOLUMNS')
BEGIN
DROP SYNONYM CURRENT_SCHEMACOLUMNS
END
SELECT #SQL = 'CREATE SYNONYM CURRENT_SCHEMACOLUMNS FOR '+ #CATALOG +'.INFORMATION_SCHEMA.COLUMNS';
EXEC sp_sqlexec #SQL
--Your not dynamic Code
SELECT * FROM CURRENT_SCHEMACOLUMNS
Now just change the value of #CATALOG and you will be able to list the same table but from different catalog.
If SQLCMD is an option, it supports scripting variables above and beyond what straight T-SQL can do. For example: http://msdn.microsoft.com/en-us/library/ms188714.aspx
You can do this:
Declare #dbName nvarchar(max);
SET #dbName = 'TESTDB';
Declare #SQL nvarchar(max);
select #SQL = 'USE ' + #dbName +'; {can put command(s) here}';
EXEC (#SQL);
{but not here!}
This means you can do a recursive select like the following:
Declare #dbName nvarchar(max);
SET #dbName = 'TESTDB';
Declare #SQL nvarchar(max);
SELECT #SQL = 'USE ' + #dbName + '; ' +(Select ... {query here}
For XML Path(''),Type)
.value('text()[1]','nvarchar(max)');
Exec (#SQL)
Use exec sp_execsql #Sql
Example
DECLARE #sql as nvarchar(100)
DECLARE #paraDOB datetime
SET #paraDOB = '1/1/1981'
SET #sql=N'SELECT * FROM EmpMast WHERE DOB >= #paraDOB'
exec sp_executesql #sql,N'#paraDOB datetime',#paraDOB
-- If you are using a variable for the database name.
-- Try something like this.
DECLARE #DBName varchar(50)
Set #DBName = 'Database1'; /* could be passed in by a parameter. */
IF( #DBName = 'Database1')
Begin
USE [Database1];
SELECT FROM Table1;
End
IF( #DBName = 'Database2')
Begin
USE [Database2];
SELECT FROM Table2;
End
IF( #DBName is null)
Begin
USE [Database1];
End

Creating stored procedure in another database

Any idea if it's possible to create a procedure in another database using T-SQL alone, where the name of the database is not known up front and has to be read from a table? Kind of like this example:
Use [MasterDatabase]
Declare #FirstDatabase nvarchar(100)
Select Top 1 #FirstDatabase=[ChildDatabase] From [ChildDatabases]
Declare #SQL nvarchar(4000)
Declare #CRLF nvarchar(10) Set #CRLF=nchar(13)+nchar(10)
Set #SQL =
'Use [+'#Firstdatabase+']'+#CRLF+
'Go'+#CRLF+
'Create Proc [Test] As Select 123'
Exec (#SQL)
See what I'm trying to do? This example fails because Go is actually not a T-SQL command but it something recognised by the query analyser/SQL management studio and produces an error. Remove the Go and it also fails because Create Proc must be the first line of the script. Arrgg!!
The syntax of T-SQL doesn't allow you do things like this:
Create [OtherDatabase].[dbo].[Test]
Which is a shame as it would work a treat! You can do that with Select statements, shame it's inconsistent:
Select * From [OtherDatabase]..[TheTable]
Cheers, Rob.
It's a pain, but this is what I do. I took this from an example I found on sqlteam, I think - you might have some quoting issues with the way I did the indiscriminate REPLACE:
DECLARE #sql AS varchar(MAX)
DECLARE #metasql as varchar(MAX)
DECLARE #PrintQuery AS bit
DECLARE #ExecQuery AS bit
SET #PrintQuery = 1
SET #ExecQuery = 0
SET #sql =
'
CREATE PROCEDURE etc.
AS
BEGIN
END
'
SET #metasql = '
USE OtherDatabase
EXEC (''' + REPLACE(#sql, '''', '''''') + ''')
'
IF #PrintQuery = 1
PRINT #metasql
IF #ExecQuery = 1
EXEC (#metasql)
DECLARE #UseAndExecStatment nvarchar(4000),
#SQLString nvarchar(4000)
SET #UseAndExecStatment = 'use ' + #DBName +' exec sp_executesql #SQLString'
SET #SQLString = N'CREATE Procedure [Test] As Select 123'
EXEC sp_executesql #UseAndExecStatment,
N'#SQLString nvarchar(4000)', #SQLString=#SQLString
This is how i have done with Alter Procedure:
DECLARE #metasql as varchar(MAX)
DECLARE #sql AS varchar(MAX)
SET #sql =
'ALTER PROCEDURE [dbo].[GetVersion]
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP(1)[Version] from VersionTable
END'
SET #metasql = '
USE MyProdDb
IF (OBJECT_ID(''GetVersion'') IS NOT NULL OR OBJECT_ID(''GetVersion'', ''P'') IS NOT NULL)
BEGIN
EXEC (''' + REPLACE(#sql, '''', '''''') + ''')
END
'
--PRINT #metasql
EXEC (#metasql)
You could shell out to osql using xp_cmdshell, I suppose.
I dont think this can be done with TSQL.
You could use an SSIS package that looped the names and connected to the servers dynamically which creates the schema (procs ) you need.
This is probably what I would do as it means it is all contained within the package.
Configuration can be kept separate by either using a table or external xml file that contained the list of server/databases to deploy the schema to.
It's not necessary to use EXEC within EXEC.
You can simply use OtherDatabase.sys.sp_executesql
DECLARE #sql AS varchar(MAX) = N'
CREATE PROCEDURE etc.
AS
BEGIN
-- whatever
END
';
PRINT #sql;
EXEC OtherDatabase.sys.sp_executesql #sql;

Resources