Come trovare le righe in una tabella che non hanno righe corrispondenti in un'altra tabella
-
06-07-2019 - |
Domanda
Ho una relazione 1: 1 tra due tabelle. Voglio trovare tutte le righe nella tabella A che non hanno una riga corrispondente nella tabella B. Uso questa query:
SELECT id
FROM tableA
WHERE id NOT IN (SELECT id
FROM tableB)
ORDER BY id desc
id è la chiave primaria in entrambe le tabelle. Oltre agli indici delle chiavi primarie, ho anche un indice sulla tabella A (id desc).
Utilizzando H2 (database incorporato Java), si ottiene una scansione completa della tabella B. Voglio evitare una scansione completa della tabella.
Come posso riscrivere questa query per eseguirla rapidamente? Quale indice dovrei?
Soluzione
select tableA.id from tableA left outer join tableB on (tableA.id = tableB.id)
where tableB.id is null
order by tableA.id desc
Se il tuo db sa come eseguire le intersezioni dell'indice, questo toccherà solo l'indice della chiave primaria
Altri suggerimenti
Puoi anche usare esiste
, poiché a volte è più veloce di left join
. Dovresti confrontarli con loro per capire quale vuoi usare.
select
id
from
tableA a
where
not exists
(select 1 from tableB b where b.id = a.id)
Per mostrare che esiste
può essere più efficiente di un left join
, ecco i piani di esecuzione di queste query in SQL Server 2008:
left join
- costo totale sottostruttura: 1.09724:
existing
- costo totale sottostruttura: 1.07421:
Devi controllare ogni ID nella tabella A con ogni ID nella tabella B. Un RDBMS completo (come Oracle) sarebbe in grado di ottimizzarlo in una SCANSIONE COMPLETA FAST INDICE e non toccare affatto la tabella. Non so se l'ottimizzatore di H2 sia così intelligente.
H2 supporta la sintassi MINUS, quindi dovresti provare questo
select id from tableA
minus
select id from tableB
order by id desc
Che potrebbe funzionare più velocemente; vale senz'altro la valutazione comparativa.
Per il mio piccolo set di dati, Oracle fornisce a quasi tutte queste query lo stesso piano esatto che utilizza gli indici delle chiavi primarie senza toccare la tabella. L'eccezione è la versione MINUS che riesce a fare meno guadagni coerenti nonostante il costo del piano più elevato.
--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;
Non posso dirti quale di questi metodi sarà meglio su H2 (o anche se funzioneranno tutti), ma ho scritto un articolo che descrive in dettaglio tutti i (buoni) metodi disponibili in TSQL. Puoi dare loro una possibilità e vedere se qualcuno di loro funziona per te:
select parentTable.id from parentTable
left outer join childTable on (parentTable.id = childTable.parentTableID)
where childTable.id is null