-- Dynamic SQL Using: sp_executesql
use
AdventureWorksgo
-- Create proc
create
proc dbo.proc_DynamicSQL_Using_sp_executesql (@DatabaseName
varchar(30),@SchemaName
varchar(30),@TableName
varchar(30),@ColumnName
varchar(30),@Value
int )as
exec
sp_executesql N'select * from ' + @DatabaseName + '.' + @SchemaName + '.' + @TableName + ' where ' + @ColumnName + ' = ' + @Valuereturn
go
-- Execute proc
exec
proc_DynamicSQL_Using_sp_executesql 'AdventureWorks', 'Person', 'Contact', 'ContactID', 1-- Execute proc specifying parameter names
exec
proc_DynamicSQL_Using_sp_executesql@DatabaseName
= 'AdventureWorks',@SchemaName
= 'Person',@TableName
= 'Contact',@ColumnName
= 'ContactID',@Value
= 1go
-- This is the select statement generated
--select * from AdventureWorks.Person.Contact where ContactID = 1
DECLARE @sql nvarchar(MAX)
SELECT @sql = N'select * from ' + @DatabaseName + '.' + @SchemaName + '.'
+ @TableName + ' where ' + @ColumnName + ' = ' + @Value
EXEC sp_executesql @sql
I like to add that this test procedure you are writing is fairly
meaningless. In a well-designed database, there is rarely reason
to pass table names as parameters.
--
Erland Sommarskog, SQL Server MVP, esq...@sommarskog.se
Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
Books Online for SQL Server 2000 at
http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx
CREATE PROCEDURE dbo.proc_DynamicSQL_Using_sp_executesql
(
@DatabaseName varchar(30),
@SchemaName varchar(30),
@TableName varchar(30),
@ColumnName varchar(30),
@Value int
)
AS
DECLARE @sql NVARCHAR(500)
SET @sql = N'select * from ' + @DatabaseName + '.' + @SchemaName + '.'
+ @TableName + ' where ' + @ColumnName + ' = ' + CAST(@Value AS VARCHAR(10))
EXEC sp_executesql @sql
A better approach would be to pass the value as parameter to sp_executesql:
CREATE PROCEDURE dbo.proc_DynamicSQL_Using_sp_executesql
(
@DatabaseName varchar(30),
@SchemaName varchar(30),
@TableName varchar(30),
@ColumnName varchar(30),
@Value int
)
AS
DECLARE @sql NVARCHAR(500)
SET @sql = N'select * from ' + @DatabaseName + '.' + @SchemaName + '.'
+ @TableName + ' where ' + @ColumnName + ' = @param_value'
EXEC sp_executesql @sql,
@params = N'@param_value INT',
@param_value = @value
HTH,
Plamen Ratchev
http://www.SQLStudio.com
Erland, I appreciate your comments about it being a fairly meaningless
query. I think I was trying more than anything to get familiar with using
dynamic SQL, and in the meantime was creating something that would never be
used! I changed it to just pass the value, which makes a little more sense.
Thanks again,
Rubens
"Plamen Ratchev" <Pla...@SQLStudio.com> wrote in message
news:0931E68D-C1DE-4160...@microsoft.com...
One style trick I've started using recently when building dynamic SQL
is to delimit the replacement parts with pipe characters, and then do
a replace; it just is easier to read (IMO) than trying to break out of
the string, concatenate, add it back in, etc..
For example
SET @sql = N'select * from ' + @DatabaseName + '.' + @SchemaName + '.'
+ @TableName + ' where ' + @ColumnName + ' = @param_value'
would become
SET @SQL = N'SELECT * FROM |DatabaseName|.|SchemaName|.|TableName|
WHERE |ColumnName| = @param_value'
SET @SQL = REPLACE(@SQL, '|DatabaseName|', @DatabaseName)
SET @SQL = REPLACE(@SQL, '|SchemaName|', @SchemaName)
SET @SQL = REPLACE(@SQL, '|TableName|', @TableName)
SET @SQL = REPLACE(@SQL, '|ColumnName|', @ColumnName)
It does involve a lot more keystrokes, but it keeps me from doing
stupid things like forgetting to leave a space before and after part
of the string (like the + ' where ' + fragment in Plamen's
example).
Stu
Thank-you,
Rubens
"Stuart Ainsworth" <stuart.a...@gmail.com> wrote in message
news:28269475-4de2-4c28...@p25g2000hsf.googlegroups.com...
I don't hink so; it's just manipulating the value of variable. It
should all be done in-memory.
Stu
Rubens
"Stuart Ainsworth" <stuart.a...@gmail.com> wrote in message
news:5b036361-9847-4205...@b1g2000hsg.googlegroups.com...
Hi Erland,
Let me suggest a safer way, eliminating the risk of SQL injection:
DECLARE @sql nvarchar(MAX)
SELECT @sql = N'select * from ' + (SELECT [name] FROM sys.databases
WHERE [name]=@DatabaseName) + '.' +
(a subquery with @SchemaName) + '.'
+ (a subquery with @TableName) + ' where ' + (a subquery
with @ColumnName) + ' = ' + @Value
EXEC sp_executesql @sql
SET @SQL = REPLACE(@SQL, '|DatabaseName|', quotename(@DatabaseName))
etc.
--
Erland Sommarskog, SQL Server MVP, esq...@sommarskog.se
Books Online for SQL Server 2005 at
http://www.microsoft.com/technet/prodtechnol/sql/2005/downloads/books.mspx
SET @SQL = N'SELECT * FROM @DatabaseName.@SchemaName.@TableName'
Then the replace is more clear on what the goal is:
SET @SQL = REPLACE(@SQL, '@DatabaseName', quotename(@DatabaseName))
Plamen Ratchev
http://www.SQLStudio.com
First, the quotename thing is pretty cool; I'll have to start using
that.
Second, I wound up using pipe delimiters because it stood out in the
SQL statement when scanning it; since some of the dynamic SQL
statements I build use parameters passed from sp_executeSQL, the pipes
made it easy to tell which variables would need to be REPLACEd, and
which would have values passed it.
6 of one, half a dozen of the other, I suppose; just glad others find
it useful.
Stu
Returns a Unicode string with the delimiters added to make the input string
a valid Microsoft SQL Server 2005 delimited identifier.
Can someone please explain what this does in laytechie terms? And how it is
used in this example?
Thank-you,
Rubens
P.S. Stu, I will give you an example of where a "6 of one" = "half dozen"
of the other. Let's use a Baker's Dozen... and say, our SQL 6.5 HRIS
system! That system is indeed actually worth about 6.5 donuts. Thank
goodness it will be replaced this year... :-)
"Stuart Ainsworth" <stuart.a...@gmail.com> wrote in message
news:93eae442-c0dd-4580...@a23g2000hsc.googlegroups.com...
SELECT QUOTENAME('foo');
? All it does is put identifiers around the string passed in. So, foo
becomes [foo].
"Rubens" <ruben...@hotmail.com> wrote in message
news:ucwN1pFl...@TK2MSFTNGP06.phx.gbl...
The purpose is to wrap the string in a delimiter (default [], and double any
closing delimiters within the string to make sure that the string works in
the context you use it.
In the examples in the thread we had quotename(@dbname) etc. This is because
if the database would be name My Space,
SELECT * FROM My Space.dbo.tbl
would not be valid SQL.
You can play with the following script and see how quotemane protects
your from injections:
declare @sql nvarchar(max),
@DatabaseName nvarchar(100),
@schemaName nvarchar(100),
@tableName nvarchar(100),
@columnName nvarchar(100)
select @DatabaseName = 'test.dbo.t;drop table t; select * from [test',
@schemaName = 'dbo',
@tableName = 't',
@columnName = 'c'
SET @SQL = N'SELECT * FROM |DatabaseName|.|SchemaName|.|TableName|
WHERE |ColumnName| = @param_value'
SET @SQL = REPLACE(@SQL, '|DatabaseName|', quotename(@DatabaseName))
SET @SQL = REPLACE(@SQL, '|SchemaName|', quotename(@SchemaName))
SET @SQL = REPLACE(@SQL, '|TableName|', quotename(@TableName))
SET @SQL = REPLACE(@SQL, '|ColumnName|', quotename(@ColumnName))
PRINT @sql
Also try setting
@DatabaseName = 'test].dbo.t;drop table t; select * from [test',
and see what happens
Rubens
"Aaron Bertrand [SQL Server MVP]" <ten...@dnartreb.noraa> wrote in message
news:04ECA6FC-CBF4-453E...@microsoft.com...
Rubens
"Erland Sommarskog" <esq...@sommarskog.se> wrote in message
news:Xns9A745EF16...@127.0.0.1...
Here is what each returned:
Example 1: select @DatabaseName = 'test.dbo.t;drop table t; select * from
[test',
Result: SELECT * FROM [test.dbo.t;drop table t; select * from
[test].[dbo].[t] WHERE [c] = @param_value
Example 2: select @DatabaseName = 'test].dbo.t;drop table t; select * from
[test',
SELECT * FROM [test]].dbo.t;drop table t; select * from [test].[dbo].[t]
WHERE [c] = @param_value
Thanks Alex,
Rubens
"Alex Kuznetsov" <alk...@gmail.com> wrote in message
news:7eaebcc1-2615-402a...@d1g2000hsg.googlegroups.com...