I have designed a function that works with an SSRS report. I have a drop down parameter that lists multiple items and only one can be selected. This drop down gets its data from a query/data set, and I added one line of data that says 'All' in it. So the dropdown will look like this:
Item1
Item2
Item3
All
And then in the function, I make one small change in the where clause:
...where (#parameterName = 'All' or table.name = #parameterName)
The problem with this is that table.name has about 50000 rows of data. When the user selects 'All' in the drop down, I would have thought that since the first statement in brackets is true, and that the next statement (after the 'or') should not even be executed. But it causes the query to run for 5-20 minutes and still does not produce any result after that long. If I simply change the where clause to
...where (#parameterName = 'All')
The function runs in less than a second, if the user still selects 'All' from the drop down.
I implement a similar concept with another filter but I guess because the table that that parameter uses is much smaller (about 90 rows), so it doesn't take long.
Is there basically a way to have an optional parameter that is not expensive to calculate?
EDIT: I will add that the parameter is declared as nvarchar(max). Will changing this to something smaller help the query?
What you have there is a catch-all query. Consider adding OPTION (RECOMPILE) to the end of your statement. This'll force the engine to recreate the plan each time it runs the query, meaning it won't use poor choices based on a previous run where your variable has a value like 'Item1'.
Related
I have a SSRS report. there is a long SQL query on the main query, in the last SELECT I want to filter the results with WHERE expression, the filter should be with a multi value parameter.
I set the parameter in this way:
Create a new Dataset with a query.
Add a new parameter to the Parameters folder (with name NewParam).
Check the "Allow multiple values" checkbox.
Add the parameter to the "Main Query" and set the value with this expression:
=Join(Parameters!NewParam.Value,",")
At the end of the Main Query I filter the results:
select *
from #FinalStatusTbl
where Test_Number in (#NewParam)
order by Priority
The problem is:
On the report when I choose one value from the list I got expected results, but If I choose multi values the results are empty (not got an error.)
Do you have any idea why?
(When I try this: where Test_Number in ('Test 1', 'Test 2') it works well).
When you create a dataset with a sql query, multi valued parameters work with the in(#ParamName) without any changes.
Replace your =Join(Parameters!NewParam.Value,",") with just =Parameters!NewParam.Value and you should be fine.
That said, the reason you see people using that join expression is because sometimes your query will slow down considerably if your parameter has a lot of potential selections and you data is reasonably large. What is done here is to combine the join expression with a string splitting function in the dataset that converts the resulting Value1,Value2,Value3 string value in a table that can be used in the query via inner join.
This is also a requirement if passing multiple values as a parameter to a stored procedure, as you can't use the in(#ParamName) syntax.
You could try taking the parameter out of the where clause and use the parameter in the filters section of the dataset properties.
This will effectively shift the filtering from the SQL to SSRS.
What you need to do is split your string in the database. What is being passed to your query is 'Test 1, Test 2' as a complete string, NOT 'Test 1' and 'Test 2'. This is why a single value works, and multiple values do not.
Here is a really good link on how to split strings, in preparation for your scenario. The function I most often use is the CTE example, which returns a table of my split strings. Then I change my SQL query to use IN on the returned table.
In your example, you will want to write WHERE Test_Number IN (SELECT Item FROM dbo.ufn_SplitStrings(#NewParam) , where ufn_SplitString is the function you create from the link previously mentioned.
This is what I did and it works well to me. You can try it also.
=sum(if(Fields!Business_Code.Value = "PH"
and (Fields!Vendor_Code.Value = "5563"
and Fields!Vendor_Code.Value = "5564"
and Fields!Vendor_Code.Value = "5565"
and Fields!Vendor_Code.Value = "5551")
, Fields!TDY_Ordered_Value.Value , nothing ))
I have built an SSRS report, but am unable to get over one hurdle. It may be simple, I am just unsure of how to solve this.
I have a parameter drop down that allows the user to select one of two options, if they select one option the a portion of the where clause should change, and if the other is chosen it would change again. I will post it below :
If master is chosen :
WHERE myId=1 AND myJournal IS NULL
If Idol is chosen :
WHERE myId=1
So basically just adding a condition and taking a condition based on parameter selection....
This seems very rudimentary, I just can't seem to figure it out.
Thanks for any input
How about
WHERE myId=1 AND (myJournal IS NULL OR #Parameter = 'Idol')
assuming your parameter value get set to Idol of course
The way this works is:
If #Parameter = 'Idol', then the OR clause in the brackets will always be true, regardless of the myJournal value. This reduces the entire WHERE clause to WHERE myId=1 AND true, which is the same as WHERE myId=1.
However if #Parameter = 'master', then the OR clause in the brackets will only be true if myJournal IS NULL as #Parameter = 'Idol' is always false. This means the OR clause can be thought of as equivalent to simply myJournal IS NULL.
This reduces the entire WHERE clause to WHERE myId=1 AND myJournal IS NULL, which is what you are after.
You don't provide any information about the way you want to perform the query.
Do you want to do it with an ajax call?
In this case you should have a selectcombo which will set the needed query string and pass this with an ajax request to the php file that will query the database.
If you want some help we need to have some more information about what you are trying to achieve.
I was just wondering if in SQL Server if in a statement like this:
SELECT A.Field1, dbo.someFunction(A.IdentifierID) As Field
FROM Table A WHERE A.IdentifierID = 1000
Will it call someFunction for all the rows in the table, or will it call it once?
Thanks!
It will be called for every row of result
the count of calls depends on the resulted rows, i.e, if the output of the query 100 row the it will be called 100 times, not for all rows in the table
but if it were in the where clause, the it will evaluated for each row
Will call it for every row, but since you are selecting a specific one (or so it seems), it will be called once on your example.
As vlad commented you should try it and see. There are of course two options:
It could call it just once knowing IdentifierID will always be 1000.
but that might not be the right thing...
It could call it for each row - maybe the function has side-effects?
I strongly suspect #2.
Compliant SQL servers are supposed to appear as if they apply the WHERE clause before they apply the SELECT clause. So it should appear to evaluate the function at most once for each row that satisfies WHERE A.IdentifierID = 1000.
But database engines are free to optimize however they like, as long as they give you the same results the standards require. So in your case, since you're selecting a single ID number, it might only have to evaluate the function once.
How can I make Sybase's database engine return an unsorted list of records in non-numeric order?
~~~
I have an issue where I need to reproduce an error in the application where I select from a table where the ID is generated in sequence, but the ID is not the last one in the selection.
Let me explain.
ID STATUS
_____________
1234 C
1235 C
1236 O
Above is 3 IDs. I had code where these would be the results of a
select #p_id = ID from table where (conditions).
However, there wasn't a clause to check for status = 'O' (open). Remember Sybase saves the last returned record into a variable.
~~~~~
I'm being asked to give the testing team something that will make the results not work. If Sybase selects the above in an unordered list, it could appear in ascending order, or, if the database engine needs to change blocks of stored data or something technical magic stuff, the order could be messed up. The original error was when the procedure would return say 1234 instead of 1236.
Is there a way that I can have a 100% guarantee that Sybase will search over a block of data and have to double back, effectively 'breaking' the ascending search, and returning not the last record, but any other one? (all records except the maximum will end up erroring, because they are all 'Closed')
I want some sort of magical SQL code that will make sure things don't search the table in exactly numeric order. Ideally I'd like to not have to change the procedure, as the test team want to see the exact same procedure breaking (as easy as plonking a order by id desc would fudge the results).
If you don't specify an order, there is no way to guarantee the return order of the results. It will be however the index is built - and can depend on the order of insertion, the type of index, and the content of index keys.
It's generally a bad idea to do those sorts of singleton SELECTs. You should always specify a specific record with the WHERE clause, or use a cursor, or TOPn or similar. The problem comes when someone tries to understand your code, because some databases when they see multiple hits take the first value, some take the last value, some take a random value (they call that "implementation-defined"), and some throw an error.
Is this by any chance related to 1156837? :)
I've been battling this one for a while now. I have a stored proc that takes in 3 parameters that are used to filter. If a specific value is passed in, I want to filter on that. If -1 is passed in, give me all.
I've tried it the following two ways:
First way:
SELECT field1, field2...etc
FROM my_view
WHERE
parm1 = CASE WHEN #PARM1= -1 THEN parm1 ELSE #PARM1 END
AND parm2 = CASE WHEN #PARM2 = -1 THEN parm2 ELSE #PARM2 END
AND parm3 = CASE WHEN #PARM3 = -1 THEN parm3 ELSE #PARM3 END
Second Way:
SELECT field1, field2...etc
FROM my_view
WHERE
(#PARM1 = -1 OR parm1 = #PARM1)
AND (#PARM2 = -1 OR parm2 = #PARM2)
AND (#PARM3 = -1 OR parm3 = #PARM3)
I read somewhere that the second way will short circuit and never eval the second part if true. My DBA said it forces a table scan. I have not verified this, but it seems to run slower on some cases.
The main table that this view selects from has somewhere around 1.5 million records, and the view proceeds to join on about 15 other tables to gather a bunch of other information.
Both of these methods are slow...taking me from instant to anywhere from 2-40 seconds, which in my situation is completely unacceptable.
Is there a better way that doesn't involve breaking it down into each separate case of specific vs -1 ?
Any help is appreciated. Thanks.
I read somewhere that the second way will short circuit and never eval the second part if true. My DBA said it forces a table scan.
You read wrong; it will not short circuit. Your DBA is right; it will not play well with the query optimizer and likely force a table scan.
The first option is about as good as it gets. Your options to improve things are dynamic sql or a long stored procedure with every possible combination of filter columns so you get independent query plans. You might also try using the "WITH RECOMPILE" option, but I don't think it will help you.
if you are running SQL Server 2005 or above you can use IFs to make multiple version of the query with the proper WHERE so an index can be used. Each query plan will be placed in the query cache.
also, here is a very comprehensive article on this topic:
Dynamic Search Conditions in T-SQL by Erland Sommarskog
it covers all the issues and methods of trying to write queries with multiple optional search conditions
here is the table of contents:
Introduction
The Case Study: Searching Orders
The Northgale Database
Dynamic SQL
Introduction
Using sp_executesql
Using the CLR
Using EXEC()
When Caching Is Not Really What You Want
Static SQL
Introduction
x = #x OR #x IS NULL
Using IF statements
Umachandar's Bag of Tricks
Using Temp Tables
x = #x AND #x IS NOT NULL
Handling Complex Conditions
Hybrid Solutions – Using both Static and Dynamic SQL
Using Views
Using Inline Table Functions
Conclusion
Feedback and Acknowledgements
Revision History
If you pass in a null value when you want everything, then you can write your where clause as
Where colName = IsNull(#Paramater, ColName)
This is basically same as your first method... it will work as long as the column itself is not nullable... Null values IN the column will mess it up slightly.
The only approach to speed it up is to add an index on the column being filtered on in the Where clause. Is there one already? If not, that will result in a dramatic improvement.
No other way I can think of then doing:
WHERE
(MyCase IS NULL OR MyCase = #MyCaseParameter)
AND ....
The second one is more simpler and readable to ther developers if you ask me.
SQL 2008 and later make some improvements to optimization for things like (MyCase IS NULL OR MyCase = #MyCaseParameter) AND ....
If you can upgrade, and if you add an OPTION (RECOMPILE) to get decent perf for all possible param combinations (this is a situation where there is no single plan that is good for all possible param combinations), you may find that this performs well.
http://blogs.msdn.com/b/bartd/archive/2009/05/03/sometimes-the-simplest-solution-isn-t-the-best-solution-the-all-in-one-search-query.aspx