I want to make a EXEC in my sql with Transact-SQL, like:
set #name = 'test'
set #sql = 'insert into TempTable values('+#name+')'
EXEC( #sql)
but i can't insert a varchar in the sql string because i can't put ' into the string so it to knows that #name is a varchar.
first declare the variable first with declare , set and then u can use it.
below the complete code :
DECLARE #name VARCHAR (500)
SET #name = 'test';
set #sql = 'insert into TempTable values('''+#name+''')'
EXEC( #sql)
use (''') three quote to add your varchar.
A simple insert statement like this does not need to executed using Dynamic SQL. you can easily achieve the same using the query below.
DECLARE #name VARCHAR (500)
SET #name = 'test';
insert into TempTable values(#name)
You can refer to the link below for more details on issues related to dynamic sql.
http://www.sommarskog.se/dynamic_sql.html
Please forget using EXEC when you are working with dynamic queries. In SQL Server you should use sp_executesql
DECLARE #name VARCHAR(500);
SET #name = 'test';
DECLARE #query = NVARCHAR(MAX) = 'INSERT INTO TempTable VALUES (#name)';
EXEC sp_executesql
#stmt = #query,
#params = N'#name VARCHAR(500)',
#name = #name
With this
You can avoid unnecessary type conversions (INT -> VARCHAR, etc)
Native data types could be passed
A bit more secure (will not solve all the problems, but a bit bette, than injecting variable values into the query string)
Related
As I have seen so far, people suggested using dynamic SQL.
For example:
How to pass schema as parameter to a stored procedure in sql server?
How to pass schema name as parameter in stored procedure
However, dynamic SQL has the risk of SQL injection. Hence, I want to know if there are any other safe alternatives?
Basically, this stored procedure that I am creating will be called at runtime. There will be 2 possible schemas to be passed in. And the table name will be passed in as well.
Something like below: (It does not work)
CREATE PROCEDURE [EFM].[usp_readApexTable]
#SCHEMANAME VARCHAR(20) = NULL,
#TABLENAME VARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT *
FROM [#SCHEMANAME].[#TABLENAME];
END
GO
This is just an example of READ action. My plan is to create for CRUD, which requires 4 different stored procedures.
You can use QUOTENAME to avoid any SQL injection and build your dynamic query like the following:
CREATE PROCEDURE [EFM].[usp_readApexTable]
#SCHEMANAME VARCHAR(20) = NULL,
#TABLENAME VARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL VARCHAR(MAX)=N'SELECT * FROM '
+ QUOTENAME(#SCHEMANAME) + '.' + QUOTENAME(#TABLENAME)
EXEC (#SQL)
END
GO
Note: If you have any plan to add parameters also for your WHERE clause, in that case QUOTENAME will not help much, I suggest to to use sp_executesql by passing appropriate parameters used in WHERE clause.
Still you need to use QUOTENAME for schema and table name as SQL excepts it only as literal, you can't use variable names for table and schema.
For example.
declare #sql nvarchar(max)
set #sql = N'select * from ' + quotename(#SCHEMANAME ) + '.' + quotename(#TABLENAME )
+ ' where (City = #City)'
exec sp_executesql
#sql,
N'#City nvarchar(50)',
#City
You can find more details here
You need to use dynamic sql to do this operation
CREATE PROCEDURE [EFM].[usp_readApexTable]
#SCHEMANAME VARCHAR(20) = NULL,
#TABLENAME VARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sqlCommand nvarchar(MAX)
SET #sqlCommand='SELECT * FROM ['+#SCHEMANAME+'].['+#TABLENAME+'];'
--Create Your Temp Table where you can set the records after executing the dynamic query
CREATE TABLE #tmpTbl(
Column1 [datatype]
Column2 [datatype]
.
.
ColumnN
)
INSERT INTO #tmpTbl EXEC sp_executesql #sqlCommand --Copy data to #tmpTbl table
SELECT * FROM #tmpTbl
DROP TABLE #tmpTbl
END
GO
I have the following query which takes the XML input and stores them into temp tables.
I need to make the query dynamic or a function which will adjust to any table or column number modification dynamically.How do I do that:
DECLARE #sXML NVARCHAR(4000)
DECLARE #hDoc INT
SET #sXML = '
<Root>
<Item>
<Key>1</Key>
<Test1>A</Test1>
<Test2>A2</Test2>
</Item>
<Item>
<Key>2</Key>
<Test1>B</Test1>
<Test2>B3</Test2>
</Item>
</Root>
'
CREATE TABLE #tabletest
(
[Key] INT,
Test1 NVARCHAR(50),
Test2 NVARCHAR(50)
)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #sXML
INSERT INTO #tabletest SELECT * FROM OPENXML (#hDoc, '/Root/Item', 2) WITH #tabletest
EXEC sp_xml_removedocument #hDoc
select * from #tabletest
DROP TABLE #tabletest
To create Dynamic SQL you need to build up the query as a string. Using If Statements and other logic to add your variables...etc.
Declare a text variable and use this to concatenate together your desired SQL.
You can then execute this code using the "EXEC" command
Example:
DECLARE #SQL VARCHAR(100)
DECLARE #TableOne VARCHAR(20) = 'TableOne'
DECLARE #TableTwo VARCHAR(20) = 'TableTwo'
DECLARE #SomeInt INT
SET #SQL = 'INSERT INTO '
IF (#SomeInt = 1)
SET #SQL = #SQL + #TableOne
IF (#SomeInt = 2)
SET #SQL = #SQL + #TableTwo
SET #SQL = #SQL + ' VALUES....etc'
EXEC (#SQL)
However, something you should really watch out for when using this method is a security problem called "SQL Injection".
You can read up on that here:
http://msdn.microsoft.com/en-gb/library/ms161953(v=sql.105).aspx
One way to guard against SQL injection is to validate against it in your C# code before passing the variables to SQL-Server.
An alternative way (or probably best used in conjecture) is instead of using the "EXEC" command, use a built in stored procedure called "sp_executesql".
Details of which can be found here: http://msdn.microsoft.com/en-gb/library/ms188001.aspx
How it is used is detailed here: http://msdn.microsoft.com/en-gb/library/ms175170(v=sql.105).aspx
You'll have to build your SQL slightly differently and pass your parameters to the stored procedure as well as the #SQL.
Can I pass a variable to a SELECT statement?
I keep getting an error message saying I need to declare it.
However, it is declared.
SELECT (list of columns)
FROM #database_table
You are looking to use Dynamic SQL to perform this type of query.
The Curse and Blessings of Dynamic SQL
Here is a quick sample
declare #sqlstatement nvarchar(4000)
declare #table sysname
set #table = 'yourTableName'
set #sqlstatement = 'SELECT * FROM ' + QUOTENAME(#table)
exec(#sqlstatement)
Yes, use dynamic sql statements to build your select statement.
-- Procedure input parameters
#TableName varchar(50)
-- Query guts
Declare #sql varchar(2000)
Set #sql = 'Select columnname from ' + #TableName
exec (#sql)
The one time you can do what you want is when you use table variables. You have to define the variables as:
declare #name table (<column list>)
This is alternative method of declaring a temporary table.
Other than this, I fully agree with bluefeet. You should read the link he posted.
Is this incorrect, can't we pass the table name to a select query dynamically?
This is giving me a error 'Must declare the table variable #TblName'
DECLARE #TblName VARCHAR(30)
SET #TblName = 'User'
SELECT *
FROM #TblName
You need to create a dynamic SQL query, preferably using the QUOTENAME function. You can avoid any issues from malicious input by using QUOTENAME function.
Here is a sample script that illustrates how to query a table by creating a dynamic SQL query by passing in a table name. You can change the table name by value to the variable #tablename.
Create and insert script for sample:
CREATE TABLE sample
(
id INT NOT NULL
);
INSERT INTO sample (id) VALUES
(1),
(2),
(3),
(4),
(5),
(6);
Dynamic SQL script:
DECLARE #execquery AS NVARCHAR(MAX)
DECLARE #tablename AS NVARCHAR(128)
SET #tablename = 'sample'
SET #execquery = N'SELECT * FROM ' + QUOTENAME(#tablename)
EXECUTE sp_executesql #execquery
Demo:
Click here to view the demo in SQL Fiddle.
Suggested read:
The Curse and Blessings of Dynamic SQL
you have to use dynamic sql execution
wrap your statement in #selectstr
use exec sp_executesql #selectstr
You can do this thing by using dynamic query, Check below
DECLARE #TblName VARCHAR(30)
DECLARE #vQuery NVARCHAR(100)
SET #TblName = 'User'
SET #vQuery = 'SELECT * FROM ' + #TblName
EXECUTE sp_executesql #vQuery
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