J'ai une table de base de données MySQL qui référence différents mots et leurs emplacements dans les documents. Je souhaite renvoyer les identifiants des documents contenant tous les mots.
Voici un exemple de tableau.
docid wordid
1 4
2 4
1 2
1 5
Ok, disons maintenant que quelqu'un a interrogé la base de données pour les mots qui avaient les WORDID 4, 2 et 5.
Mon instruction SQL SELECT erronée ressemblerait à ceci:
Select docid from table where wordid = 4 and wordid = 2 and wordid = 5
Cela me donne 0 résultats.
I ai v dans d'autres endroits où le where in
une clause a été suggérée:
Si je comprends bien, c'est une autre façon d'écrire une clause OR. J'ai essayé ceci:
select docid from table where wordid in (4,2,5)
Mais cela me donne tous les résultats. Il devrait exclure le docid 2 car il ne contient pas les autres mots. Je m'attends à obtenir juste docid 1.
Cependant, je pourrais utiliser le where in
clause incorrecte car j'ai très peu d'expérience en db.
Comment puis-je retourner des docids contenant tous les mots?
Veuillez également noter que ma clause where sera générée dynamiquement dans une boucle FOR. La requête peut être aussi simple qu'un ou deux mots ou 10 ou 12 mots. Je recherche une structure de requête qui prend en compte la vitesse. Veuillez me faire savoir si vous avez besoin de plus d'informations.
Pour référence, j'essaie de convertir ce code en PHP/MYSQL, mais je ne comprends pas l'instruction sql ici ou son équivalent en MYSQL:
C'est le problème division relationnelle et il y a une question à ce sujet chez SO, avec beaucoup de façons d'écrire cette requête , ainsi qu'une analyse des performances pour PostgreSQL: Comment filtrer les résultats SQL dans une relation has-many-through
Copiez sans vergogne le code ici et supprimez/modifiez le code pour les réponses qui ont des fonctionnalités qui manquent à MySQL, comme les CTE, EXCEPT
, INTERSECT
, etc., voici quelques façons de le faire.
Hypothèses:
factors
UNIQUE
sur (wordid, docid)
documents
et une table words
:Facile à écrire, efficacité moyenne:
-- Query 1 -- by Martin
SELECT d.docid, d.docname
FROM document d
JOIN factors f USING (docid)
WHERE f.wordid IN (2, 4, 5)
GROUP BY d.docid
HAVING COUNT(*) = 3 ; -- number of words
Facile à écrire, efficacité moyenne:
-- Query 2 -- by Erwin
SELECT d.docid, d.docname
FROM documents d
JOIN (
SELECT docid
FROM factors
WHERE wordid IN (2, 4, 5)
GROUP BY docid
HAVING COUNT(*) = 3
) f USING (docid) ;
Plus complexe à écrire, très bonne efficacité dans Postgres - probablement moche dans MySQL:
-- Query 4 -- by Derek
SELECT d.docid, d.docname
FROM documents d
WHERE d.docid IN (SELECT docid FROM factors WHERE wordid = 2)
AND d.docid IN (SELECT docid FROM factors WHERE wordid = 4);
AND d.docid IN (SELECT docid FROM factors WHERE wordid = 5);
Plus complexe à écrire, très bonne efficacité dans Postgres - et probablement la même chose dans MySQL:
-- Query 5 -- by Erwin
SELECT d.docid, d.docname
FROM documents d
WHERE EXISTS (SELECT * FROM factors
WHERE docid = d.docid AND wordid = 2)
AND EXISTS (SELECT * FROM factors
WHERE docid = d.docid AND wordid = 4)
AND EXISTS (SELECT * FROM factors
WHERE docid = d.docid AND wordid = 5) ;
Plus complexe à écrire, très bonne efficacité dans Postgres - et probablement la même chose dans MySQL:
-- Query 6 -- by Sean
SELECT d.docid, d.docname
FROM documents d
JOIN factors x ON d.docid = x.docid
JOIN factors y ON d.docid = y.docid
JOIN factors z ON d.docid = z.docid
WHERE x.wordid = 2
AND y.wordid = 4
AND z.wordid = 5 ;
Facile à écrire et à étendre à un ensemble arbitraire de words
mais pas aussi efficace que les solutions JOIN
et EXISTS
:
-- Query 7 -- by ypercube
SELECT d.docid, d.docname
FROM documents d
WHERE NOT EXISTS (
SELECT *
FROM words AS w
WHERE w.wordid IN (2, 4, 5)
AND NOT EXISTS (
SELECT *
FROM factors AS f
WHERE f.docid = d.docid
AND f.wordid = w.wordid
)
);
Facile à écrire, pas une bonne efficacité:
-- Query 8 -- by ypercube
SELECT d.docid, d.docname
FROM documents d
WHERE NOT EXISTS (
SELECT *
FROM (
SELECT 2 AS wordid UNION ALL
SELECT 4 UNION ALL
SELECT 5
) AS w
WHERE NOT EXISTS (
SELECT *
FROM factors AS f
WHERE f.docid = d.docid
AND f.wordid = w.wordid
)
);
Profitez de les tester :)