How to do a conditional where clause with where in PL/SQL within Procedure - sql

I have a pretty simple Stored Procedure that I am in trouble to do because i'm new to SQL and PL/SQL. I Have a table with a name column that is a varchar(55).
I discovered that if the user executes my procedure with an empty string as a paramter the LIKE statment brings all rows from TABLE1
SELECT *
FROM TABLE1
WHERE COLUMN LIKE VARIABLE || '%'
AND...
So I tried to change the query so if the VARIABLE is passed with a empty string it can still perform other conditions in the where statment.
SELECT *
FROM TABLE1
WHERE (VARIABLE <> '' AND COLUMN LIKE VARIABLE || '%')
AND...
But now wherever I pass as variable ('', NULL, 'anystring') I get no rows returned.
How can I build a query that validates if the variable is different of empty string and if it is it performs the LIKE statment with the variable correctly?

If I understand you correctly, it is not difficult thing to do. You can use conditional WHERE clause using CASE WHEN. So your query will support different scenarios, something like this:
SELECT *
FROM TABLE1
WHERE (CASE WHEN variable IS NULL AND column IS NULL THEN 1
WHEN variable LIKE '%' AND column LIKE variable||'%' THEN 1
ELSE 0
END) = 1
AND...
Basically, it checks if the variable = '' then it will compare the column against ''. Otherwise, it will compare it against variable||'%'.
Notice, Oracle treats empty string of the type VARCHAR as NULL (this does not apply to CHAR). So, in the first scenario we compare against NULL.

