I'm writing a REST handler in ORDS. The URL endpoint should allow queries to be made with a number of optional parameters. One way to write the PL/SQL code could be:
DECLARE
cur SYS_REFCURSOR
BEGIN
OPEN cur FOR
SELECT * FROM MYTABLE WHERE
(:param1 IS NULL OR column1 = :param1) AND
(:param2 IS NULL OR column2 = :param2);
:resultSetOut := cur;
END;
The other way that I thought could be slightly more performant is to construct the SQL string
DECLARE
cur SYS_REFCURSOR
sqlString VARCHAR2(200)
BEGIN
sqlString := 'SELCT * FROM MYTABLE WHERE 1=1';
IF (:param1 IS NOT NULL) THEN sqlString := sqlString || ' AND COLUMN1=:param1'; END IF;
IF (:param2 IS NOT NULL) THEN sqlString := sqlString || ' AND COLUMN1=:param2'; END IF;
OPEN cur FOR sqlString USING :param1, :param2;
:resultSetOut := cur;
END;
However, this string construction at the end need to be statically bound to variables, which essentially makes all variables required and not optional in the URL query.
For a PL/SQL block that allows for dynamic WHERE clauses, is the ony way to use the first way shown here? Is there a way to construct a string and bind similar to the second way shown here?
CodePudding user response:
You’re on the right track:
IF (:param1 IS NOT NULL) THEN sqlString := sqlString || ' AND COLUMN1=:param1';
Else sqlString := sqlString || ' and (1=1 or :param1 is null) ';
END IF;
Now you need the same amount of bind variables no matter what and the Oracle optimizer will know that 1 is always 1 so it doesn’t need to consider the other predicate.
CodePudding user response:
You can do it in an equivalent way without dynamic SQL:
BEGIN
IF :param1 IS NOT NULL AND :param1 IS NOT NULL THEN
OPEN :resultSetOut FOR
SELECT *
FROM MYTABLE
WHERE COLUMN1=:param1
AND COLUMN2=:param2;
ELSIF :param1 IS NOT NULL THEN
OPEN :resultSetOut FOR
SELECT *
FROM MYTABLE
WHERE COLUMN1=:param1;
ELSIF :param2 IS NOT NULL THEN
OPEN :resultSetOut FOR
SELECT *
FROM MYTABLE
WHERE COLUMN2=:param2;
ELSE
OPEN :resultSetOut FOR
SELECT *
FROM MYTABLE;
END IF;
END;