web-dev-qa-db-fra.com

Comment puis-je sélectionner des éléments dans un tableau où une seule colonne doit contenir deux (ou plus) valeurs?

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:

http://my.safaribooksonline.com/book/web-development/9780596529321/4dot-searching-and-ranking/querying

6
user658182

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:

  • la table s'appelle factors
  • il y a une contrainte UNIQUE sur (wordid, docid)
  • il y a une table 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 :)

9
ypercubeᵀᴹ