Hello Just a thought for this we can use Dynamic sql too. If you may try this approach. Hope it helps.
CREATE OR REPLACE PROCEDURE SPS_TEST_OUT(
p_input_in IN VARCHAR2
)
AS
lv_sql LONG;
lv_where VARCHAR2(100);
BEGIN
lv_where:= CASE WHEN p_input_in IS NULL OR p_input_in = '' THEN
''
ELSE
' AND COLUMN1 LIKE '''||p_input_in||'''%'
END;
lv_sql:='SELECT * FROM TABLE
WHERE 1 = 1
' ||lv_where;
dbms_output.put_line(lv_sql);
END;

Related

How to set NULL in a Coalesce

Quick question on Coalesce:
clw.ClawbackPercent = Coalesce(#ClawbackPercent, clw.ClawbackPercent)
Lets say for column 'ClawbackPercent' I have a value of 100.
If I execute a proc and set parameter #ClawbackPercent to have the value NULL, it keeps the value 100 in the row for that column which is great.
However, if I want to set 100 to actually be NULL, what do I need to write in the exec proc statement or what do I need to add in the Coalesce statement?
Thank you
It sounds like you want 100 to be the Default value of a stored proc parameter, not necessarily to replace all NULLs with this value. If this is the case, you don't want a COALESCE but you do need to provide a default value for the parameter on the proc definition.
e.g.
CREATE PROC dbo.MyProc (
#MyParam INT = 100
)
AS
-- My code here
If somebody executes this proc without specifying a value for #MyParam, the default of 100 will be assigned. If they explicitly specify #MyParam = NULL then NULL will be assigned..
Then probably you should not use coalesce, instead you can use case statement as below:
clw.ClawbackPercent = CASE WHEN #ClawbackPercent = 100
THEN NULL
ELSE
#ClawbackPercent END
in the select statement
You have to write in the following way:
CREATE PROCEDURE sp_test(
#ClawbackPercent VARCHAR(30)
) AS
BEGIN
SELECT COALESCE(#ClawbackPercent, '100')
END
--call as below and if you want to return second value then, EXEC sp_test NULL
--if your second, thirds... parameters are in INTEGER then simply CAST to VARCHAR
EXEC sp_test 'NULL'

Check a certain field for not null based on a parameter in sql

I get data from a stored procedure. Based on a passed Parameter I want to Change the SQL Statement in the where clause
The Statement Looks like this
SELECT
CC.Id,
CC.TId,
CC.PId,
CC.Date
FROM Mytable CC
WHERE CC.TId IS NOT NULL
I pass a Parameter #Qualifier to the procedure so I want to check this param. If it is '1' this SQL Statement should be executed, otherwise the second SQL Statement should be executed:
SELECT
CC.Id,
CC.TId,
CC.PId,
CC.Date
FROM Mytable CC
WHERE CC.PId IS NOT NULL
I tried to achieve this using the where clause like this
SELECT
CC.Id,
CC.TId,
CC.PId,
CC.Date
FROM Mytable CC
WHERE
(CASE
WHEN #Qualifier = '1'
THEN CC.TId IS NOT NULL
ELSE CC.PId IS NOT NULL)
But that is not working. Anyone knows how to solve this?
You mean:
WHERE (#Qualifier = '1' and CC.TId IS NOT NULL)
OR (#Qualifier <> '1' and CC.PId IS NOT NULL)

How to write filtered queries using SQL stored procedures?

How can I write a SQL stored procedure where I want the parameters to be optional in the select statement?
try this.. Make the SPs input parameters that control the filtering optional, witrh default values of null. In each select statement's Where clause, write the predicate like this:
Create procedure MyProcedure
#columnNameValue [datatype] = null
As
Select [stuff....]
From table
Where ColumnName = Coalesce(#columnNameValue , ColumnName)
this way if you do not include the parameter, or if you pass a null value for the parameter, the select statement will filter on where the column value is equal to itself, (effectively doing no filtering at all on that column.)
The only negative to this is that it prevents you from being able to pass a null as a meaningfull value to explicitly filter on only the nulls.... (i.e., Select only the rows where the value is null) Once the above technique has been adopted, you would need to add another parameter to implement that type of requirement. ( say, #GetOnlyNulls TinyInt = 0, or something similar)
Create procedure MyProcedure
#columnNameValue [datatype] = null,
#GetOnlyNulls Tinyint = 0
As
Select [stuff....]
From table
Where (ColumnName Is Null And #GetOnlyNulls = 1)
Or ColumnName = Coalesce(#columnNameValue , ColumnName)

Using a function in a where clause that includes null search

I currently have a prepared statement in Java which uses the following SQL statement in the WHERE clause of my query, but I would like to re-write this into a function to limit the user parameters passed to it and possibly make it more efficient.
(
(USER_PARAM2 IS NULL AND
( COLUMN_NAME = nvl(USER_PARAM1, COLUMN_NAME) OR
(nvl(USER_PARAM1, COLUMN_NAME) IS NULL)
)
)
OR
(USER_PARAM2 IS NOT NULL AND COLUMN_NAME IS NULL)
)
USER_PARAM1 and USER_PARAM2 are passed into the prepared statement by the user.
USER_PARAM1 represents what the application user wants to search this particular COLUMN_NAME for. If the user does not include this parameter, it will default to NULL.
USER_PARAM2 was my way to allow a user to request a NULL value only search on this COLUMN_NAME. Additionally I have some server logic that sets USER_PARAM2 to 'true' if passed in by the user or NULL if it wasn't specified by the user.
The intended behavior is that if USER_PARAM2 was declared then only COLUMN_NAME values of NULL are returned. If USER_PARAM2 wasn't declared and USER_PARAM1 was declared then only COLUMN_NAME = USER_PARAM1 are returned. If neither user params are declared then all rows are returned.
Could anyone help me out on this?
Thanks in advance...
EDIT:
Just to clarify this is how my current query looks (without the other WHERE clause statements..)
SELECT *
FROM TABLE_NAME
WHERE (
(USER_PARAM2 IS NULL AND
( COLUMN_NAME = nvl(USER_PARAM1, COLUMN_NAME) OR
(nvl(USER_PARAM1, COLUMN_NAME) IS NULL)
)
)
OR
(USER_PARAM2 IS NOT NULL AND COLUMN_NAME IS NULL)
)
... and this is where I would like to get to...
SELECT *
FROM TABLE_NAME
WHERE customSearchFunction(USER_PARAM1, USER_PARAM2, COLUMN_NAME)
EDIT #2:
OK, so another co-worker helped me out with this...
CREATE OR REPLACE function searchNumber (pVal IN NUMBER, onlySearchForNull IN CHAR, column_value IN NUMBER)
RETURN NUMBER
IS
BEGIN
IF onlySearchForNull IS NULL THEN
IF pVal IS NULL THEN
RETURN 1;
ELSE
IF pVal = column_value THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END IF;
ELSE
IF column_value IS NULL THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END IF;
END;
... this seems to work in my initial trials..
SELECT *
FROM TABLE_NAME
WHERE 1=searchNumber(USER_PARAM1, USER_PARAM2, COLUMN_NAME);
... the only issues I have with it would be
1)possible performance concerns vs the complex SQL statement I started with.
2)that I would have to create similar functions for each data type.
However, the latter would be less of an issue for me.
EDIT #3 2012.02.01
So we ended up going with the solution I chose below, while using the function based approach where code/query cleanliness trumps performance. We found that the function based approach performed roughly 6x worse than using pure SQL.
Thanks everyone for the great input everyone!
EDIT #4 2012.02.14
So looking back I noticed that applying the virtual table concept in #Alan's solution with the clarity of #danihp's solution gives a very nice overall solution in terms of clarity and performance. Here's what I now have
WITH params AS (SELECT user_param1 AS param, user_param2 AS param_nullsOnly FROM DUAL)
SELECT *
FROM table_name, params p
WHERE ( nvl(p.param_nullsOnly, p.param) IS NULL --1)
OR p.param_nullsOnly IS NOT NULL AND column_name IS NULL --2)
OR p.param IS NOT NULL AND column_name = p.param --3)
)
-- 1) Test if all rows should be returned
-- 2) Test if only NULL values should be returned
-- 3) Test if param equals the column value
Thanks again for the suggestions and comments!
There's a simple way of to pass your parameters only once and refer to them as many times as needed, using common-table expressions:
WITH params AS (SELECT user_param1 AS up1, user_param2 AS up2 FROM DUAL)
SELECT *
FROM table_name, params p
WHERE ((p.up2 IS NULL
AND (column_name = NVL(p.up1, column_name)
OR (NVL(p.up1, column_name) IS NULL)))
OR (p.up2 IS NOT NULL AND column_name IS NULL))
In effect, you're creating a virtual table, where the columns are your parameters, that is populated with a single row.
Conveniently, this also ensures that all of your parameters are collected in the same place and can be specified in an arbitrary order (as opposed to the order that the naturally appear in the query).
There are a couple big advantages to this over a function-based approach. First, this will not prevent the use of indexes (as pointed out by #Bob Jarvis). Second, this keeps the query's logic in the query, rather than hidden in functions.
I don't know if my approach has more performance, but it has best readability:
Sending 2 additionals parameters to query you can rewrite query like:
where
( P_ALL_RESULTS is not null
OR
P_ONLY_NULLS is not null AND COLUMN_NAME IS NULL
OR
P_USE_P1 is not null AND COLUMN_NAME = USER_PARAM1
)
Disclaimer: answered before OP question clarification

Why doesn't SQL2008 interpret the conditional WHERE clause in my stored procedure the way 2005 did?

My procedure was working fine in a 2005 database, but my PC died and when I set it up again I installed 2008 rather than 2005 - now this procedure doesn't return any results. I ask the question about the difference between the 2 versions simply because I have used this logic before and I haven't changed the procedure since I created it and it was working, the only change has been the fact I am now using SQL 2008
I wrote the procedure in Visual Studio and have noticed that when I paste the select statement into the SQL pane for the table that it is restructured and expanded so that each variation that could be expressed by combining the ANDs and ORs.
What I need is to be able to call this procedure optionally passing either parameter; so if I pass only the componentType it should evaluate the final statement part of the statement and use the value passed - if no value was passed then it would match the IS NULL side of the condition.
ALTER PROCEDURE dbo.uspSel_ComponentByType(
#filterText VARCHAR(50) = NULL
, #componentType CHAR(2) = NULL)
AS
SELECT [pkComponentID], [ComponentType], [ComponentName], [fkSupplierID], [Cost], [WastageCost]
FROM [tblComponents] AS c INNER JOIN
[tblSuppliers] AS s ON [c].[fkSupplierID] = [s].[pkSupplierID]
WHERE ([ComponentName] LIKE #filterText + '%' OR [SupplierName] LIKE #filterText + '%')
AND [c].[IsDeleted] = 0
AND (#componentType IS NULL OR [ComponentType] = #componentType)
I'm no SQL pro, but if I understand your intentions right, you should be able to use the sproc with no/either/both parameters and get expected results if you default #filterText to '' instead of NULL.
ALTER PROCEDURE dbo.uspSel_ComponentByType(
#filterText VARCHAR(50) = '' ,
#componentType CHAR(2) = NULL
)
AS
SELECT ... --the rest of the sproc is unchanged
As David M clarified, this is probably because SQL Server 2005 and 2008 handles string concatenation with NULL values differently. It seems that in SQL Server 2005
'A string' + NULL = 'A string';
while in SQL Server 2008
'A string' + NULL = NULL;
By changing the default to '', we made the default comparison '' + '%', which will always be treated as % and match everything, instead of NULL + '%', which will end upp NULL and match nothing.
Looks like different default handling of concatenating a null value - it is now concatenating NULL with '%' to give NULL, causing the LIKE comparison to fail. If you replace the NULL default with an empty string for #filterText you have a stored procedure that is not affected by such a difference in behaviour.
I guess it's due to SET options, especially likely is CONCAT_NULL_YIELDS_NULL, with ANSI_NULLS as a bubbler