How do you write the SQL for a PreparedStatement using a WHERE x IN clause? - sql

I have a query that looks like this:
SELECT last_name,
first_name,
middle_initial
FROM names
WHERE last_name IN ('smith', 'jones', 'brown')
I need to be able to parameterize the list in the IN clause to write it as a JDBC PreparedStatement. This list could contain any number of names in it.
Is the correct way to do this:
SELECT last_name,
first_name,
middle_initial
FROM names
WHERE last_name IN (?)
and then build a list of parameters? Or is there a better (more correct) way to do that?

In short, you can't out of the box. However, with Spring you can do what you want. See How to generate a dynamic "in (...)" sql list through Spring JdbcTemplate?

Standard SQL doesn't allow the IN clause to be parameterized into a single variable -- only dynamic SQL, the SQL query being constructed as a string prior to execution with the comma separated list of values is supported.

I'm going to research this topic, as well. I've been guilty of writing similar code and never felt 100% comfortable with it. I suppose I'd like to find something on "variable SQL parameter lists".
In code, using hibernate, and given a String of comma-delimited order Ids, I've used:
Session s = getSession();
Criteria crit = s.createCriteria(this.getOrderListingClass());
crit.add(Expression.sql(String.format("{alias}.orderId in (%s)", orderIds)));
crit.add(Expression.eq("status", OrderInfo.Order_STATUS_UNFILLED));
orders = crit.list();
Whereas orderId is really part of a "SELECT x FROM y WHERE IN (%s)".
I did run the orderIds String through a validator prior to passing it to hibernate - being fearful of injections, etc.
Something else that I've been meaning to do is check the limit on SQL parameters and number of characters in the query. I seem to recall hitting a limit somewhere around 2000+ (with MS SQL). That's something to consider if you go with this approach.
I think this is kludgy... to be passing off that many Ids in a Where-clause, but it's a section of code that needs refactoring. Thankfully, the use case has only seen a handful of Ids queried at any one time.

