96 votes

MySQL JOIN avec LIMIT 1 sur la table jointe

Je veux joindre deux tables, mais obtenir seulement un enregistrement de la table 2 par enregistrement de la table 1.

Par exemple :

SELECT c.id, c.title, p.id AS product_id, p.title
FROM categories AS c
JOIN products AS p ON c.id = p.category_id

Cela me permettrait d'avoir tous les enregistrements dans products ce qui n'est pas ce que je veux. Je veux 1 [le premier] produit par catégorie (j'ai une catégorie de produits). sort dans le champ des produits).

Comment dois-je m'y prendre ?

117voto

Kostanos Points 1126

J'aime davantage une autre approche décrite dans une question similaire : https://stackoverflow.com/a/11885521/2215679

Cette approche est préférable, notamment si vous devez afficher plusieurs champs dans SELECT. Pour éviter Error Code: 1241. Operand should contain 1 column(s) ou une double sous-sélection pour chaque colonne.

Pour votre situation, la requête devrait ressembler à ceci :

SELECT
 c.id,
 c.title,
 p.id AS product_id,
 p.title AS product_title
FROM categories AS c
JOIN products AS p ON
 p.id = (                                 --- the PRIMARY KEY
  SELECT p1.id FROM products AS p1
  WHERE c.id=p1.category_id
  ORDER BY p1.id LIMIT 1
 )

0 votes

@BenoitDuffez je parie que c'est plus difficile d'un degré exactement (le subselect).

1 votes

@BenoitDuffez - Voir ma solution pour répondre à votre question concernant l'impact sur les performances.

2 votes

Malheureusement, trop lent :(

34voto

Gravy Points 1770

La réponse acceptée par @goggin13 semble incorrecte. Les autres solutions fournies jusqu'à présent fonctionnent, mais souffrent du problème n+1 et, en tant que telles, ont un impact sur les performances.

Problème n+1 : s'il y a 100 catégories, il faut faire 1 select pour obtenir les catégories, puis pour chacune des 100 catégories retournées, il faut faire un select pour obtenir les produits de cette catégorie. Il faudrait donc effectuer 101 requêtes SELECT.

Ma solution alternative résout le problème n+1 et devrait donc être beaucoup plus performante puisque seulement 2 sélections sont effectuées.

SELECT
  *
FROM
    (SELECT c.id, c.title, p.id AS product_id, p.title
    FROM categories AS c
    JOIN products AS p ON c.id = p.category_id
    ORDER BY c.id ASC) AS a 
GROUP BY id;

16voto

Jessé Catrinck Points 28
SELECT c.id, c.title, p.id AS product_id, p.title
FROM categories AS c
JOIN products AS p ON c.id = p.category_id
GROUP BY c.id

Ceci retournera la première donnée dans les produits (égale limite 1).

1 votes

Le regroupement semble avoir des frais généraux importants. Si j'exécute la requête sans le regroupement (avec une limite), elle est la suivante beaucoup plus rapide. Je suppose que ça ignore les index ?

2 votes

Cela en renverra un, mais serait-ce nécessairement le premier ?

0 votes

Facile à mettre en œuvre et, si nécessaire, vous pouvez regrouper par plusieurs champs.

6voto

Krab Points 1359

Et ça ?

SELECT c.id, c.title, (SELECT id from products AS p 
                            WHERE c.id = p.category_id 
                            ORDER BY ... 
                            LIMIT 1)
   FROM categories AS c;

3voto

la_kal Points 11

La clause With ferait l'affaire. Quelque chose comme ça :

WITH SELECTION AS (SELECT id FROM products LIMIT 1)
SELECT a.id, c.id, c.title FROM selection a JOIN categories c ON (c.id = a.id);

0 votes

En passant, ceci ne fonctionnera pas avec MySQL 5.7 ou inférieur... LA SYNTAXE "WITH ... AS" est disponible dans MySQL 8, cependant.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X