Reputation: 3176
I have to compare a column in a database to a set of numbers. Essentially, I want to output all the numbers that are in the set, but not in the table.
I've tried this, but I still get the columns that are in the set and are not in the table.
This is what I tried so far,
DECLARE
CURSOR c_applnbr IS
SELECT * FROM appl a
WHERE a.applnbr IN
(7701, 7702, 7703, 7704, 7705, 7706, 7707, 7708, 7709, 7710, 7711,
7712, 7713, 7714, 7715, 7716, 7717, 7718, 7719, 7720, 7721, 7722,
7723, 7724, 7725, 7727, 7728, 7729, 7730, 7731, 7732, 7733, 7734,
7735, 7736, 7737, 7738, 7739, 7740, 7741, 7742, 7743, 7744, 7745,
7746, 7747, 7748, 7749, 7750, 7751, 7752, 7753, 7754, 7755, 7756,
7757, 7758, 7759, 7760, 7761, 7762, 7763, 7764, 7765, 7766, 7767,
7768, 7769, 7770, 7771, 7772, 7773, 7774, 7775, 7776, 7777, 7778,
7779, 7781, 7782, 7783, 7784, 7786, 7787, 7788, 7789, 7791, 7792,
7798, 7799, 7801, 7802, 7803, 7804, 7805, 7818, 7819, 7833); -- There is no 7833 in the table
CURSOR c_applnbr_total IS SELECT * FROM appl;
TYPE t_applnbr IS TABLE OF appl.applnbr%TYPE;
applnbrs t_applnbr := t_applnbr();
applnbrs_total t_applnbr := t_applnbr();
commonNumbers t_applnbr := t_applnbr();
counter integer := 0;
counter2 integer := 0;
BEGIN
-- Fill applnbrs
FOR n IN c_applnbr LOOP
counter := counter + 1;
applnbrs.EXTEND;
applnbrs(counter) := n.applnbr;
END LOOP;
-- Fill applnbrs_total
FOR n IN c_applnbr_total LOOP
counter2 := counter2 + 1;
applnbrs_total.EXTEND;
applnbrs_total(counter2) := n.applnbr;
END LOOP;
-- Getting the numbers in the set, and not in the table.
commonNumbers := applnbrs MULTISET EXCEPT DISTINCT applnbrs_total;
-- Checking counts
dbms_output.put_line('applnbrs count: ' || applnbrs.COUNT); -- Holds 97
dbms_output.put_line('applnbrs_total count: ' || applnbrs_TOTAL.COUNT); -- Holds 1979
dbms_output.put_line('commonNumbers.count: ' || commonNumbers.COUNT); -- Holds 0
FOR n IN 1..commonNumbers.COUNT LOOP -- Doesn't go in loop
dbms_output.put_line(n || ': ' || commonNumbers(n));
END LOOP;
END;
I have't done Pl/SQL in a while, so maybe I'm not using nested tables correctly.
There is no entry for 7833
in the table. So the desired output should spit out 7833
, but commonNumbers.COUNT = 0
. So essentially everything matches.
Upvotes: 0
Views: 67
Reputation: 31716
You can use the XMLTABLE
function and NOT EXISTS
in simple select.There is no need of PL/SQL.
SELECT * FROM
(
SELECT
to_number(column_value) AS appl
FROM
XMLTABLE ( '7701, 7702, 7703, 7704, 7705, 7706, 7707, 7708, 7709, 7710, 7711,
7712, 7713, 7714, 7715, 7716, 7717, 7718, 7719, 7720, 7721, 7722,
7723, 7724, 7725, 7727, 7728, 7729, 7730, 7731, 7732, 7733, 7734,
7735, 7736, 7737, 7738, 7739, 7740, 7741, 7742, 7743, 7744, 7745,
7746, 7747, 7748, 7749, 7750, 7751, 7752, 7753, 7754, 7755, 7756,
7757, 7758, 7759, 7760, 7761, 7762, 7763, 7764, 7765, 7766, 7767,
7768, 7769, 7770, 7771, 7772, 7773, 7774, 7775, 7776, 7777, 7778,
7779, 7781, 7782, 7783, 7784, 7786, 7787, 7788, 7789, 7791, 7792,
7798, 7799, 7801, 7802, 7803, 7804, 7805, 7818, 7819, 7833'
) ) t WHERE
NOT EXISTS (
SELECT
1
FROM
applnbr a
WHERE
a.appl = t.appl
);
Note: XMLTABLE
works only for Numbers.
Upvotes: 1
Reputation: 351228
A solution would be to use a query that produces the needed values. Something like:
select 7701 as applnbr from dual union all
select 7702 from dual union all
select 7703 from dual union all
select 7704 from dual union all
select 7705 from dual
... etc. As this becomes quite long, and a big part of your numbers are consecutive, you could also compress this a bit with a recursive query:
select applnbr
from (select 7700 + level as applnbr from dual connect by level < 134)
where applnbr not in (7726, 7780, 7785, 7790, 7800)
and applnbr not between 7793 and 7797
and applnbr not between 7806 and 7817
and applnbr not between 7820 and 7832
This produces all numbers between 7701 and 7833 and then removes some of them via the where
clause to get to the actual set.
Finally, you can use an outer join to get the desired result:
with needed as (
select applnbr
from (select 7700 + level as applnbr from dual connect by level < 134)
where applnbr not in (7726, 7780, 7785, 7790, 7800)
and applnbr not between 7793 and 7797
and applnbr not between 7806 and 7817
and applnbr not between 7820 and 7832
)
select needed.applnbr
from needed
left join appl
on appl.applnbr = needed.applnbr
where appl.applnbr is null
Upvotes: 1