You could also construct your query as a stored procedure that takes the parameterized list as a varchar. For example, in sql server:
CREATE PROCEDURE dbo.[procedure_name]
#IN_LIST VARCHAR(MAX)
AS
BEGIN
DECLARE #SQL VARCHAR(MAX)
SET #SQL = '
SELECT last_name,
first_name,
middle_initial
FROM names
WHERE last_name IN (' + #IN_LIST + ')'
EXECUTE(#SQL)
END
Just make sure your #IN_LIST is formatted as a string that includes the single quotes and commas. For example in java:
String inList = "'smith','jones','brown'";

If You use MS SQL Server, try reshape your TSQL to use UDF, Maybe this my post can help You

Related

Using Regex to determine what kind of SQL statement a row is from a list?

I have a large list of SQL commands such as
SELECT * FROM TEST_TABLE
INSERT .....
UPDATE .....
SELECT * FROM ....
etc. My goal is to parse this list into a set of results so that I can easily determine a good count of how many of these statements are SELECT statements, how many are UPDATES, etc.
so I would be looking at a result set such as
SELECT 2
INSERT 1
UPDATE 1
...
I figured I could do this with Regex, but I'm a bit lost other than simply looking at everything string and comparing against 'SELECT' as a prefix, but this can run into multiple issues. Is there any other way to format this using REGEX?
You can add the SQL statements to a table and run them through a SQL query. If the SQL text is in a column called SQL_TEXT, you can get the SQL command type using this:
upper(regexp_substr(trim(regexp_replace(SQL_TEXT, '\\s', ' ')),
'^([\\w\\-]+)')) as COMMAND_TYPE
You'll need to do some clean up to create a column that indicates the type of statement you have. The rest is just basic aggregation
with cte as
(select *, trim(lower(split_part(regexp_replace(col, '\\s', ' '),' ',1))) as statement
from t)
select statement, count(*) as freq
from cte
group by statement;
SQL is a language and needs a parser to turn it from text into a structure. Regular expressions can only do part of the work (such as lexing).
Regular Expression Vs. String Parsing
You will have to limit your ambition if you want to restrict yourself to using regular expressions.
Still you can get some distance if you so want. A quick search found this random example of tokenizing MySQL SQL statements using regex https://swanhart.livejournal.com/130191.html

Passing muliple values ssrs sql

I have the following sql:
--DECLARE #absent nvarchar(20)
--SET #absent = 'Y' -- Not needed in sql as we are setting this in ssrs
SELECT *
FROM Employee
WHERE Absent in #Absent
Here is the employees table.
Employee
Name Absent
Dan Y
Laura N
Ross N
James Y
I want to be able to filter between Y and N and both.
This is working fine for me in SSRS when I pass #absent both Y and N. However, when i convert to a stored procedure and run in SSRS, I now get an issue. I longer get any results.
ALTER PROCEDURE usp_GetEmployees
#Absent nvarchar(20)
SELECT *
FROM Employee
WHERE Absent in #Absent
I've tried the line =join(parameters!Absent.value,",") in the parameter properties but no luck. I believe the issue is with the data type that is being passed in. It is just odd that it works before i converted to a usp.
Any help is much appreciated :)
Assuming you had a typo in your question and you meant to write
SELECT *
FROM Employee
WHERE Absent in (#Absent)
The reason that this WILL work when used directly in a dataset query is that SSRS will take your multi-valued parameter's selected options, convert them into a comma separated list and inject them into the SQL. So what will actually be executed will be, for example,
SELECT *
FROM Employee
WHERE Absent in ('Y', 'N')
This process is not the same when calling a stored procedure as SSRS cannot change the code inside your SP.
When passing multi-value parameters to a Stored Proc you would typically pass in an expression such as =JOIN(Parameters!myParam.Value, ",") . This passes in a string containing a comma separated list of your selected parameter values.
Inside the Stored Proc you would typically call a function to split this string back out into a table that you can join to.
There are plenty or articles describing this and providing a usable split function in case you don't have one. Check out the answers here https://social.msdn.microsoft.com/Forums/sqlserver/en-US/0ead7ceb-3fdd-4625-aa82-1d4195f984b1/passing-multivalue-parameter-in-stored-procedure-ssrs?forum=sqlreportingservices
Your original code wouldn't work directly in SQL. You can fix it using parentheses:
WHERE Absent in (#Absent)
This is exactly equivalent to:
WHERE Absent = #Absent
which is probably not what you intend. In the more recent versions of SQL Server, you can use:
WHERE Absent IN (SELECT value FROM string_split(#Absent, ','))
The ',' is my guess at the separator you are using. You can also use:
WHERE ',' + #Absent + ',' LIKE '%,' + Absent + ',%'
With SSRS, if using a Stored Procedure, the format Column IN (#Variable) doesn't work. That type of query only works if the SQL in directly in the SSRS Data Set's definition. Then SSRS will replace the value of #Variable and (apparently) securely inject the values in a delimited and quoted list.
For something as simple this, you are, in truth, better off likely just putting the below statement in the dataset in SSRS:
SELECT *
FROM Employee
WHERE Absent in (#Absent);
If not, and you need to use a Stored Procedure, then SSRS passes a delimited string as the parameter. In such cases you need to use a splitter. Considering you tag BIDS (visual-studio-2008) then you need to use a really old way of doing this. I also strongly recommend you look at upgrade paths ASAP.
For 2008, then I recommend delimitedsplit8K (or delimitedsplitN4K if using an nvarchar). Then your Procedure will look like this:
ALTER PROCEDURE usp_GetEmployees #Absent nvarchar(4000) AS
BEGIN
SELECT E.* --YOu should replace this with your actual columns
FROM Employee E
JOIN dbo.delimitedsplitN4K(#Absent,',') DS ON E.Absent = DS.item;
END;

How to manipulate multi-value string parameters for a SQL Command in Crystal Reports

I have a Crystal Report based on a SQL Command that, in part, consists of procedure names. What I'm trying to do is add a multi-value string parameter to the SQL Command such that the end users can enter partial procedure names and the report will return only those relevant procedures that string match.
For example, a user should be able to enter "%KNEE%" and "%HIP%" into the parameter and return all procedures that contain the words "KNEE" and "HIP". The problem is that I can't figure out how to manipulate the parameter value in the SQL to accomplish this. I've done this before with a report parameter (as opposed to a SQL Command parameter) by simply adding the line {table.procedure_name} like {?name match parameter} to the record selection formula, but taking the same approach in the SQL Command gets me an "ORA-00907: Missing right parenthesis" error.
Any suggestions on how I can manipulate the multi-value string parameter to accomplish this?
I dont like to post this as an answer because I don't care for the solution however it is the only way I have found to work around this.
I have had to instruct users to enter '%KNEE%','%HIP%','%ETC%' at the parameter prompt. Then the {table.procedure_name} like {?name match parameter} should work in your SQL. Not optimal, especially for your scenario with the %. I would love to hear someone provide a better solution because I have wrestled with this many times.
Here's an approach:
SELECT column0
FROM table0
INNER JOIN (
SELECT trim('%' || x.column_value.extract('e/text()') || '%') SEARCH
FROM ( SELECT 'arm,knee' options FROM dual ) t,
TABLE (xmlsequence(xmltype('<e><e>' || replace(t.options,',','</e><e>')|| '</e></e>').extract('e/e'))) x
) v ON column0 LIKE v.search
Use Oracle's XML functionality to convert a comma-delimited string to an equivalent number of rows, wrapping each clause with %%. Then join those rows to the desired table.
To use with CR, create a single-value, string parameter and add it to the code:
...
FROM ( SELECT '{?search_param}' options FROM dual ) t,
...

SQL SELECT Statement - Multiple IDs in IN Clause [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Parameterizing an SQL IN clause?
Every now and then I work on a system that allows the user to select multiple items and then perform a bulk action on them. Typically, I resorted to building the SQL at runtime, something like this:
string inClause = String.Join(", ", selectedIds);
string command = "SELECT * FROM Customer WHERE CustomerId IN ({0})";
command = String.Format(command, inClause);
Of course, this style of code is insecure because of SQL injection. I could solve that by putting in parameter placeholders and creating parameters.
Still, I am wondering if there is another approach that I've just not considered. I certainly don't want to execute the command once for each ID.
There are two good approaches:
Build the string with command placeholders (like you said)
Join to the values of a TVP
Burning the IDs into the SQL is not good because it prevents plan caching and opens the potential for injection.
You can build an XML string and then pass it to a stored proc. Executing it would look like:
EXECUTE getLocationTypes '<IDList><ID>1</ID><ID>3</ID></IDList>'
The stored proc would look something like:
create proc [dbo].[getLocationTypes](#locationIds XML)
as
begin
set nocount on
SELECT locationId, typeId
FROM xrefLocationTypes
WHERE locationId
IN (SELECT Item.value('.', 'int' )
FROM #locationIDs.nodes('IDList/ID') AS x(Item))
ORDER BY 1, 2
end
Notice the data type of the parameter is XML. This is a little more complicated than what you are doing, guess you could do it all in a single SQL string.

Dynamic Query in SQL Server

I have a table with 10 columns as col_1,col_2,.... col_10. I want to write a select statement that will select a value of one of the row and from one of these 10 columns. I have a variable that will decide which column to select from. Can such query be written where the column name is dynamically decided from a variable.
Yes, using a CASE statement:
SELECT CASE #MyVariable
WHEN 1 THEN [Col_1]
WHEN 2 THEN [Col_2]
...
WHEN 10 THEN [Col_10]
END
Whether this is a good idea is another question entirely. You should use better names than Col_1, Col_2, etc.
You could also use a string substitution method, as suggested by others. However, that is an option of last resort because it can open up your code to sql injection attacks.
Sounds like a bad, denormalized design to me.
I think a better one would have the table as parent, with rows that contain a foreign key to a separate child table that contains ten rows, one for each of those columns you have now. Let the parent table set the foreign key according to that magic value when the row is inserted or updated in the parent table.
If the child table is fairly static, this will work.
Since I don't have enough details, I can't give code. Instead, I'll explain.
Declare a string variable, something like:
declare #sql varchar(5000)
Set that variable to be the completed SQL string you want (as a string, and not actually querying... so you embed the row-name you want using string concatenation).
Then call: exec(#sql)
All set.
I assume you are running purely within Transact-SQL. What you'll need to do is dynamically create the SQL statement with your variable as the column name and use the EXECUTE command to run it. For example:
EXECUTE('select ' + #myColumn + ' from MyTable')
You can do it with a T-SQl CASE statement:
SELECT 'The result' =
CASE
WHEN choice = 1 THEN col1
WHEN choice = 2 THEN col2
...
END
FROM sometable
IMHO, Joel Coehoorn's case statement is probably the best idea
... but if you really have to use dynamic SQL, you can do it with sp_executeSQL()
I have no idea what platform you are using but you can use Dynamic LINQ pretty easily to do this.
var query = context.Table
.Where( t => t.Id == row_id )
.Select( "Col_" + column_id );
IEnumerator enumerator = query.GetEnumerator();
enumerator.MoveNext();
object columnValue = enumerator.Current;
Presumably, you'll know which actual type to cast this to depending on the column. The nice thing about this is you get the parameterized query for free, protecting you against SQL injection attacks.
This isn't something you should ever need to do if your database is correctly designed. I'd revisit the design of that element of the schema to remove the need to do this.