When building a procedure, I want to be able to pass a parameter or pass a NULL if I don’t want to search by a parameter. Some people will tell you to accomplish this that you should write a dynamic SQL statement so you can add or remove parameters as needed. I avoid Dynamic SQL wherever possible and this is a situation where Dynamic SQL is not needed. Instead, I can use the NVL function. More NULL fun here.

Try the following example.

create table tbl_locations (
 location_id integer,
 city varchar2(50),
 state varchar2(2),
 zip_code varchar2(10)
 );

insert into tbl_locations values (1,'ORLANDO','FL', '32801');
insert into tbl_locations values (2,'ORLANDO','FL', '32802');
insert into tbl_locations values (3,'ORLANDO','FL', '32803');
insert into tbl_locations values (4,'ORLANDO','FL', '32804');
insert into tbl_locations values (5,'ORLANDO','FL', '32805');
insert into tbl_locations values (6,'ORLANDO','FL', '32806');
insert into tbl_locations values (7,'LEESBURG','FL', '34748');
insert into tbl_locations values (8,'LEESBURG','FL', '34749');
insert into tbl_locations values (9,'LEESBURG','FL', '34788');
insert into tbl_locations values (10,'LEESBURG','FL', '34789');
insert into tbl_locations values (11,'ATLANTA','GA', '30301');
insert into tbl_locations values (12,'ATLANTA','GA', '30302');
insert into tbl_locations values (13,'ATLANTA','GA', '30303');
insert into tbl_locations values (14,'ATLANTA','GA', '30304');
insert into tbl_locations values (15,'ATLANTA','GA', '30305');
insert into tbl_locations values (16,'ATLANTA','GA', '30306');
insert into tbl_locations values (17,'MARIETTA','GA', '30006');
insert into tbl_locations values (18,'MARIETTA','GA', '30007');
insert into tbl_locations values (19,'MARIETTA','GA', '30008');

I want a procedure that will let me select State and Zip Code. If I leave Zip Code null, it should return all records in that State. If I enter a Zip Code and no State, it should return that Zip Code’s information. It shouldn’t bomb because I have a null State.

In the following procedure, NVL is used in the WHERE clause. Each row is evaluated according to theĀ WHERE clause, determining if the conditions listed are true for that row.

create or replace procedure prc_search_locations (
  results OUT sys_refcursor, 
  p_state IN varchar2, 
  p_zipcode IN varchar2)
is
begin

 open results for
 select city, state, zip_code
 from tbl_locations
 where nvl(p_state, state) = state
 and nvl(p_zipcode, zip_code) = zip_code;

end;
/

If p_state is not null, it is returned by theĀ NVL function and is then compared to the state column. Substituting “GA” for p_state, this is how it would look at runtime. The first value is the p_state parameter, the second value is the value found in the row. If the row is an “FL” row, it would look like the second example because the second value is the value found in the row. It comes out “GA” either way because we passed a value for p_state.

where nvl(p_state, state) = state
SQL> select NVL('GA','GA') from dual;

NV
--
GA

SQL> select NVL('GA','FL') from dual;

NV
--
GA

Alternatively, if p_zipcode is left null, then we expect all zip codes to be returned. Substituting null for p_zipcode, this is how it would look at runtime. Comparing row by row to see if the condition evaluates to true, when it happens upon the row where zip_code = “30301” it will find “30301” on both sides because the parameter is null so it is passing the second value, which comes from the table data.

and nvl(p_zipcode, zip_code) = zip_code;
SQL> select NVL(null, '30301') from dual;

NVL(N
-----
30301

The following is how the code was executed. p_state = ‘GA’ so for every row where state = p_state, the row is returned.

SQL> var r refcursor;
SQL> exec prc_search_locations (:r, p_state => 'GA', p_zipcode => null);

PL/SQL procedure successfully completed.

SQL> print r;

CITY       ST ZIP_CODE
---------- -- ----------
ATLANTA    GA 30301
ATLANTA    GA 30302
ATLANTA    GA 30303
ATLANTA    GA 30304
ATLANTA    GA 30305
ATLANTA    GA 30306
MARIETTA   GA 30006
MARIETTA   GA 30007
MARIETTA   GA 30008

9 rows selected.

If p_state is left null and p_zipcode is populated, we expect states to not be a restriction on which rows are returned, only the zip code.

SQL> var r refcursor;
SQL> exec prc_search_locations (:r, p_state => null, p_zipcode => '30301');

PL/SQL procedure successfully completed.

SQL> print r;

CITY     ST ZIP_CODE
-------- -- ----------
ATLANTA  GA 30301