2014-04-16 11 views
6

Esiste una funzione o un operatore o comunque un semplice (r) costrutto per ottenere l'intersezione di due letterali tupla in Oracle sql?Oracle SQL: Interseca tuple letterali

Prendendo in considerazione il seguente esempio:

Avere la seguente tabella

------------------------------ 
TABLE sometable 
------------------------------ 
id | telephone | mobile | fax 
------------------------------ 
1 | 123  | 456 | 789 

Dato un elenco di numeri di n {n1, n2, n3, ..., n} Cercare un ID , tale che:

telephone = n1 or mobile = n1 or fax = n1 
or telephone = n2 or mobile = n2 or fax = n2 
or telephone = n3 or mobile = n3 or fax = n3 
.... 
or telephone = n or mobile = n or fax = n 

Due soluzioni plausibili sono:

1. Soluzione 1

SELECT id FROM sometable 
WHERE 
    n1 IN (telephone, mobile, fax) 
OR n2 IN (telephone, mobile, fax) 
OR n3 IN (telephone, mobile, fax) 
.... 
OR n IN (telephone, mobile, fax) 
; 

2. Soluzione 2

SELECT id FROM sometable 
WHERE 
    telephone IN (n1, n2, n3, ..., n) 
OR mobile IN (n1, n2, n3, ..., n) 
OR fax  IN (n1, n2, n3, ..., n) 
; 

è tuttavia esiste una funzione/operatore per effettuare le seguenti?

SELECT id 
FROM sometable 
WHERE 
intersect_function 
(
    (telephone, mobile, fax), 
    (n1, n2, n3, ..., n) 
) 
= TRUE 
; 

Un'alternativa, semplice costrutto Sarebbe auspicabile, tenendo conto che questa condizione è parte di una query più numerosi e possibilmente più condizioni più complesse.

Grazie.

risposta

2

La mia idea è quella di convertire i numeri di ricerca in una tabella tramite la clausola with:

Poi, con un piccolo trucco regexp è possibile creare, da una singola riga, una riga per il valore, e abbinarli contro il tuo tavolo grazie alla clausola di in:

create TABLE sometable 
(
    id number, 
    telephone number, 
    mobile number, 
    fax number 
); 

insert into sometable values(1, 123, 456, 789); 
insert into sometable values(2, 0, 0, 123); 
insert into sometable values(3, 456, 0, 0); 

with w(n) as 
(
    select regexp_substr('123, 456', '\d+', 1, level) n 
    from dual 
    connect by regexp_instr('123, 456', '\d+', 1, level) != 0 
) 
select * 
from sometable s, w 
where w.n in (s.telephone, s.mobile, s.fax) 
; 

Questo dà come previsto:

ID TELEPHONE MOBILE FAX N 
1 123  456  789 123 
2 0   0  123 123 
1 123  456  789 456 
3 456  0  0 456 
+0

'select REGEXP_SUBSTR ('123, 456', '\ d +', 1, livello) n da doppio connettersi REGEXP_INSTR ('123, 456', '\ d +', 1, livello)! = 0 ' è molto utile. una volta rotto in un tavolo le possibilità sono numerose. una cosa interessante che ho notato è il costo della query sembra fisso mentre le soluzioni 1 e 2 i costi sono gli stessi e tendono a variare. – fluxy

1

creando il proprio tipo e la funzione si può ottenere qualcosa di molto simile a SQL desiderata:

SELECT id 
FROM sometable 
WHERE 
intersect_function 
(
    num_tab(telephone, mobile, fax), 
    num_tab(123, 456) 
) > 0; 

Per prima cosa è necessario dichiarare questo tipo:

SQL> create type num_tab is table of number; 
    2/

Type created. 

... e questa funzione:

create or replace function intersect_function 
    (p_tab1 num_tab 
    , p_tab2 num_tab 
) return number 
is 
    l_intersect num_tab; 
begin 
    l_intersect := p_tab1 multiset intersect p_tab2; 
    return l_intersect.count; 
end; 

Tuttavia, essere consapevole che chiamando una funzione per ogni riga di dati e senza poter fare uso di indici questa potrebbe non essere la soluzione più performante!

+0

questo è anche un ottimo modo per suddividere i dati in tabelle. la mia preferenza va comunque alla tecnica suggerita da @Emmanuel perché evita la necessità di creare un tipo personalizzato e di eseguire meglio. – fluxy

+0

Sì, sono due buoni motivi per andare con la soluzione di @ Emmanuel. –