Ik was even benieuwd of iemand een mooie handige query zou weten om de laatste vijf artikelen op te halen uit alle categoriëen, en hierbij te groeperen op de categorie. Ik krijg het niet voor elkaar na wat geklooid te hebben van GROUP BY en zelfs HAVING.

Momenteel heb ik dit stukje oude code waarbij ik dus een query uitvoer met in het statement een extra query.
Resultaat: met 8 categorieën dus 9 queries. Dit wil ik dus -als het kán- omlaag brengen naar één query.

En zo een resultaat wil ik eruit halen, en de enter halverwege heb ik even voor het overzicht geplaatst.

Title - DateCreation - Category
-----------------------------

Bla die bla - 11-5-2022 - Categorie A
Bla die bla - 12-5-2022 - Categorie A
Bla die bla - 13-5-2022 - Categorie A
Bla die bla - 14-5-2022 - Categorie A
Bla die bla - 15-5-2022 - Categorie A

Bla die bla - 11-5-2022 - Categorie B
Bla die bla - 12-5-2022 - Categorie B
Bla die bla - 13-5-2022 - Categorie B
Bla die bla - 14-5-2022 - Categorie B
Bla die bla - 15-5-2022 - Categorie B


Dit stukje code heb ik momenteel, maar er moet toch een query mogelijk zijn om bovenstaand resultaat te krijgen, zodat ik die tweede whileloop kan laten vervallen?


<?php
    $data = array();
    $result_cats = $db->query("SELECT ID, Title, Keyword FROM news_categories");
    while ($newscat = $result_cats->fetch_assoc()) {
	$sql = "SELECT ID, Title, DateCreation, Author FROM news_articles WHERE catID ='" . $db->real_escape_string($newscat['ID']) . "' AND concept=0 ORDER BY DateCreation DESC LIMIT 5";
	$result_items = $db->query($sql);
	while ($item = $result_items->fetch_assoc()) {
	    $item['slug'] = $slug->create($item['Title']);
	    $data[$newscat['Title']][] = $item;
	}
    }
?>
Als je de mogelijkheid hebt (MySQL 8+) kan je ook een window-functie gebruiken die een rijnummer aanmaakt per groep, zodat je in de WHERE al kan zeggen dat je alleen de eerste X rijen wilt.

Want wanneer je het met een HAVING doet, moet de database eerst alle rijen evalueren en dat pakt nadelig uit voor de performance.
Zoiets?

select `title`, cat, wanneer
from
    (
        select `title`, cat, `DateCreation`,
               (@rn:=if(@prev = cat, @rn +1, 1)) as rownumb,
               @prev:= cat
        from
            (
            SELECT `title`, cat, `DateCreation`
            FROM {table_name}
            ORDER BY cat ,  `DateCreation` desc, `title`
            )   as sortedlist
            JOIN (select @prev:=NULL, @rn :=0) as vars
            ORDER BY cat, `DateCreation`
    ) as groupedlist
WHERE rownumb<=5
ORDER BY cat , `DateCreation` desc, `title`;
Hier wat voorbeelden :

https://www.sqlines.com/mysql/how-to/get_top_n_each_group

De SQL code (MariaDB 10) volgens het laatste voorbeeld :

SELECT
	mk.brand AS brand,
	models.model AS model
FROM (
	SELECT
		models.brand_id AS brand_id,
		models.model AS model,
		row_number() over ( partition by models.brand_id
	ORDER BY
		models.brand_id DESC ) AS mobiel_rank
	FROM models ) ranked

LEFT JOIN
	make mk
ON
	mk.id = brand_id

WHERE
	mobiel_rank <= 5 AND brand_id <= 20; # aanpassen naar behoefte


Je zou er een VIEW of een STORED PROCEDURE van kunnen maken.

De bijbehorende database :

https://github.com/VinceG/Auto-Cars-Makes-And-Models
Thanks voor de reacties. Ik ga er morgen even naar kijken en uitzoeken hoe dit werkt. ;-)
Het zou leuk zijn als ik gelijk heb over de performance, maar al proberend (ik probeer MySQL te vermijden zoals ik wel eens heb laten weten :-)) kwam ik een nare melding tegen:

#4015 - Window function is allowed only in SELECT list and ORDER BY clause

Uiteindelijk kwam ik uit op iets als dit:
[mysql]
WITH testtabel AS (
  SELECT 'a' AS categorie, 1 AS nummer
  UNION SELECT 'a', 2
  UNION SELECT 'a', 3
  UNION SELECT 'b', 4
  UNION SELECT 'b', 5
  UNION SELECT 'b', 6
), gesorteerd AS (
  SELECT testtabel.*,
    ROW_NUMBER() OVER (
      PARTITION BY categorie 
      ORDER BY nummer DESC
    ) AS rn
  FROM testtabel
)
SELECT categorie, nummer
FROM gesorteerd
WHERE rn < 3;
[/mysql]

Teruglezend is dat dezelfde oplossing als wat Adoptive Solution schreef, maar dan met CTE in plaats van nested SELECTs. Dus die van Adoptive Solution is beter, omdat die ook met eerdere versies van MySQL werkt.
Het punt is wel dat ik een recente MariaDB gebruik. Ik ga ervanuit dat dit ook daarop zal werken gezien het een fork is.
Ja het werkt op MariaDB 10.3.
Daarmee had ik het getest, het is wat er op mijn bijna verouderde Synology NAS staat, maak je geen zorgen.

Reageren