2014-04-16 15 views
6

Czy istnieje funkcja lub operator, lub w inny sposób prosty (r) konstrukcja, aby uzyskać przecięcie dwóch literałów krotek w sql Oracle?Oracle SQL: Przecięcie literałów tptle

Biorąc pod uwagę następujący przykład:

Mając Poniższa tabela

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

otrzymali listę liczb {n N1, N2, N3, ..., n} Znajdź ID tak, że:

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 

dwóch prawdopodobnych rozwiązania to:

1. Roztwór 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. Roztwór 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) 
; 

Jednakże istnieje funkcja/operator wykonać następujące czynności?

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

Alternatywą, prostsza konstrukcja będzie mile widziana, biorąc pod uwagę, że warunek ten jest częścią dłuższego zapytania z bardziej liczne i ewentualnie bardziej skomplikowanych warunkach.

Dzięki.

Odpowiedz

2

Mój pomysł jest do konwersji liczb wyszukiwania do tabeli poprzez with klauzuli:

Następnie przy odrobinie regexp trik można tworzyć, z jednym rzędzie, jeden wiersz na wartości, i dopasować je na stole Dzięki in klauzuli:

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) 
; 

Daje to zgodnie z oczekiwaniami:

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

'wybrać regexp_substr ('123, 456', '\ D +', 1, poziom) n z podwójnym połączyć się regexp_instr ('123, 456', '\ D +', 1 stopień)! = 0' jest bardzo pomocny. po rozbiciu go na stół możliwości są liczne. Jedna z interesujących rzeczy, którą zauważyłem, to koszt zapytania wydaje się stały, podczas gdy rozwiązania 1 i 2 koszty są takie same i mają tendencję do różnic. – fluxy

1

tworząc swój własny rodzaj i funkcję można osiągnąć coś bardzo podobnego do żądanego SQL:

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

najpierw trzeba zadeklarować tego typu:

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

Type created. 

... i ta funkcja:

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; 

jednak być świadomość, że wywołanie funkcji dla każdego wiersza danych i bez możliwości korzystania z indeksów może nie być najbardziej wydajnym rozwiązaniem!

+0

to także świetny sposób na rozbicie danych na tabele. moje preferencje odnoszą się jednak do techniki sugerowanej przez @Emmanuela, ponieważ eliminuje potrzebę tworzenia niestandardowego typu i działa lepiej. – fluxy

+0

Tak, to dwa powody, dla których warto skorzystać z rozwiązania @ Emmanuela. –