Passing muliple values ssrs sql - 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;

Related

Is it possible to add a multiple value parameter query in SSRS with left()?

I have a query in SSRS where I can search for a title with a certain prefix. The reason why I used left() is because there are some prefixes that have an underscore or a dash so I need my search results to be exact and it wasn't giving me that when I used the like operator.
Here's an example of what my query looks like:
select title
from table1
where left(title, len(#prefix)) = #prefix
This works if my prefix dropdown doesn't allow multiple values. Now, I have also tried this query:
select title
from table1
where left(title, len(#prefix)) in (#prefix)
But this just caused my report to generate a "Query execution failed for dataset" however, this query does work when I try it on SQL Server and when it isn't allowed for multiple values.
As I mentioned in my comment, I hate that SSRS allows the syntax IN (#Parameter); it causes misunderstandings and teaches people incorrect syntax.
When you have statement like SELECT * FROM dbo.MyTable WHERE MyColumn IN (#MyParameter); the SQL that SSRS runs is not that statement. Instead SSRS removes the parameter and then injects the multiple values into the statement. So the statement it actually runs would be something like SELECT * FROM dbo.MyTable WHERE MyColumn IN (N'MyValue1',N'MyValue2',N'MyValue3');
If you were to actually run the SQL SELECT * FROM dbo.MyTable WHERE MyColumn IN (#MyParameter); in SSMS, or any other product, it would be a synonym of SELECT * FROM dbo.MyTable WHERE MyColumn = #MyParameter;. The syntax that SSRS uses is, for all intensive purposes, wrong.
There are multiple problems with that, which I'm not going to cover here.
When you then have a query like you do LEFT(title, LEN(#prefix)) IN (#prefix) you end up with a problem; LEN(#prefix) can't have tuples injected into it (LEN(N'MyValue1',N'MyValue2',N'MyValue3') isn't valid syntax). This just results in a invalid query because SSRS teaches bad habits.
The solution to this is th force SSRS to not be (completely) dumb. Unfortunately SSRS is dumb and still (even in SSRS 2022!) doesn't support Table Table Parameters (TVPs) which would trivialise this. As such you'll need to have SSRS pass a delimited value to a Procedure, and then you can handle the logic in your procedure. As you're using SSRS 2019 then you're also using SQL Server 2019 and thus have access to STRING_SPLIT. Also we can switch to a LIKE here.
This means you need a procedure like the following:
CREATE PROC dbo.YourProc #prefix nvarchar(4000) --Assumed data type
AS
BEGIN
SELECT title
FROM dbo.table1 t1
WHERE EXISTS (SELECT 1
FROM STRING_SPLIT(#prefix,',') SS
WHERE t1.title LIKE SS.value + '%');
END;
Then, instead, you execute the procedure from your report.

Query not returning same results local vs openquery

I have an Oracle database linked to a SQL Server database.
Whenever I perform a specific query in Oracle I get expected data.
The fields I'm selecting are
SELECT
J.JOBNUMBER AS JOBNUMBER,
J.OPENDATE AS OPENDATE,
S.SERVICEREPORTNUMBER AS SERVICEREPORTNUMBER,
F.SSO AS SSO,
S.DATEOFVISIT AS DATEOFVISIT,
CASE WHEN J.SOURCE='MUST' THEN SRVCD.DACRENR ELSE W.DEBRIEFDATE END as "DEBRIEFDATE",
J.CLOSEDDATE AS CLOSEDDATE,
INITCAP(HR.COUNTRY_NAME) as "COUNTRY",
F.NAME AS NAME,
SY.MODALITY AS MODALITY,
HR.MANAGER_SSOID AS MANAGER_SSOID,
HR.MANAGER_NAME AS MANAGER_NAME
If I do the same query with openquery I get null values on the column which corresponds to
CASE WHEN J.SOURCE='MUST' THEN SRVCD.DACRENR ELSE W.DEBRIEFDATE END as "DEBRIEFDATE"
In this case the column holds dates and the ones that correspond to SRVCD.DACRENR are being shown and the W.DEBRIEFDATE dates are not.
I don't know why I'm seeing those values correctly in Oracle but not in the openquery result. I tried changing the values with TO_CHAR and TO_DATE and all scecnarios I see the values in Oracle but not in SQL Server.
Could you try a convert to varchar on your date so it match the same datatype?
Or post some sample data
Convert(varchar(10), your field,105)
Not sure how you declare your variables in OPENQUERY. There is one direct way where you don't use any variables to store the query for latter execution, and indirect way which is the opposite way of direct way. Most of the times, indirect way is the best way in order to get the desired results.
First you will need to declare some variables and execute it using EXEC() function. Let's see how it is done.
DECLARE #ORACLE_ENV nvarchar(20) = 'YOUR_ORACLE_DB_INSTANCE_HERE'
DECLARE #ORACLE_SQL nvarchar(max)
SET #ORACLE_SQL = 'SELECT * FROM OPENQUERY ('+#ORACLE_ENV+',''' +SELECT
J.JOBNUMBER AS JOBNUMBER,
J.OPENDATE AS OPENDATE,
S.SERVICEREPORTNUMBER AS SERVICEREPORTNUMBER,
F.SSO AS SSO,
S.DATEOFVISIT AS DATEOFVISIT,
CASE WHEN J.SOURCE=''MUST'' THEN CONVERT(VARCHAR(15), SRVCD.DACRENR, YOUR_PREFERRED_DATE_FORMAT) ELSE CONVERT(VARCHAR(15), W.DEBRIEFDATE, YOUR_PREFERRED_DATE_FORMAT) END as DEBRIEFDATE,
J.CLOSEDDATE AS CLOSEDDATE,
INITCAP(HR.COUNTRY_NAME) as COUNTRY,
F.NAME AS NAME,
SY.MODALITY AS MODALITY,
HR.MANAGER_SSOID AS MANAGER_SSOID,
HR.MANAGER_NAME AS MANAGER_NAME + ''')'
EXEC sp_executesql #ORACLE_SQL
Points to remember:
Everytime you filter something based on a value, it should always be inside double quotes ('').
SQL dates and ORACLE date formats are different. You have to convert it to varchar first.
Always be careful for the quotes that you are adding. Even one single quote can mess the entire OPENQUERY query.
The CONVERT() above has YOUR_PREFERRED_DATE_FORMAT which means you have to set the preferred date format according to your requirement. Here is the complete reference just in case if you get stuck: https://www.mssqltips.com/sqlservertip/1145/date-and-time-conversions-using-sql-server/
Hope this helps.

How to select all parameter values in SSRS?

I have a parameters #Year and #Month in my report, where month has assigned values like January(label): 1(values), February: 2, ...
My datased is feeded by stored procedure which has a filter
WHERE (cal.CalendarYear = #Year) AND (cal.MonthId = #Month)
When I check 'Allow multiply values' in parameter settings, it returns an error
Error converting data type nvarchar into int.
How can I select all values (by default)?
If you don't need to use a Stored Proc you can easily solve the problem.
Copy the SQL from your stored proc (excluding the PROC definition, just the main code) and paste it in as your dataset query.
Change your = #Year and = #Month to IN(#Year) and IN(#Month)
That's all there is to it, no joining, splitting or anything else.
SSRS will inject the parameter values as comma separated values correctly for you.
When you select multiple values, you pass the parameter to the procedure as an expression with Join().
Then in your procedure change your month parameter to be of type nvarchar(max).
Create a temp table and use a t-sql split string function (Google this, there are so many varieties but if you're stuck we can find one) to split the string into a table. Then inner join to your table in your procedure to filter on the selections.
Your error message about "nvarchar into int" suggests a data type mismatch between your SSRS parameter and your MonthId column. Check the Report Parameter Properties -> General -> Data type for the former and your table schema for the latter, and make sure they're either both text/varchar or both integers.
Allowing your query to handle multiple parameter values should be much simpler than needing to use joins and splits. Just use IN instead of =, and put your parameter name inside a set of brackets.
AND (cal.MonthId IN (#Month))
To set the defaults for your parameter, go to the Report Parameter Properties -> Default Values. Select the Specify values option, and add your numbers 1-12 as separate Value records.

Multi-value parameter truncation

On my SSRS report I have a multi-value parameter which contains 250+ uniqueidentifier data type values. This works fine with a small selection of values in the parameter dropdown, but when user chooses (select all), they get this error:
An error occurred during local report processing.
String or binary data would be truncated.
Each uniqueidentifier field is 36 characters long, which means 250 of them added together result in a 9000 character string. This is what causes the truncation to occur.
What approach should I take to handle this situation?
Edit:
Couple snapshots of the stored procedure:
ALTER PROCEDURE [dbo].[spReport]
#StartDate as datetime
,#EndDate as datetime
,#LocationId uniqueidentifier
,#UserIds uniqueidentifier
#UserIds is the multi-value parameter. It is used in the where clause of the query:
WHERE (U.UserId IN (#UserIds) OR #UserIds IS NULL)
You can't use an SSRS multi-value parameter with a stored procedure like that. You'll need to join the values in the report, pass them as a varchar(max), and then split them in the stored procedure:
https://stackoverflow.com/a/9862901/124386
http://www.codeulike.com/2012/03/ssrs-multi-value-parameters-with-less.html
SSRS does have a limit on the size of multi-value parameters. I can't remember what it is off the top of my head, but I think you are well beyond it. (SSRS converts the multi-value parameter to a comma separated string and replaces the occurances of the variable name in the query with the string.)
So as mentioned in the comments, you've got two problems:
SP's can't take multi-value parameters directly from SSRS. You'll need to do some manipulation.
Your overall parameter length. This may require a little bit of creativity to solve. Some options:
Can you supply either a separate parameter or a special value in your existing parameter for <All Users> and then check for this in the SP, returning all values in that case. If the query is directly in SSRS (instead of a SP) something like this would work:
...WHERE ( U.UserId in ( #UserIds) OR '<All Users>' in ( #UserIds ) )
...
Can you filter the number of items in your parameter, based on earlier parameters? Such as have the user select a date range and/or department, and only return UIDs that match that range?
Another approach is to create a user defined table type and use that instead of a varchar to pass in the selected values.

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

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