Comment trouver des lignes dans une table qui n'ont pas de ligne correspondante dans une autre table
-
06-07-2019 - |
Question
J'ai une relation 1: 1 entre deux tables. Je souhaite rechercher toutes les lignes de la table A qui n'ont pas de ligne correspondante dans la table B. J'utilise cette requête:
SELECT id
FROM tableA
WHERE id NOT IN (SELECT id
FROM tableB)
ORDER BY id desc
id est la clé primaire dans les deux tables. Outre les index de clé primaire, j'ai aussi un index sur tableA (id desc).
Si vous utilisez H2 (base de données intégrée Java), vous obtenez une analyse complète de la tableB. Je veux éviter une analyse complète de la table.
Comment puis-je réécrire cette requête pour qu'elle s'exécute rapidement? Quel index devrais-je avoir?
La solution
select tableA.id from tableA left outer join tableB on (tableA.id = tableB.id)
where tableB.id is null
order by tableA.id desc
Si votre base de données sait comment effectuer les intersections d'index, cela ne touchera que l'index de clé primaire
Autres conseils
Vous pouvez également utiliser existe
, car il est parfois plus rapide que left join
. Vous devrez les comparer pour déterminer lequel vous voulez utiliser.
select
id
from
tableA a
where
not exists
(select 1 from tableB b where b.id = a.id)
Pour montrer que existe
peut être plus efficace qu'une jointure à gauche
, voici les plans d'exécution de ces requêtes dans SQL Server 2008:
jointure à gauche
- coût total de la sous-arborescence: 1.09724:
existe
- coût total de la sous-arborescence: 1.07421:
Vous devez vérifier chaque ID de la tableA par rapport à chaque ID de la tableB. Un SGBDR complet (tel que Oracle) serait capable de l’optimiser dans un INDEX FULL FAST SCAN et de ne pas toucher la table du tout. Je ne sais pas si l'optimiseur de H2 est aussi intelligent que cela.
H2 prend en charge la syntaxe MINUS, vous devriez donc essayer ceci
select id from tableA
minus
select id from tableB
order by id desc
Cela peut être plus rapide. il vaut certainement la peine de procéder à une analyse comparative.
Pour mon petit ensemble de données, Oracle donne à la quasi-totalité de ces requêtes le même plan que celui qui utilise les index de clé primaire sans toucher à la table. La seule exception est la version MINUS, qui parvient à obtenir moins d’objectifs cohérents malgré le coût plus élevé du forfait.
--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;
Je ne peux pas vous dire laquelle de ces méthodes sera la meilleure sur H2 (ni même si chacune d’elles fonctionnera), mais j’ai écrit un article détaillant toutes les (bonnes) méthodes disponibles dans TSQL. Vous pouvez leur donner un coup de feu et voir si l'un d'eux fonctionne pour vous:
select parentTable.id from parentTable
left outer join childTable on (parentTable.id = childTable.parentTableID)
where childTable.id is null