I found this example exposed by A.Cedano. Everything works great except that I want to limit impressions by categories. That is, print only 3 news per category.
The LIMIT 3
only thing that I have added that it does is to limit the existing categories and not the links and text of the news. That is to say, if I have 10 categories, what it does is show me only 3 categories, but that is not what I am looking for, rather limit the result of impression data, that is, be able to show only 3 data per category.
What changes should be used in the following code?:
<?php
$strSQL="SELECT
c.title_category,
GROUP_CONCAT(COALESCE(n.mini_title,''),'≠',COALESCE(n.url,'') SEPARATOR '|') data
FROM category c
INNER JOIN news n ON c.id_category=n.id_category
WHERE language=? AND c.active=? AND n.active
GROUP BY c.id_category
LIMIT 3";
if($stmtcategory = $con->prepare($strSQL)){
$stmtcategory->bind_param("si",$language,$active);
$stmtcategory->execute();
$stmtcategory->store_result();
$strHTML='<div class="date-text">';
if ($stmtcategory->num_rows>0) {
$stmtcategory->bind_result($title_category, $data);
$arrResult=array();
while ($stmtcategory->fetch()) {
$arrResult[]=array("title_category"=>$title_category, "data"=>$data);
}
foreach ($arrResult as $row){
$strHTML.="<h4>".$row["title_category"]."</h4>";
$strHTML.="<ul>";
$arrTitleNews=explode("|",$row["data"]);
if(count(array_filter($arrTitleNews)) == 0){
$strHTML.="<li><span>No existe noticias nuevas</span></li>";
} else {
foreach ($arrTitleNews as $theData){
$arrTitleParts=explode("≠",$theData);
$partOne=($arrTitleParts[0]) ? $arrTitleParts[0] : "No data"; //
$theTitle=($arrTitleParts[1]) ? '<li><a href="'.$Get_Domain.''.$arrTitleParts[1].'">'.$partOne.'</a></li>' : "";
$strHTML.=$theTitle;
}
}
$strHTML.="</ul>";
}
} else {
$strHTML.="<ul>";
$strHTML.="<span>No existen noticias nuevas</span>";
$strHTML.="</ul>";
}
$strHTML.="</div>";
$stmtcategory->close();
} else {
$strHTML="Ocurrió un error en la consulta";
}
echo $strHTML;
?>
Indeed, if you use the
LIMIT 3
at the end of the query, you will be limited in the number of categories.To reduce the news by category, the
LIMIT 3
must go in theGROUP_CONCAT
such that like this:Following @OscarGarcia's comment, if your MySQL server does not support
LIMIT
enGROUP_CONCAT
, you can try substituting the line:By:
Which basically solves the problem from PHP, assigning a number to each news and keeping the top 3.
To solve your problem, you could use an
LIMIT
insideGROUP_CONCAT()
as @David JP suggests , but it would only work if you use a version of MariaDB 10.3.3 or higher .To make it work in any version of MySQL you should use this little trick with user variables that I propose:
What are you doing?
To start with I initialize the user variables to
0
conCROSS JOIN (SELECT @IDC := 0, @NUM := 0) iniciar
(to avoid aSET @IDC := 0, @NUM := 0
previous). Otherwise, the initial value would benull
, causing unwanted behavior.To continue, for each row I perform the following check:
@IDC
does not matchid_category
(we have started a new category) start@NUM
a1
.@NUM
.After that I update the value of
@IDC
to the value ofid_category
.This has the effect of counting the elements within each category, so at the end of the query I can filter all the records that have a count equal to or less than
3
.You can see the operation online at the following address:
Although I would suggest changing your SQL and PHP code so that the following works:
You can see the operation online at the following address:
This SQL code will require changes to your PHP code to get the data, as each record is not a category.
Finally, I have to warn you that you should not use
GROUP_CONCAT()
to get aggregated data obtained from other tables. You could run into problems if they contain the characters you use as a delimiter.Instead you should use, for example,
JSON_ARRAYAGG()
. It allows you to JSON format the aggregated data in a way that misinterpretation is not possible.For example:
See online example: