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:
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.