Mysql
 sql >> Base de données >  >> RDS >> Mysql

MySQL ne prend pas en charge la clause limit dans une sous-sélection, comment puis-je faire cela ?

SELECT ... LIMIT n'est pas pris en charge dans les sous-requêtes, j'en ai bien peur, il est donc temps de sortir la magie de l'auto-jointure :

SELECT article.*
FROM article
JOIN (
    SELECT a0.category_id AS id, MIN(a2.article_id) AS lim
    FROM article AS a0
    LEFT JOIN article AS a1 ON a1.category_id=a0.category_id AND a1.article_id>a0.article_id
    LEFT JOIN article AS a2 ON a2.category_id=a1.category_id AND a2.article_id>a1.article_id
    GROUP BY id
) AS cat ON cat.id=article.category_id
WHERE article.article_id<=cat.lim OR cat.lim IS NULL
ORDER BY article_id;

Le bit du milieu calcule l'ID de l'article ayant le troisième ID le plus bas pour chaque catégorie en essayant de joindre trois copies de la même table dans l'ordre croissant des ID. S'il y a moins de trois articles pour une catégorie, les jointures à gauche garantiront que la limite est NULL, donc le WHERE externe doit également prendre en compte ce cas.

Si votre exigence "top 3" peut changer en "top n" à un moment donné, cela commence à devenir difficile à manier. Dans ce cas, vous voudrez peut-être reconsidérer l'idée d'interroger d'abord la liste des catégories distinctes, puis d'unir les requêtes par catégorie.

ETA :Commande sur deux colonnes :eek, nouvelles exigences ! :-)

Cela dépend de ce que vous voulez dire :si vous n'essayez que de commander les résultats finaux, vous pouvez le frapper à la fin sans problème. Mais si vous devez utiliser cet ordre pour sélectionner les trois articles à sélectionner, les choses sont beaucoup plus difficiles.

Nous utilisons une auto-jointure avec '<' pour reproduire l'effet qu'aurait 'ORDER BY article_id'. Malheureusement, alors que vous pouvez faire "COMMANDER PAR a, b", vous ne pouvez pas faites ‘(a, b)<(c, d)’... vous ne pouvez pas non plus faire ‘MIN(a, b)’. De plus, vous seriez en fait en train de trier par trois colonnes, issticky, publié et article_id, car vous devez vous assurer que chaque valeur de tri est unique, pour éviter de renvoyer quatre lignes ou plus.

Alors que vous pourriez composez votre propre valeur ordonnable par un nombre entier brut ou une combinaison de chaînes de colonnes :

LEFT JOIN article AS a1
ON a1.category_id=a0.category_id
AND HEX(a1.issticky)+HEX(a1.published_at)+HEX(a1.article_id)>HEX(a0.issticky)+HEX(a0.published_at)+HEX(a0.article_id)

cela devient incroyablement moche, et les calculs anéantiront toute chance d'utiliser les indices pour rendre la requête efficace. À ce stade, vous feriez mieux de simplement effectuer les requêtes séparées par catégorie LIMITED.