Ok, c'est un peu difficile à expliquer, et le titre n'est pas le meilleur: / mais, en gros, j'ai 3 tables et j'essaye d'écrire une requête qui les renvoie avec 2 niveaux de classement. Le premier par le nombre de "balises" de chaque ligne, et le second par la création de cette ligne.
* Item1 * Item4 * Item2 * Item3
Et je voudrais lister tous les éléments qui ont une ou plusieurs balises, dans ordre croissant par date de création. Mais, et voici où je rencontre des problèmes, je veux que la commande soit basée sur le nombre de balises qui correspondent à un article et, dans cette commande, appliquer les critères ascendants.
Alors, disons J'ai:
Item1 -> CreatedAt(0), Tags(A, B) Item2 -> CreatedAt(0), Tags(A) Item3 -> CreatedAt(1), Tags(B) Item4 -> CreatedAt(1), Tags(A, B)
Ensuite, la recherche de tous les articles avec les balises A et B doit retourner d'abord Item1
et Item4
et commander ceux par ordre croissant de CreatedAt
. Et puis Item2
et Item3
et commandez-les par CreatedAt
par ordre croissant. Donc, le résultat serait:
Table1: Items * ID * Name * CreatedAt Table2: Tags * ID * Name Table3: TaggedItems * TagID * ItemID
Je suis inexpérimenté avec SQL donc peut-être y a-t-il des solutions très bien connues à cela si je l'explique même correctement?
Nous apprécions toute aide!
3 Réponses :
Vous pouvez effectuer un tri conditionnel:
select i.*, x.no_tags from items cross join lateral ( select count(*) no_tags from taggedItems ti inner join tags t on t.id = ti.tagID where ti.ItemID = i.ID and t.name in ('A', 'B') ) x order by (x.no_tags > 1) desc, case when x.no_tags > 1 then i.createdAt end desc, createdAt
La requête commence à partir de la table items
, puis utilise une jointure latérale
pour récupérer le nombre de balises correspondantes qui appartiennent à la liste cible (j'ai utilisé ('A', 'B')
comme dans votre exemple). Ensuite, order by
place les éléments qui ont plus d'une balise en premier, et utilise une expression conditionnelle pour les trier par date décroissante; les enregistrements avec une seule balise ne sont pas pris en compte par ce critère de tri, et tombent dans le dernier critère de tri, qui trie par date ascendante.
Essayez :
select i.id,i,name from Items i join TaggedItems ti on i.ID=ti.ItemID join Tags t on t.ID=ti.TagID group by i.id,i,name order by count(*),CreatedAt
L'expression order by count (*)
fonctionne pour la plupart des serveurs de base de données.
Utilisez une jointure LEFT
de Items
à TaggedItems
(juste au cas où un élément n'aurait pas de balise du tout) et groupez par élément .
Enfin trier par le nombre de balises décroissant puis par la date:
SELECT i.name FROM Items i LEFT JOIN TaggedItems ti ON ti.ItemID = i.ID LEFT JOIN Tags t ON t.ID = ti.TagID AND t.Name IN ('A', 'B') GROUP BY i.ID, i.Name ORDER BY COUNT(t.ID) DESC, i.CreatedAt
Le tableau Tags
n'est pas nécessaire.
Mais si vous voulez les résultats pour une liste de balises comme 'A'
et 'B'
alors:
SELECT i.name FROM Items i LEFT JOIN TaggedItems t ON t.ItemID = i.ID GROUP BY i.ID, i.Name ORDER BY COUNT(t.TagID) DESC, i.CreatedAt