Mysql
 sql >> Database >  >> RDS >> Mysql

MySQL non supporta la clausola limit all'interno di una sottoselezione, come posso farlo?

SELECT ... LIMIT non è supportato nelle sottoquery, temo, quindi è ora di scatenare la magia del self-join:

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;

Il bit nel mezzo sta elaborando l'ID del terzo articolo con ID più basso per ciascuna categoria cercando di unire tre copie della stessa tabella in ordine crescente di ID. Se ci sono meno di tre articoli per una categoria, i join di sinistra assicureranno che il limite sia NULL, quindi anche l'esterno WHERE deve raccogliere quel caso.

Se il tuo requisito "top 3" potrebbe cambiare in "top n" a un certo punto, questo inizia a diventare ingombrante. In tal caso potresti voler riconsiderare l'idea di interrogare prima l'elenco di categorie distinte, quindi unire le query per categoria.

ETA:Ordinare su due colonne:eek, nuovi requisiti! :-)

Dipende da cosa intendi:se stai solo cercando di ordinare il risultato finale puoi sbatterlo alla fine nessun problema. Ma se devi usare questo ordinamento per selezionare quali tre articoli devono essere selezionati, le cose sono molto più difficili.

Stiamo usando un self-join con '<' per riprodurre l'effetto che avrebbe 'ORDER BY article_id'. Sfortunatamente, mentre puoi fare "ORDINA PER a, b", non puoi do '(a, b)<(c, d)'... né puoi fare 'MIN(a, b)'. Inoltre, in realtà dovresti ordinare per tre colonne, issticky, pubblicato e article_id, perché devi assicurarti che ogni valore di ordinazione sia univoco, per evitare che vengano restituite quattro o più righe.

Mentre tu potresti crea il tuo valore ordinabile con un numero intero grezzo o una combinazione di stringhe di colonne:

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)

questo sta diventando incredibilmente brutto e i calcoli naufragheranno ogni possibilità di utilizzare gli indici per rendere efficiente la query. A quel punto è meglio eseguire semplicemente le query LIMITATE separate per categoria.