Hi is it possible for me to do this somehow? When i run the statement i get an exception, #Price_Plan is not delared, so obviously the adhoc query does not have scope to access #Price_Plan. Is there a workaround, or a better way to query a table whose name changes per execution of this query.
DECLARE #Price_Plan varchar(3), #MNP_Network varchar(3), #GSM_Code varchar(3), #GEO_Dist varchar(6),
#Call_ProdNo varchar(7), #Call_Time datetime, #CallId int, #dtl_call_dur int,
#Volume varchar(10), #Call_Cost int
--Assume CallId has a value
SET #Sql =
'SELECT
#Price_Plan = Price_Plan, #MNP_Network = MNP_Network, #GSM_Code = GSM_Code, #GEO_Dist = GEO_Dist,
#Call_ProdNo = Call_ProdNo, #Call_Time = Call_Time, #dtl_call_dur = dtl_call_dur,
#Volume = Volume
FROM ' + #TableName + '
WHERE CallId = ' + CONVERT(varchar(10),#CallId) + ''
PRINT #SQL
EXEC (#Sql)
Are you sure this is this error? I tested that query and it returned two errors:
- Not declared #TableName
- Not declared #Sql
When you declare those variables it should work good.
But better way is to use sp_executesql.
An example:
EXEC sp_executesql
N'SELECT * FROM AdventureWorks.HumanResources.Employee
WHERE ManagerID = #level',
N'#level tinyint',
#level = 109;
First argument is the query with parametes, 2nd argument - parameters names with types separated by commas, and then goes the actual values of the parameters.
EDITED:
Here is the another example using OUTPUT parameter:
DECLARE #retCnt INT
EXEC sp_executesql
N'SELECT #retCnt = COUNT(*) FROM sys.tables',
N'#retCnt INT OUTPUT',
#retCnt = #retCnt OUTPUT
SELECT #retCnt
SELECT return 5 on my computer.
Related
Requirement: To write stored Procedure(s) such that the values passed in stored procedures are matched against the values in columns in the table and then arranged with highest to lowest matching number of attributes.Then are inserted in a dynamically created temporary table inside the stored procedure.
Problem:
I have say 15-20 attributes that are matched against to confirm the suggestions made in response to record search. Basically, There is a table that stores Patients information and multiple parameters may be passed into the stored procedure to search through so that a Temporary table is created that suggests records in decreasing order of matching attributes.
To frame the basic structure, I tried with 3 attributes and respective stored procedures to match them which in turn are collectively called from a calling procedure based on the input parameter list which in turn creates the required temporary table.
Here is the SQL code(Just to give the gist of what I have tried so far):
But as a matter of fact it is, I realize this is way too naive to be used in a real time application which may require 80-90% of accuracy.So, what exactly can replace this technique for better efficiency?
Create Procedure NameMatch
(
#Name nvarchar(20),
#PercentContribution nvarchar(4) OUT, #PatientName nvarchar(20) out
)
As
declare #temp int
DECLARE #Query nvarchar(500)
if Exists(select Name from dbo.PatientDetails where Name = #Name)
Begin
set #PatientName = #Name
set #query = 'select * from dbo.PatientDetails where Name =' + #Name
set #temp = 0.1*100
set #PercentContribution = #temp + '%'
Execute(#query)
Return
End
Create Procedure AgeMatch
(
#Name nvarchar(20),
#Age int,
#PercentContribution nvarchar(4) OUT, #PatientName nvarchar(20) out
)
As
declare #temp int
DECLARE #Query nvarchar(500)
if Exists(select Name from dbo.PatientDetails where Name =#Name and Age = + #Age)
Begin
set #PatientName = #Name
set #query = 'select * from dbo.PatientDetails where Name = ' + #Name + ' and Age = '+ #Age
set #temp = 0.2*100
set #PercentContribution = #temp + '%'
Execute(#query)
Return
End
Create Procedure Nationality
(
#Name nvarchar(20),
#Age int,
#Nation nvarchar(10),
#PercentContribution nvarchar(4) OUT, #PatientName nvarchar(20) out
)
As
declare #temp int
DECLARE #Query nvarchar(500)
if Exists(select Name from dbo.PatientDetails where Name = #Name and Age = #Age and Nationality = #Nation )
Begin
set #PatientName = #Name
set #query = 'select * from dbo.PatientDetails where Name = ' + #Name + ' and Age = '+ #Age + ' and Nationality = ' + #Nation
set #temp = 0.3*100
set #PercentContribution = #temp + '%'
Execute(#query)
Return
End
create procedure CallingProcedure
(
#Name nvarchar(20),
#Age int = null,
#Nation nvarchar(10)= null
)
As
declare #PercentMatch nvarchar(4)
Begin
create table #results(PatientName nvarchar(30), PercentMatch nvarchar(4))
if(#Nation IS NOT NULL)
Insert into #results exec Nationality #Nation, #Name output, #PercentMatch output
else if(#Age is not Null)
Insert into #results exec AgeMatch #Age, #Name output, #PercentMatch output
else
Insert into #results exec NameMatch #Name, #Name output, #PercentMatch output
End
Setting aside nuances of stored procedure syntax, given parameters 1-n that if not null should match columns 1-n and the results sorted by highest number of matches first, a dynamic query is not needed - plain SQL can do it.
select *
from patient
where #param1 is null or column1 = #param1
or #param2 is null or column2 = #param2
...
or #paramN is null or columnN = #paramN
order by if(column1 = #param1, 1, 0)
+ if(column2 = #param2, 1, 0)
...
+ if(columnN = #paramN, 1, 0) desc
I have a problem when I would like to transfer the value of a variable to another variable.
declare #column varchar(255);
set #column = 'cheesecake';
declare #tmp varchar(255);
set #tmp = (select #column from TEST where id = 1);
But in this case #tmp won't have the value of the table, but the name of the #column variable. I tried it with dynamic sql, but I got a syntax error.
declare #column varchar(255);
set #column = 'cheesecake';
declare #tmp varchar(255);
set #tmp = exec('select ' + #column + ' from TEST where id = 1');
How can I solve that the #tmp variable would contain the value of the query?
EXEC executes in a different context, therefore the variables cannot be shared without hassle. You specify an output parameter to the statement using sp_executesql
DECLARE
#language nvarchar(255),
#translation nvarchar(255),
#statement nvarchar(255)
SET #language = N'norwegian'
SET #statement = 'select #translation='+#language+' from Translations where id = 1'
EXEC sp_executesql
#statement,
N'#translation nvarchar(255) OUTPUT',
#translation OUTPUT
SELECT #translation
SQLFiddle
AFAIK, it is not possible to directly assign to a variable using `exec. A workaround to your issue would be to create a table variable to store the results of the dynamic query, and then set the value of the second variable using the table variable, like so:
declare #column varchar(255);
set #column = 'cheesecake';
declare #tmp varchar(255);
declare #query nvarchar(255) = N'select ' + #column + N' from TEST where id = 1'
declare #tbl table(tmp varchar(255)) --Intermediate Table variable
insert into #tbl --Insert dynamic query results here
exec sp_executesql #query
select top 1 #tmp = tmp from #tbl --Assign value here
select #tmp
EDIT: I stand corrected. This answer shows how you can assign from dynamic SQL result to a variable by making use of OUTPUT parameters within the dynamic query.
I've written a stored procedure which is called on a link which provides a date value every time and #cg is NULL that time to filter the result on a particular date.
DECLARE #return_value int
EXEC #return_value = [dbo].[Get_Mydata]
#cg = NULL,
#tosearch = '15-05-2014'
SELECT 'Return Value' = #return_value
GO
And after first execution of the stored procedure, it gives some results and using same stored procedure.
I need to filter result by passing below parameter so this time #cg is NOT NULL.
DECLARE #return_value int
EXEC #return_value = [dbo].[Get_Mydata]
#cg = 'CUSTOMER NAME',
#tosearch = 'manish'
SELECT 'Return Value' = #return_value
GO
I'm not able to figure how should I create a dynamic where clause and add it to existing query as well as how to pass value to same parameter which already been passed as date.
More like first getting results for a particular date and then applying like filter on that result. I cannot pass different parameter that's Front end developers requirement.
This is my stored procedure and table data here. http://sqlfiddle.com/#!3/bb917
create proc Get_Mydata
(
#cg varchar(50),
#tosearch varchar(50)
)
as
begin
set nocount on
declare #sqlquery nvarchar(max)
set #sqlquery = N'select q_no, trandate, cust_name from testsp where CONVERT(Date, trandate, 103) = CONVERT(Date, ''' + #tosearch + ''' ,103)';
create table #temp1
(
q_no int,
trandate datetime,
cust_name varchar(50)
)
insert into #temp1(q_no, trandate, cust_name)
exec (#sqlquery)
select * from #temp1 as T;
set nocount off
end
What I have understood is that you want stored procedure to filter results on Date column when you pass null to #cg param and you want to filter results on Cust_name when you pass string 'Cust_Name' to your #Cg Param.
It should be fairly simple, But in any case you do not need a temp table to get the results back its just an over kill of a fairly simple query.
I would do something like this....
Pass the column name to #ColumnName Parameter, and your value to #tosearch parameter. It will build the query depending on what values you pass.
Make sure when you pass a value(Column Name) to #ColumnName.
create proc Get_Mydata
(
#ColumnName varchar(50),
#tosearch varchar(50)
)
as
begin
set nocount on;
declare #sqlquery nvarchar(max);
set #sqlquery = N' select q_no, trandate, cust_name '
+ N' from testsp '
+ N' where ' + QUOTENAME(#ColumnName) + N' = '
+ CASE
WHEN #ColumnName = 'trandate'
THEN N' CAST(#tosearch AS DATE)'
WHEN #ColumnName = 'cust_name'
THEN N' #tosearch'
ELSE N'' END
EXECUTE sp_executesql #sqlquery
,N'#tosearch varchar(50)'
,#tosearch
set nocount off;
end
Table called Emp contain id,name,lname,birthdate,address and salery .I want to select from emp.
Basic query : select * from emp
If pass the value for lname ,query : select * from emp where lname = 'fgfg' like this.
So I created following sp.
create Procedure Proc_selectEmp
(
#name varchar(10) = null,
#lname varchar(10) = null,
#id varchar(10) = null
)
as
begin
select * from Emp
where
(#name is null or name = #name)
and (#lname is null or lname = #lname)
and (#id is null or id = #id)
end
Like emp,there are 13 table having same column name.
So my tablenmae is also dynamic.That's why, I choose execute sp_executesql.Can I create like this
create Procedure Proc_selectEmp
(
#name varchar(10) = null,
#lname varchar(10) = null,
#id varchar(10) = null
#tableName varchar(30)
)
as
begin
declare #query nvarchar(1000)
set #query = #query +'select * from '+#tableName+'
where ('+#name+' is null or name = '+#name+')
and ('+#lname+' is null or lname = '+#lname+')
and ('+#id+' is null or id = '+#id+')
end'
execute sp_executesql #query
It will work, although is pretty smelly, given that it requires that it requires that table name is a variable and thus tables must have the same column definitions.
Also, rather than including the #param is null or column = #param, rather, leave out unnecessary filters entirely, which is easy to do since you are using dynamic sql. This will avoid the parameter sniffing problem.
Lastly, instead of appending the column filters into the string, rather use the parameterized overload of sp_executesql, which will protect your from SQL injection attacks, and handle the escaping of quotes etc for you. Unfortunately, #tablename can't be parameterized, but hopefully? this isn't a user or foreign-supplied variable (in which case you will need to do some more thinking about design and or validation techniques).
i.e.
declare #query nvarchar(max)
set #query = N'select * from ' + #tableName + N` where (1=1)`
if (#name is not null)
set #query = #query + N'and name = #name'
-- ... same for #id and #lname
exec sp_executesql #SQL,
N'#name varchar(10),
#lname varchar(10),
#id varchar(10)',
#name = #name,
#lname = #lname,
#id = #id
Edit
Re : securing un-parameterizable inputs like table or column names in dynamic sql - see this post here for ideas - use of QUOTENAME and white-listing column / table names are prominent.
Yes you can but you have to write
EXECUTE(#query)
Instead of
execute sp_executesql #query
I have a stored procedure for creating inventory transactions that requires the assembly of a description. Since other inventory stored procedures will also need to assemble their descriptions similarly, I am trying to create a helper stored procedure.
This helper will use standard parameters and construct the description. The trouble I am having is returning the string Description back to the inventory transaction.
A Inventory transaction calls the helper this way:
declare #TransDescription nvarchar(256)
declare #TransDescOut nvarchar(256)
EXEC [dbo].[sp_KF_Helpers_CreateInvTransDescription]
#TransactionTypeID, #UserName, #OwnerTypeID, #OwnerID,
#TransDesc = #TransDescOut OUTPUT
SET #TransDescription = #TransDescOut
Then I use #TransDescription as a value for inserting into column data.
The helper code is:
CREATE PROCEDURE [dbo].[sp_KF_Helpers_CreateInvTransDescription]
( #TransactionTypeID int,
#UserName nvarchar(256),
#OwnerTypeID int,
#OwnerID int,
#TransDesc varchar(256) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
declare #rslt int = 0
declare #strTyepID varchar(256) = #TransactionTypeID
declare #strOwnerID varchar(256) = #OwnerID
declare #intOwnerTypeID int = #OwnerTypeID
declare #OwnerStr varchar(256) = 'KF_'
declare #OwnerIDStr varchar(256) = (select Description from KF_OwnerType where ID = #intOwnerTypeID)
select #OwnerStr = #OwnerStr + #OwnerIDStr
declare #sql1 nvarchar(4000)
Select #sql1 = 'Select Top 1 (a.Description + '' - '' + ' + #OwnerStr + '.Name) TransDesc
from KF_InventoryTransactionType a, KF_OwnerType c, ' + #OwnerStr + '
where a.ID = ' + #strTyepID + ' and '
+ #OwnerStr + '.ID = ' + #strOwnerID
exec SP_EXECUTESQL #sql1, N'#TransDesc varchar output ', #TransDesc output
End
As you can see, I am using dynamic SQL to generate the description. The problem is that the help code generates the correct description, but does not pass it back as output.
Anyone know why or where I am losing scope for returning my output description?
You forgot to assign the variable. Try:
select top 1 #TransDesc = a.Description + '' - '' + ' + #OwnerStr + '.Name
...
Also, change the part where you are declaring the parameter of the dynamic script (#TransDesc), or you will run into another issue. Currently the parameter is being declared like this:
#TransDesc varchar output
which is equivalent to
#TransDesc varchar(1) output
Most likely, it should be
#TransDesc varchar(256) output
instead.