2009-09-12 10 views
49

Mam stosunek 1: 1 między dwiema tabelami. Chcę znaleźć wszystkie wiersze w tabeli A, które nie mają odpowiedniego wiersza w tabeli B. używam tej kwerendy:Jak znaleźć wiersze w jednej tabeli, które nie mają odpowiadającego wiersza w innej tabeli

SELECT id 
    FROM tableA 
WHERE id NOT IN (SELECT id 
        FROM tableB) 
ORDER BY id desc 

id jest kluczem podstawowym w obu tabelach. Oprócz indeksów klucza podstawowego, mam również indeks na tableA (id desc).

Za pomocą H2 (wbudowanej bazy danych Java) powoduje to pełne skanowanie tabeli tableB. Chcę uniknąć pełnego skanowania tabeli.

Jak mogę przepisać to zapytanie, aby działało szybko? Jaki powinien być indeks?

+1

za każdym razem piszesz 'WHERE col [NIE] IN (SELECT col FROM othertable)' jesteś lepiej refactoring używając [NIE] istnieje. – topchef

Odpowiedz

72
select tableA.id from tableA left outer join tableB on (tableA.id = tableB.id) 
where tableB.id is null 
order by tableA.id desc 

Jeśli db wie jak to zrobić skrzyżowań indeksu, to będzie dotykać tylko indeks klucz podstawowy

+4

Dlatego właśnie uwielbiam Stack Overflow. Sobota, problem SQL - pytanie odpowiedział precyzyjnie i skutecznie za 5 minut! –

+2

masz dobre sugestie również w innych odpowiedziach. Oczywiście myślę, że moja będzie najszybsza :-), ale implementacje db różnią się znacznie i nie mam doświadczenia z H2. Byłoby wspaniale, gdybyś porównała różne podejścia i zaktualizowała pytanie swoimi wynikami. – SquareCog

25

Można również użyć exists, ponieważ czasami jest to szybciej niż left join. Będziesz musiał je porównać, aby określić, z którego chcesz skorzystać.

select 
    id 
from 
    tableA a 
where 
    not exists 
    (select 1 from tableB b where b.id = a.id) 

Aby pokazać, że exists mogą być bardziej efektywne niż left join, oto plany realizacji tych zapytań w SQL Server 2008:

left join - całkowity koszt subtree: 1,09724:

left join

exists - całkowity koszt poddrzewi: 1.074 21:

exists

+1

+1: Warunek EXISTS jest uważany za "spełniony", jeśli podzapytanie (w tym przypadku skorygowane) zwraca co najmniej jeden wiersz. –

+0

testowanie porównawcze to dobry pomysł. Tkam w mózg, próbując dowiedzieć się, co może zrobić db w ramach okładek pod względem istniejącego + skorelowanego podzapytania, które sprawiłoby, że byłoby szybsze niż połączenie hashujące tylko do indeksu. Czy wiesz? – SquareCog

+2

'Exist'' nie używa twojego standardowego skorelowanego podzapytania. Używa semi-join. Plan wykonania SQL Server 2008 dla 'left join' to dwa skany indeksu do dopasowania hash do filtra do selekcji. W przypadku "nie istnieje", jest to dwa skany indeksu do dopasowania hash do zaznaczenia - bez filtra. Dopasowanie "istnieje" hash jest w rzeczywistości nieco szybsze niż "left join". 'left join' ma całkowity koszt 1,09,' nie istnieje' of 1,07 na 'DimCustomer' dla' AdventureWorksDW' na 'AdventureWorksDW2008'. – Eric

5

Musisz sprawdzić każdy identyfikator w TABLEA przed każdym ID w tableB. W pełni funkcjonalny RDBMS (taki jak Oracle) byłby w stanie zoptymalizować to do INDEX FULL FAST SCAN i wcale nie dotykać stołu. Nie wiem, czy optymalizator H2 jest tak inteligentny.

H2 obsługuje składni MINUS więc powinieneś spróbować tej

select id from tableA 
minus 
select id from tableB 
order by id desc 

To może działać szybciej; to z pewnością warte analizy porównawczej.

+0

+1 To może rzeczywiście być bardziej wydajne w Oracle. –

4

Dla mojego małego zestawu danych, Oracle przekazuje prawie wszystkie te zapytania do tego samego planu, który wykorzystuje indeksy kluczy głównych bez dotykania tabeli. Wyjątkiem jest wersja MINUS, której udaje się uzyskać mniejszą zgodność mimo wyższych kosztów planu.

--Create Sample Data. 
d r o p table tableA; 
d r o p table tableB; 

create table tableA as (
    select rownum-1 ID, chr(rownum-1+70) bb, chr(rownum-1+100) cc 
     from dual connect by rownum<=4 
); 

create table tableB as (
    select rownum ID, chr(rownum+70) data1, chr(rownum+100) cc from dual 
    UNION ALL 
    select rownum+2 ID, chr(rownum+70) data1, chr(rownum+100) cc 
     from dual connect by rownum<=3 
); 

a l t e r table tableA Add Primary Key (ID); 
a l t e r table tableB Add Primary Key (ID); 

--View Tables. 
select * from tableA; 
select * from tableB; 

--Find all rows in tableA that don't have a corresponding row in tableB. 

--Method 1. 
SELECT id FROM tableA WHERE id NOT IN (SELECT id FROM tableB) ORDER BY id DESC; 

--Method 2. 
SELECT tableA.id FROM tableA LEFT JOIN tableB ON (tableA.id = tableB.id) 
WHERE tableB.id IS NULL ORDER BY tableA.id DESC; 

--Method 3. 
SELECT id FROM tableA a WHERE NOT EXISTS (SELECT 1 FROM tableB b WHERE b.id = a.id) 
    ORDER BY id DESC; 

--Method 4. 
SELECT id FROM tableA 
MINUS 
SELECT id FROM tableB ORDER BY id DESC; 
+1

za napisanie 'd r o p', sprawia, że ​​ludzie czytają kod, dostajesz +1 – Flavius

Powiązane problemy