2010-10-20 3 views
0

J'ai 2 tables (nom (champs)):Comment optimiser la requête de suppression lente (suppression des données qui ne sont pas utilisées dans une autre table) dans Postgresql

data(object_id, property_id, value_id) 

et

string(id, value) 

Toutes les les données sont dans la table "chaîne". "données" ne fait référence qu'aux chaînes correspondantes.

Par exemple, j'ai:

data(1,2,3) 
data(1,4,5) 
data(6,4,7) 



string(1, 'car') 
string(2, 'color') 
string(3, 'red') 
string(4, 'make') 
string(5, 'audi') 
string(6, 'car2') 
string(7, 'toyota') 

Maintenant ce que je veux, quand je supprime quelques lignes dans le tableau de données, puis toutes les lignes orphelines dans le tableau de chaînes seraient également supprimés:

si je supprimer les données (6,4,7) alors les chaînes avec les identifiants 6 et 7 seraient supprimées (parce qu'elles ne sont plus utilisées); 4 est utilisé dans une autre ligne de données et n'est donc pas supprimé.

Ma question est, comment écrire une requête de suppression optimisée pour la table de chaînes?

Actuellement, j'ai quelque chose comme ça (ce qui fonctionne, mais il est très lent):

delete 
from string s 
where 1=1 
and (select count(id) from data where object_id = s.id) = 0 
and (select count(id) from data where property_id = s.id) = 0 
and (select count(id) from data where value_id = s.id) = 0 

J'ai aussi essayé (en fonction du nombre d'orphelins donne parfois résultat 10-20% plus rapide):

delete from string 
where (id not in (select usedids.id from (select object_id as id from data 
    union 
    select property_id as id from data 
    union 
    select value_id as id from data) as usedids) 
); 

J'ai environ 100k lignes dans les deux tables. Si je supprime environ 6000 lignes dans la table de données, le nettoyage de la table de chaînes prend environ 3 minutes. J'ai un index sur chaque domaine. J'ai aussi des contraintes de clés étrangères.

+0

Donc l'enregistrement dans les données de (6,4,7) est en fait un fk_id à la table de chaînes pour chaque colonne dans les données? Donc 6 points à la chaîne id = 6 et 4 points à la chaîne id = 4 et la même chose pour 7? Une normalisation pourrait probablement résoudre la plupart de vos problèmes si c'est le cas. – Kuberchaun

+0

Peut spécifier "une certaine normalisation"?L'idée est d'avoir une structure schématique, où je peux stocker tout simplement sans apporter de changements à la structure db. Il veut reproduire la base de données de style RDF. Je sais, il y a des bases RDF spécifiques, mais j'ai besoin d'utiliser poistgresql. Mais peut-être avez-vous de bonnes idées sur la normalisation? – Ago

+0

"L'idée est d'avoir une structure sans schémas, où je peux stocker pratiquement tout sans apporter de modifications à la structure db." Eh bien, vous voyez maintenant pourquoi ce n'est pas une bonne idée dans un dbms SQL. –

Répondre

0

le problème semble être que vous accédez à votre table de données 3x au lieu d'une fois.

votre requête doit être

supprimer de la chaîne WHERE (SELECT COUNT (*) à partir des données OÙ object_id = s.id OU property_id = s.id OU value_id = s.id) = 0;

N'a pas encore essayé mais je peux faire des modifications si vous me faites savoir ce qui s'est passé.

+0

J'ai essayé ça. Il fait toujours fondamentalement la même chose (au moins la vitesse est presque la même). expliquer (pour select * from ...) me donne: Seq Scan sur chaîne s (coût = 0.00..82770498.75 lignes = 353 largeur = 119) Bitmap Heap Scan sur les données (coût = 92.42..1166.00 lignes = 2376 width = 0) BitmapOr (coût = 92.42..92.42 lignes = 2376 largeur = 0) – Ago

+0

Il semble que je ne peux pas formater mon commentaire? – Ago

1

Vous souhaitez EXISTS.

delete 
from string s 
where 1=1 
and (select count(id) from data where object_id = s.id) = 0 

est effectivement fait correctement comme

delete from string s 
where not exists (select * from data d where d.object_id = s.id) 

Vous ne voulez pas vraiment à count, mais plutôt de savoir exactement si le sous tableau exists. En dehors de cela, notez que tout cela serait géré pour vous si vous utilisiez des clés étrangères. Cela devrait être votre prochaine étape après avoir fait fonctionner ce code.

+0

Pouvez-vous être plus précis comment pourrais-je le gérer avec des clés étrangères? Je comprends, que je pourrais utiliser FK-s pour la vérification inverse: si j'ai besoin de supprimer la ligne "données" sur la base de la suppression "chaîne". Je suppose que je pourrais écrire un déclencheur qui, pour chaque suppression de ligne "data", essaie de supprimer les lignes "string" orphelines. Mais je suppose que 10000 x déclencheur d'exécution pourrait avoir plus de peine que d'exécuter 1 requête de suppression. – Ago

+0

en utilisant "existe" me donne un meilleur coût (lors de l'exécution expliquer). 16k vs 234M (avec compte). Lorsque j'ai essayé de supprimer environ 10k lignes de "données", puis exécutez delete sur "chaîne", le "existe" -query exécuter environ 80 secondes (par rapport à 180 secondes pour "count" -query). MAIS j'ai remarqué que lorsque je commets ma suppression de "données" et exécute la deuxième suppression après cela, ma requête "count" s'exécute avec 1.5 secondes, alors que la requête "exists" fonctionne toujours avec 80 secondes. Je préférerais les suppressions en une seule transaction. Mais 1,5 secondes sonne plutôt bien :) Je pourrais bien y aller jusqu'à ce qu'il n'y ait pas de meilleure solution. – Ago

+0

Ago: Renseignez-vous sur les contraintes de clés étrangères dans vos documents SGBD. Version courte: Si vous avez des tables enfants avec des contraintes de clé étrangère pour les tables parent, alors vous pouvez dire, par exemple "si je supprime l'enregistrement parent, cascade supprimer les enfants, aussi". Dans votre cas, vous souhaitez que la contrainte indique "Si j'essaie de supprimer le parent, mais qu'il existe des enfants, ne supprimez pas le parent". –

Questions connexes