|
|
Getting the
most out of INSTR as an alternative to using the IN and NOT IN operator.
By Grant Daley (gdaley@cuscal.com.au) and
Brahmaiah Koniki (bkoniki@cuscal.com.au)
Many people may already know that you can use the INSTR operator as an alternative to using IN and NOT IN where he search column contains alpha data.
For Example:
We have a simple table that contains Australian Political Parties.
SQL> DESC POLITICAL_PARTY
Name
Null? Type
----------------------------------------- -------- ---------------
PARTY_ID
NOT NULL NUMBER(7)
PARTY_SNAME
NOT NULL VARCHAR2(10)
PARTY_NAME
NOT NULL VARCHAR2(40)
MAJOR_PARTY_FLAG
NOT NULL VARCHAR2(1)
If we wanted to know the PARTY_ID of the political party representing the conservationists in the Federal sphere of government, we can use :
SQL> select party_id, party_name
2 from political_party
3 where instr('The Greens',party_name) > 0;
PARTY_ID PARTY_NAME
---------- ----------------------------------------
20 The Greens
It is more likely though that we will have an application built in Developer6 or equivalent that already has the parties listed and our users select which party or parties they want to pull data about.
The check box in the form will return the PARTY_ID for use in other queries.
Thus we will end-up with a query that is the equivalent of this :
SQL> select party_id, party_name
2 from political_party
3 where instr('1,22',party_id) > 0;
Unfortunately, we get more than we wanted !
PARTY_ID PARTY_NAME
---------- ------------------------------
1 Australian Labor Party
2 Democrats
22 Unity
3 rows selected.
which somehow always disappoints the users !
So here is our first tip :
To overcome this you need to ensure that INSTR can compare each comma delimited variable with the equivalent comma delimited variable from the table.
Building on the above problematic example, this will give us :
SQL> select party_id, party_name
2 from political_party
3 where instr(',1,22,',','||party_id||',') > 0;
INSTR will now use the ',1,22,' part to compare with the ','||party_id||',' part, giving :
PARTY_ID PARTY_NAME
---------- ----------------------------------------
1 Australian Labor Party
22 Unity
2 rows selected.
which is exactly what we wanted.
Our second tip for INSTR is for those occasions you might want to easily include or exclude or effectively ignore the elements in the string.
We know from above that stating :
where instr(',1,22,',','||party_id||',') > 0
gives us the elements in the list, but what if we want to ignore 1 and 22 ?
Observe the subtle difference below :
SQL> select party_id, party_name
2 from political_party
3 where instr(',1,22,',','||party_id||',') = 0;
Oracle returns all political parties that do not have and PARTY_ID of 1 or 22.
PARTY_ID PARTY_NAME
---------- ----------------------------------------
2 Democrats
3 Liberal Party
4 National Party
5 ACT Greens
6 Australian Greens
7 Better Future For Our Children
8 Christian Democratic Party
9 Greens Western Australia
10 Independent
11 Independent Australian Labor Party
12 Independent Liberal
13 No Pokies Party
14 One Nation
15 Outdoor Recreation Party
16 Reform The Legal System
17 Shooters Party
18 South Australia First
19 Tasmanian Greens
20 The Greens
21 United Canberra Party
23 NT Country Liberal Party
21 rows selected.
There is no prizes for guessing what using
where instr(',1,22,',','||party_id||',') >= 0;
will give us.
This is fine if you are a lazy programmer. But we like it when our code is smart enough to cope with the subtle differences, and as Ganesh and God knows, we can go on writing endless cursors or whatever, which brings us to our third and final tip :
The thing about INSTR is that when it finds a match it returns a number > 0 and when there is no match it returns a zero.
Thus, if we introduce our old friend DECODE to INSTR we have the means to make the process of including and excluding lists of values soft.
For example :
SQL> select party_id, party_name,
2 instr(',1,22,',','||party_id||',') instring
3 from political_party
4 where decode(instr(',1,22,',','||party_id||','),0,0,1) > 0;
gives us :
PARTY_ID PARTY_NAME INSTRING
---------- ---------------------------------------- --------
1 Australian Labor Party 1
22 Unity 3
2 rows selected.
while
SQL> select party_id, party_name,
2 instr(',1,22,',','||party_id||',') instring
3 from political_party
4 where decode(instr(',1,22,',','||party_id||','),0,1,0) > 0;
gives us everything but PARTY_ID 1 and 22.
PARTY_ID PARTY_NAME INSTRING
---------- ---------------------------------------- --------
2 Democrats 0
3 Liberal Party 0
4 National Party 0
5 ACT Greens 0
6 Australian Greens 0
7 Better Future For Our Children 0
8 Christian Democratic Party 0
9 Greens Western Australia 0
10 Independent 0
11 Independent Australian Labor Party 0
12 Independent Liberal 0
13 No Pokies Party 0
14 One Nation 0
15 Outdoor Recreation Party 0
16 Reform The Legal System 0
17 Shooters Party 0
18 South Australia First 0
19 Tasmanian Greens 0
20 The Greens 0
21 United Canberra Party 0
23 NT Country Liberal Party 0
21 rows selected.
We reckon you will have the skill to work out what would give us all 23 rows.
From here you will be able to parameterise as required to make the whole thing very flexible.
For example :
CURSOR c1 (party_id_str IN VARCHAR2,
instr_true IN NUMBER,
instr_false IN NUMBER) IS
select party_id, party_name,
instr(party_id_str,','||party_id||',') instring
from political_party
where decode(instr(party_id_str,',
'||party_id||','),0, instr_true, instr_false) > 0;
Good luck !