January 9, 2006 at 1:25 am
Hi
I am pretty new to stored procedures, but an old hand at crafting select statements by hand. I have an ASP.net system (using VB) that must display 150 different Crystal Reports. I have tried various different methods to feed these reports their needed data, but in the end it seems to be best to use stored procedures .
On to the actual problem.
I need my stored procedure to return a recordset to its report. this is pretty easily done through a simple "select" SP, which just basically encapsulates a select command. However, I need to be able to modify the "where" clause of the selection based on parameters. For example,
CREATE PROCEDURE spMyproc(@param1 int) AS
SELECT * FROM Users WHERE Username = @param1
GO
would be the basic way. Now what I want is for it to disregard the Username filter if @param1 is null. What effective manner is there for me to implement that? My select statement is quite large, and there are multiple parameters that need to be considered as above, so making a block if statement is not feasible.
January 9, 2006 at 1:54 am
This is one way:
where username = IsNull(@param1, username)
if @param1 is NULL, the IsNull function returns username, which is, of course, always equal to itself.
The absence of evidence is not evidence of absence
- Martin Rees
The absence of consumable DDL, sample data and desired results is, however, evidence of the absence of my response
- Phil Parkin
January 9, 2006 at 2:32 am
Ah, great! Thanks Phil. That should work great for the simple filters. Unfortunately, I just realized that I have a couple more intricate filters to do as well {stuff like WHERE left(field4, len(field2)-2) = @param1}.
I thought I'd try to solve the abovementioned problem by doing the "basic" (without conditional filters) selection into a temp table (#temptable1). Then, I would run through each conditional filter and do a re-selection into a #temptable2, and finally just drop the results of that selection back into #temptable1. The SQL I used looked like this:
.....
CREATE PROCEDURE CR_Selection @param1 varchar(50) = NULL
AS
SELECT
table1.field1, table1.field2, table2.field3, table3.field4
INTO #temptable1
FROM table1 INNER JOIN table2 ON table1.table1id = table2.table2id INNER JOIN table3 ON table2.table2id = table3.table3id
IF @param1 IS NOT NULL
BEGIN
SELECT *
INTO #temptable2
FROM #temptable1
WHERE left(field4, len(field2)-2) = @param1
DROP TABLE #temptable1
SELECT * INTO #temptable1 FROM #temptable2
DROP TABLE #temptable2
END
GO
.....
but when I try to verify my SQL, the Stored Procedure Syntax Checker throws an error for the third select (back INTO #temptable1), saying that #temptable1 already exists. I thought that the DROP TABLE #temptable1 would work... What am I doing wrong here?
January 9, 2006 at 2:34 am
Sorry, I just realized that your solution is still valid, even for the more complex filter. However, I would like to know the reason for the error (assuming someone can help) for interest sake.
Thanks for the help!
January 9, 2006 at 2:34 am
Another way is to use default values on your parameters of '%'.
So the above example becomes:
where username like @param1
if @param1 has a value of '%' (instead of null) then it will return everything. Note that this works for integers too.
The only problem with this solution is that you won't be making the best use of any indexes. However, tith the IsNull solution you'll be forcing a scan instead because of the use of a function in the WHERE clause so you're in pretty much the same situation.
What I don't know is which is the better solution in terms of performance.
Ultimately, if you want the best possible performance you probably have to resort to using IF...ELSE statements.
Hope that helps,
January 9, 2006 at 2:41 am
It's all about how the compiler works as it tries to optimise the code. God knows what's going on in detail though.
The absence of evidence is not evidence of absence
- Martin Rees
The absence of consumable DDL, sample data and desired results is, however, evidence of the absence of my response
- Phil Parkin
January 9, 2006 at 2:52 am
Hmmm.
Thanks for everyone's suggestions. I am not overly concerned about performance here - Crystal's method of querying is far less efficient than the most basic SQL filter (it normally selects EVERYTHING in each referenced table, and then do the joins internal to Crystal!), so pretty much anything I do here will improve.
So there isn't anything wrong with my logic there, right?
Please note, I will probably not use that hack, but it's an interesting method which appeals to the programmer in me, and should be useful for more general cases (value @param1 for instance, or some other arbitrary case).
January 10, 2006 at 8:50 am
Try this:
CREATE PROCEDURE spMyproc(@param1 int) AS
DECLARE @SQL_String varchar(4098)
If ISNULL(@param1,'') = ''
BEGIN
SET @SQL_String = 'SELECT * FROM Users'
END
IF CHARINDEX('A',@param1,1) > 0
BEGIN
SET @SQL_String = 'SELECT * FROM Users WHERE USername = ' + LEft(@param1,CHARINDEX('A',@param1,1))
END
(and so on)......
Exec (@SQL_String 
GO
Hopefully you get the idea from above.
Rick Sheeley, DBA, ETelecare, Scottsdale, AZ
January 10, 2006 at 9:44 am
This will keep you from having to duplicate your column names / complex formulae on both sides of the = operator.
If @param1 is null, query planner just pulls everything instead of isNull()-ing the parameter to the column/formula for each row, to boot.
CREATE PROCEDURE spMyproc(@param1 int) AS
SELECT
*
FROM
Users
WHERE
left(field4, len(field2)-2) = @param1 OR @param1 IS NULL
GO
January 11, 2006 at 1:53 am
Excellent! Thanks everybody for your help, these are a bunch of really valuable tips. I prefer the @SQLString method, for it's flexibility, even though it can take longer to execute (SQL Server takes time to do conditionals and string concatenations it would seem), and all the other tips will come in handy too.
My procedure has been crafted, and it works well, thanks to y'all.
Viewing 10 posts - 1 through 9 (of 9 total)
You must be logged in to reply to this topic. Login to reply