Search code examples
oracle-databaseplsql

PL/SQL: EXECUTE IMMEDIATE with condition variable


I have a function in PL/SQL written like this

IF p_class IS NOT NULL
THEN
v_conditions := ' WHERE class = :p_class '
ELSE
v_conditions := ' WHERE 1 = :p_class '
p_class := 1
END IF;

I want to execute query with given p_class:

v_query := v_query || v_conditions;
EXECUTE IMMEDIATE v_query USING p_class

Now I want to check if my p_class is not exists, I won't have v_conditions in my query. Is there any better way instead of using WHERE 1 = :p_class?

I try to refactor my function to check if p_class is null, I won't have to add v_conditions to query, but I get an error:

bind variable does not exists.

I also try v_conditions := ' WHERE class = ' || p_class but I'm afraid that it will get SQL injection so I didn't use it.


Solution

  • You can also do it in a single statement, no IF needed:

    DECLARE
      l_ename VARCHAR2(100);
      l_statement VARCHAR2(500) := 'SELECT COUNT(*) FROM emp WHERE ename = :ename or :ename IS NULL';
      l_result NUMBER;
    BEGIN
      l_ename := 'KING';
      --l_ename := NULL
      EXECUTE IMMEDIATE l_statement INTO l_result USING l_ename, l_ename;
      dbms_output.put_line(l_result);
    END;
    /
    

    --update-- if there are multiple parameters and the check of the NULL value has performance consequences then you can try the following. execute a 1=1 first if the parameter value is null. This will be evaluated and yield true so the next condition will be ignored.

    DECLARE
      l_ename VARCHAR2(100);
      l_job VARCHAR2(100);
      l_statement VARCHAR2(500) := 'SELECT COUNT(*) FROM emp WHERE 1 = 1 ';
      l_condition VARCHAR2(100);
      l_result NUMBER;
    BEGIN
      l_ename := NULL;
      l_job := 'CLERK';
      IF l_ename IS NULL THEN
        l_statement := l_statement ||' AND (1 = 1 OR 1 = :p1) ';
      ELSE
        l_statement := l_statement ||' AND ename = :p1 ';
      END IF;
      IF l_job IS NULL THEN
        l_statement := l_statement ||' AND (1 = 1 OR 1 = :p2) ';
      ELSE
        l_statement := l_statement ||' AND job = :p2 ';
      END IF;
        
      dbms_output.put_line(l_statement);
      EXECUTE IMMEDIATE l_statement INTO l_result USING l_ename, l_job;
      dbms_output.put_line(l_result);
    END;
    /