Home » Php » Create grouping rows by foreach and mysql in PHP

Create grouping rows by foreach and mysql in PHP

Posted by: admin February 25, 2020 Leave a comment

Questions:

This is my table structure :

----------------------
 # |  name  | active |
---|--------|--------|
 1 |  Name1 |   1    |
---|--------|--------|
 2 |  Name2 |   1    |
---|--------|--------|
 3 |  Name3 |   1    |
---|--------|--------|
 4 |  Name4 |   1    |
---|--------|--------|
 5 |  Name5 |   1    |
---|--------|--------|
 6 |  Name6 |   1    |
---|--------|--------|
 7 |  Name7 |   1    |
---|--------|--------|
 8 |  Name8 |   1    |
---|--------|--------|
 9 |  Name9 |   1    |
---|--------|--------|

I want to grouping (groups of three) rows by foreach and mysql, like the following:

--------------------------------
 Group 1 |  Group 2  | Group 3 |
---------|-----------|---------|
  Name1  |   Name4   |  Name7  |
---------|-----------|---------|
  Name2  |   Name5   |  Name8  |
---------|-----------|---------|
  Name3  |   Name6   |  Name9  |
---------|-----------|---------|

How do I do this?

How to&Answers:

Honestly I would not use SQL for the column layout task.

Just fetch the results for all names with a simple SQL query:

$stmt = $pdo->query("SELECT name FROM MyTable ORDER BY id");
$names = $stmt->fetchAll(PDO::FETCH_OBJ);

Then output your tabular format using PHP code:

$numColumns = 3;
$rowCount = $stmt->rowCount();
$numRows = ceil($rowCount / $numColumns);
for ($row = 0; $row < $numRows; $row++) {
  for ($col = 0; $col < $numColumns; $col++) {
    $i = $row + $numRows * $col;
    if ($i >= $rowCount) break;
    print $names[$i]->name;
    print "\t";
  }
  print "\n";
}

Output in my test:

Name1   Name4   Name7   
Name2   Name5   Name8   
Name3   Name6   Name9   

I added another row for Name10, and this is the output:

Name1   Name5   Name9   
Name2   Name6   Name10  
Name3   Name7   
Name4   Name8   

Answer:

In MySQL 8.0? you can do this with row_number() and aggregation:

select
    max(case when rn % 3 = 0 then name end) group1,
    max(case when rn % 3 = 1 then name end) group2,
    max(case when rn % 3 = 2 then name end) group3
from (select name, row_number() over(order by name) - 1 rn from mytable where active = 1) t
group by floor(rn / 3)

Note that this does not produces exactly the result that you showed: names are spread from left to right, then top to bottom (while your result is top to bottom, then left to right).

Demo on DB Fiddle:

group1 | group2 | group3
:----- | :----- | :-----
Name1  | Name2  | Name3 
Name4  | Name5  | Name6 
Name7  | Name8  | Name9 

In earlier versions of MySQL, you can emulate row_number() with variable, like so:

set @rn = -1; 
select
    max(case when rn % 3 = 0 then name end) group1,
    max(case when rn % 3 = 1 then name end) group2,
    max(case when rn % 3 = 2 then name end) group3
from (
    select name, @rn := @rn + 1 rn from mytable where active = 1 order by name) t
group by floor(rn / 3)

Demo on MySQL 5.6 DB Fiddle