Dynamically creating the IN clause in a stored procedure [duplicate] - sql

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Need help in dynamic query with IN Clause
I am using SQL server 2008 and this is the problem that I am facing. I have a table named Cars with a column Company. Now I have a stored procedure that looks something like this
CREATE PROCEDURE FindCars (#CompanyNames varchar(500))
AS
SELECT * FROM Cars WHERE Company IN (#CompanyNames)
I tried something like this and failed
DECLARE #CompanyNames varchar(500)
SET #CompanyNames = '''Ford'',''BMW'''
exec FindCars #CompanyNames
I dont get any rows returned. When I do the following
DECLARE #CompanyNames varchar(500)
SET #CompanyNames = '''Ford'',''BMW'''
Select #CompanyNames
I get the following result
'Ford','BMW'
and if I replace this value in the select statement inside the stored procedure, it works
SELECT * FROM Cars where Company in ('Ford','BMW')
Thus I think that the stored procedure seems to be treating 'Ford','BMW' as one string rather than an array. Could someone please help me with this. How do I dynamically construct the string/array required in the IN clause of the select statement inside the stored procedure.

You are right, you created one string, and that is being processed as a list of strings that contains one string. The commas are just characters in that one string. The only equivilent to an array in SQL Server is a table.
For example; WHERE x IN (SELECT y FROM z).
For this reason many people create a SPLIT_STRING() function that returns a table of items from a given comma delimitted string...
WHERE x IN (SELECT item FROM dbo.split_string(#input_string))
There are many ways to implement that split string. Some return strings, some cast to integers, some accept a second "delimiter" parameter, etc, etc. You can search the internet for SQL SERVER SPLIT STRING and get many results - Including here in StackOverflow.
An alternative is to use dynamic SQL; SQL that writes SQL.
SET #sql = 'SELECT * FROM x WHERE y IN (' + #input_string_list + ')'
SP_EXECEUTESQL #sql
(I recommend SP_EXECUTESQL over just EXEC because the former allows you to use parameterised queries, but the latter does not.)

Related

Run SQL Dynamic using column value [duplicate]

This question already has answers here:
T-SQL: How to use parameters in dynamic SQL?
(4 answers)
sql stored procedure argument as parameter for dynamic query
(2 answers)
SQL Server 2017 - How to pass a parameter in a SELECT inside a dynamic SQL
(1 answer)
Closed last year.
I am using dynamic SQL.
I inserted a select statement into one row from #example, I want to call that select statement and run it with dynamic SQL, my select stored has a variable call #NumOrder
create table #example(id int, description varchar(1000))
insert into #example(id, description)
values(1, 'select case when name=''Carlos'' then ''Pass'' else ''FAIL'' from server.dbo.person where number = #NumOrder')
declare #NumOrder int, #SQL nvarchar(max)
set #NumOrder=2621
set #SQL='description'
--here is where I want to call my query and is failing
--once I filled #NumOrder I want to display the results
exec(#SQL)
I want to run this dynamic SQL using a row stored into the temp table, I know that I can put the complete select statement into the #SQL and works but I want to know if there is a way to call the select statement in a row stored from a table. Is there a way to do this?

Why do SQL injection not working?

DECLARE #a varchar(max);
set #a ='''a'' OR Name like ''%a'';';
--Why the below query not working
Select TOP 10 * FROM Member where Name = #a
-- The query below was executed to make sure that the query above
being constructed properly
print 'SQL: Select TOP 10 * FROM Member where Name ='+ #a
--SQL: Select TOP 10 * FROM Member where Name ='a' OR Name like '%a';
Correct me if im wrong, SQL injection wont work in Stored Procedure is due to some precompiled factor but the above scenario was tested in query statement instead of Stored Procedure. Why still not working?
I'm not sure why you think that would work. #a is a varchar variable, so Select TOP 10 * FROM Member where Name = #a finds rows where Name is equal to the value of that variable.
If you want SQL-Server to take the value of #a and insert it into the query as code, then you need to use sp_executesql (analogous to eval in languages like Bash and Python and JavaScript):
EXECUTE sp_executesql 'Select TOP 10 * FROM Member where Name = ' + #a
SQL Injection occurs when data is confused for and interpreted as code.
This does not happen in your scenario since parameter or variable values are not directly interpreted as code - they're only at risk of being interpreted as code if you construct new code by combining strings and these parameter/variable values and then pass the entire constructed string to the system and ask it to interpret the entire string as code - via exec, sp_executesql or other such means.
Look there is no name ending with 'a'. Try like
Select TOP 10 * FROM Member where Name ='a' OR Name like '%a%'
Updated
Microsoft handle SQL injection for SQL parameters.

SELECT Query selecting values based on a value in another table

I have 2 tables
Account(AccountId, Encoding)
DeviceAccountMap(AccountId, DeviceId)
Now I need to fetch the devices from the DeviceAccountMap. I pass a list of AccountId to a stored procedure and while fetching the DeviceId from the DeviceAccountMap table I need to compare the Encoding value for each account with a particular value.
Which is the easy way to do this? I am totally lost.
The select clause in the stored procedure will look something like this:
DECLARE #Accounts [usp].[Array]
and [usp].[Array] is defined as below
CREATE TYPE [usp].[Array] AS TABLE
(
Value VARCHAR(36) NULL
)
SELECT
DeviceId,
AccountEncoding = A.Encoding
FROM
usp.DeviceControllerAccountMap DCAM
INNER JOIN
usp.Account A ON (DCAM.AccountId = A.AccountId)
WHERE
DCAM.AccountId IN (SELECT Value From #AccountIds)
AND DCAM.IsShared = 1
AND AccountEncoding LIKE A.Encoding + '.%'
In other words I need to fetch the encoding value for each account and use that in this where clause.
So you can look up information on Table-Valued Parameters (TVPs) in T-SQL.
Here is an article by Erland Sommarskog.
You can refer to this StackOverflow answer to see an example of C# code calling a stored procedure that uses a TVP. I believe TVPs require SQL Server 2008 or higher.
TVPs, as far as I understand, provide a way to make your own data type in sql server that gets treated as if it was a table. You're doing this when you declare your Array type and then when you use the #AccountIds in your stored procedure's select statement.
CREATE TYPE [usp].[Array] AS TABLE -- maybe choose a more descriptive name than 'Array'
(
Value VARCHAR(36) NULL -- choose a more descriptive name than 'Value'
)
CREATE PROCEDURE [usp].[your_procedure_name]
#AccountIds [usp].[Array] READONLY -- use TVP as a parameter
AS
SELECT …
It is not clear form your question details whether you also mean to have a parameter in the stored procedure for the Encoding. It seems like you're looking for accounts whose Encodings start with a period '.'.
So first, create your type, like you're doing.
Then create your stored procedure.
Then test your stored procedure, something like this:
DECLARE #mylist Array -- make TVP sample data
INSERT #mylist(Value) VALUES(1),(11),(27),(123) -- insert some values
exec your_procedure_name #mylist -- run stored procedure
The following line is completely unnecessary. The JOIN to Account does this filter for you.
DCAM.AccountId IN (SELECT Value From #AccountIds)
Or am I missing something?

Error in SQL stored procedure

I am getting the following error when I execute my stored procedure:
Msg 102, Level 15, State 1, Line 6Incorrect syntax near '2011'.(1 row(s) affected)
Here is the stored procedure:
ALTER PROCEDURE [dbo].[DeliveryFileNames]
AS
BEGIN
SET NOCOUNT ON;
declare #SQL nvarchar(4000)
Create Table #DelivTemp(
Style nvarchar(50),
Material nvarchar(50),
Filename nvarchar(100),
delivered_date date)
set #SQL=
N'insert into #DelivTemp
Select distinct Style,Material,filename
from OPENQUERY(GCS_PRODUCTION,
''SELECT LEFT(FILENAME,locate(''''_'''',FILENAME)-1)as Style,
substring_index(filename,''''_'''',2)as Material,filename,
delivered_date FROM view_delivery_log
where delivered_date > ''2011%'' order by Style '')'
exec (#SQL)
drop table dbo.DelivFN
Select * into dbo.DelivFN
from #DelivTemp
END
I am using OpenQuery to update a SQL table from a linked server on SQL Server 2008 R2.
I know that the underscore is a real issue, but I have tried a plethora of options including \, % and both single and double quotes.
Regardless I am getting the same result. I can run the query independently of the stored procedure and achieve the correct results. The filename field referenced several times is formatted 00000000_ABC4_A.png. I am using the underscore to identify the components of the file name that I need for my reporting purposes.
In addition to the the logical error of your date comparison using the % that the others have pointed out, your current issue is a syntactical error.
Since you've got a dynamic sql statement contained within another dynamic sql statement... you'll need to double-escape all of your single quotes... which you did in most of the query, except for the following line:
where delivered_date > ''2011%'' order by Style '')'
Properly escaped, would be:
where delivered_date > ''''2011%'''' order by Style '')'
Which raises the question... why are you building up the string to execute dynamically, instead of just calling the statement directly?
It's the syntax of ''2011%''. This is not a valid date. % being a wildcard means the compiler can't know what to compare against in the WHERE clause. You'd need to use an actual date: i.e. ''2011_01_01'' so the compiler can know what to compare against
I believe the stored proc exec runs under a different session, therefore you won't have access to the temp table anyway. So, it won't matter if you get that sql statement to run. You could always use YEAR(delivered_date) > 2011.
Another approach would be to use the fqn for the linked server to select into and bypass the temp table all together:
SELECT LEFT(FILENAME,locate('_',FILENAME)-1)as Style,
substring_index(filename,'_',2)as Material,filename,delivered_date
FROM [linked_server_name].[db_name].[dbo].view_delivery_log
into dbo.DelivFN

MySQL - Using stored procedure results to define an IN statement

I'd like to use a stored procedure to define the IN clause of a select statement.
This is (a simplified version of) what I'm trying to do:
SELECT *
FROM myTable
WHERE columnName IN (CALL myStoredProc)
myStoredProc performs some complicated logic in the database and returns a list of possible matching values for columnName. The statement above does not work obviously. The select statement may be performed in another stored procedure if that makes a difference.
Is this at all possible in mySQL?
What return type does your current stored procedure have? You are speaking of "a list", so TEXT?
Maybe there's an easier way, but one thing you can do (inside another stored procedure) is to build another query.
To do that, we need to work around two limitations of MySQL: a) To execute dynamic SQL inside a stored procedure, it needs to be a prepared statement. b) Prepared statements can only be created out of user variables. So the complete SQL is:
SET #the_list = myStoredProc();
SET #the_query = CONCAT('SELECT * FROM myTable WHERE columnName IN (' , #the_list , ')');
PREPARE the_statement FROM #the_query;
EXECUTE the_statement;
If you're talking about returning a result set from a stored routine and then using it as table, that is not possible. You need to make a temporary table to work around this limitation.