2010-03-20 5 views
1

J'ai une table primaire pour les articles qui sont liés par une table de jointure Info à une table Balises qui a seulement un petit nombre d'entrées. Je souhaite diviser la table Articles en supprimant des lignes ou en créant une nouvelle table avec uniquement les entrées souhaitées, en fonction de l'absence de lien vers une balise donnée. Il y a quelques millions d'articles. Comment puis-je faire ceci?Instruction SQL pour diviser une table basée sur une jointure

Tous les articles n'ont aucun tag et certains ont de nombreuses étiquettes.

Exemple:

table Articles 
    primary_key id 
table Info 
    foreign_key article_id 
    foreign_key tag_id 
table Tags 
    primary_key id 

Il me fut facile de séparer les articles qui n'ont le match dès le départ, donc je pensais que je pouvais le faire et utiliser un pas dans la déclaration mais est Si la course est lente, on ne sait pas si ça va finir. Je l'ai fait avec ces commandes:

INSERT INTO matched_articles SELECT * FROM articles a LEFT JOIN info i ON a.id = i.article_id WHERE i.tag_id = 5; 
INSERT INTO unmatched_articles SELECT * FROM articles a WHERE a.id NOT IN (SELECT m.id FROM matched_articles m); 

Si cela fait la différence, je suis sur Postgres.

Répondre

1
INSERT INTO matched_articles 
SELECT * FROM articles a LEFT JOIN info i ON a.id = i.article_id WHERE i.tag_id = 5; 

INSERT INTO unmatched_articles 
SELECT * FROM articles a WHERE a.id NOT IN (SELECT m.id FROM matched_articles m); 

Il y a tellement de problèmes ici, je ne sais pas par où commencer. OK dans votre premier insert, vous n'avez pas besoin d'une jointure à gauche, en fait vous n'en avez pas. Il devrait être

INSERT INTO matched_articles 
SELECT * FROM articles a INNER JOIN info i ON a.id = i.article_id WHERE i.tag_id = 5; 

si vous aviez besoin d'une gauche ADHÉRER vous aurait eu

INSERT INTO matched_articles 
SELECT * FROM articles a LEFT JOIN info i ON a.id = i.article_id AND i.tag_id = 5; 

Lorsque vous mettez quelque chose du côté droit d'une jointure gauche dans la clause where (autre que la recherche de la valeur nulle valeurs), alors vous le convertissez en jointure interne car il doit remplir cette condition, donc les enregistrements qui n'ont pas de correspondance dans la table de droite sont éliminés.

Maintenant, la deuxième instruction peut être faite avec un cas spécial de la jointure gauche, bien que ce que vous avez fonctionnera.

INSERT INTO matched_articles 
SELECT * FROM articles a 
LEFT JOIN info i ON a.id = i.article_id AND i.tag_id = 5 
WHERE i.tag_id is null 

Ceci vous donnera tous les enregistrements qui se trouvent dans la table d'information sauf ceux qui correspondent à la table des articles.

Maintenant, la prochaine chose, vous ne devriez pas écrire des stents d'insertion sans spécifier les champs que vous souhaitez insérer. Vous ne devez jamais écrire une déclaration select en utilisant select *, surtout si vous avez une jointure. C'est généralement codé paresseux, paresseux et devrait être corrigé. Et si quelqu'un changeait la structure de l'une des tables mais pas l'autre? Ce genre de chose est mauvais pour la maintenance et dans le cas d'une déclaration de sélection avec une jointure, elle retourne une colonne deux fois (la colonne de jointure) et c'est une perte de ressources serveur et réseau. Il est juste mauvais codage d'être trop paresseux préciser ce dont vous avez besoin et seulement ce dont vous avez besoin. Alors sortez de l'habitude et ne le faites plus pour aucun code de production.

Si l'emplacement actuel est trop lent, vous pouvez également le fixer avec les bons index. Les champs id sont-ils indexés sur les deux tables? En revanche, s'il y a des millions d'articles, il faudra du temps pour les insérer.Il est souvent préférable de le faire par lots peut-être 50000 à la fois (moins encore si cela prend trop de temps). Il suffit de faire l'insertion dans une boucle qui sélectionne les meilleurs enregistrements XXX, puis les boucles jusqu'à ce que le nombre de lignes affectées soit nul.

+0

Merci beaucoup pour votre aide. Je commence vraiment à travailler avec des bases de données, et vos commentaires ont été très utiles. Votre instruction d'insertion pour les correspondances a bien fonctionné, a été faite en quelques secondes ce qui avait déjà fonctionné pendant des heures avec ma méthode précédente. –

1

Vos requêtes semblent correctes sauf que la première devrait être une jointure interne, pas une jointure à gauche. Si vous voulez essayer quelque chose d'autre, considérez ceci:

INSERT INTO matched_articles 
SELECT * 
FROM articles a 
INNER JOIN info i ON a.id = i.article_id 
WHERE i.tag_id = 5; 

INSERT INTO unmatched_articles 
SELECT * 
FROM articles a 
LEFT JOIN info i ON a.id = i.article_id AND a.id <> 5 
WHERE a.id IS NULL 

Cela pourrait être plus rapide, mais vraiment, ce que vous avez est probablement correct si vous suffit de le faire une fois.

1

Vous ne savez pas si PostgreSQL a un concept de table temporaire.
Voici comment cela peut également être fait.

CREATE Table #temp 
AS SELECT A.ID, COUNT(i.*) AS Total 
FROM Articles A 
LEFT JOIN info i 
ON A.id = i.Article_ID AND i.Tag_ID = 5 
GROUP BY A.ID 

INSERT INTO Matched_Articles 
SELECT A.* 
FROM Articles A INNER JOIN #temp t 
ON A.ID = t.Article_ID AND T.Total = 0 

DELETE FROM #Temp 
WHERE Total = 0 

INSERT INTO UnMatched_Articles 
SELECT A.* 
FROM Articles AINNER JOIN #temp t 
ON A.ID = t.Article_ID 

Notez que je n'utilise aucun éditeur pour l'essayer.
J'espère que cela vous donne un indice sur la façon dont j'aborderais cela.

Questions connexes