Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

nategg's avatar

Number the rows in a subquery using Query Builder (or Eloquent)?

I'm trying to line up two sets of players (users), who have drawn different colors, opposite each other on the same table row of the view. The players who will occupy the same row have no common field value to join them except their row number from their subqueries. I need to number the rows in the subqueries so the first player to draw green appears in the same row as the first to draw yellow, and so on. Each subquery joins the players table and the draws table, and the outer query joins the two subs using the player's row number from their subquery. The subquery row numbers don't need to be displayed, they're just for lining up the rows.

Tables:

players: id, name (simplified user);
draws: id, player_id, color

Desired result (greatly simplified):

    Green     |Row|         Yellow
Name   Draw             Name     Draw
Joe   green     1       Ed      yellow
Mary  green     2       John    yellow
Sam   green     3       Clara   yellow

I was able to produce that view last year experimenting with php and raw sql, before I even heard of Laravel (or tried OOP). Recently I recreated the desired result in Laravel by concatenating pure sql into "DB::select($query)" (shown below), but it won't let me paginate that (unless someone can show me how). So how can I do this with Query Builder or Eloquent? I've made many attempts with Query Builder (one below). But the row number attempts in the subqueries either cause errors or just don't work.

Errors

Mysql won't accept "DB::raw('(@rownum:=@rownum+1) AS RowNumY'))" coming from Query Builder as a member of the field list even tho it accepted it from "DB::select($query)" in the concatenated version. Or it rejects "(SELECT @rownum:=0) AS Outerrow_num" in the FROM clause. Or when it was accepted in the FROM clause, it overwrote the base table (draws), so that the fields from that table (draws.color) didn't exist.

Concatenated raw sql version (this works):

$query = 'select colorY, playerY, ';                     // outer query fields
$query .= '@rownum:= @rownum + 1 AS row_numOuter, ';    // outer query row number
$query .= 'colorG, playerG ';                            // outer query fields
$query .= 'from (SELECT @rownum:=0) AS Outerrow_num, '; // set outer row number=0
$query .= '(select drawsG.color as colorG, ';            // start green subquery field list
$query .= 'playersG.name as playerG, ';
$query .= '(@rownumG:=@rownumG + 1) AS RowNumG ';        // green subquery row number
$query .= 'from (select @rownumG:=0) AS rownumG, ';  // set green subquery row number=0
$query .= 'draws AS drawsG ';
$query .= 'inner join players as playersG ';            // join draws w/ players – green
$query .= 'on playersG.id = drawsG.player_id ';
$query .= 'where color = "green") ';
$query .= 'as drawsGreen ';                             // green subquery alias
$query .= 'right join ';             // join green and yellow subqueries (yellow on right-has more in my db)
$query .= '(select drawsY.color as colorY, ';           // start yellow subquery field list
$query .= 'playersY.name as playerY, ';
$query .= '(@rownumY:=@rownumY + 1) AS RowNumY ';       // yellow subquery row number
$query .= 'from (select @rownumY:=0) AS rownumY, ';     // set yellow subquery row number=0
$query .= 'draws AS drawsY ';
$query .= 'inner join players as playersY ';            // join draws w/ players - yellow
$query .= 'on playersY.id = drawsY.player_id ';
$query .= 'where color = "yellow") ';
$query .= 'as drawsYellow ';                            // yellow subquery alias
$query .= 'on RowNumG = RowNumY';                       // ON clause for right join

$draws = DB::select($query);
return view('draws.index', compact(['draws']));

So here's my (latest?) attempt to do it via Query Builder:

$subQYellow = DB::table('draws AS drawsY')                 // yellow subquery, on draws table
    ->join('players AS playersY', 'playersY.id', '=', 'drawsY.player_id')    // join to players table on id
    ->select('color AS colorY', 'playersY.name AS playerY',         // yellow field list 
        DB::raw('(@rownum:=@rownum+1) AS RowNumY'))        // set yellow subquery row number=0
    ->from(DB::raw('(select @rownum:=0) AS rownumY'),          // from "list", yellow subquery row number
('draws AS drawsY'))                          // repeat draws table (??)
    ->where('color', 'yellow');              // color value filter

$subQGreen = DB::table('draws AS drawsG')                // green subquery, on draws table
    ->join('players AS playersG', 'playersG.id', '=', 'drawsG.player_id')    // join to players table on id
    ->select('color AS colorG', 'playersG.name AS playerG',         // green field list 
        DB::raw('(@rownum:=@rownum+1) AS RowNumG'))          // set yellow subquery row number=0
    ->from(DB::raw('(select @rownum:=0) AS rownumG'),             // from "list", green subquery row number 
('draws AS drawsG'))                              // repeat draws table (??)
    ->where('color', 'green');                   // color value filter

$draws = DB::table($subgreen, 'drawsGrn')                  // QB on subgreen w/alias
    ->select('colorY AS colorYlw', 'playerY AS playerYlw',        // outer query field list w/ aliases
'colorG AS colorGrn', 'playerG AS playerGrn')                  // more fields
    ->rightJoinSub($subyellow, 'drawsYlw', function ($join) {        // joinSub on yellow with closure
        $join->on('RowNumG', '=', 'RowNumY');                // outer query ON clause to join subs
    })              
    ->get();

Note: I had to repeat the draws table in FROM clause of each subquery because the row numbering code in the FROM clause seemed to overwrite it. If I put it in another ->from clause, the second FROM overwrites the first FROM. Catch 22. Note: I'm never sure when mysql needs aliases. Is there a rule? Any help? Thanks in advance.

0 likes
1 reply
nategg's avatar

Been waiting for help 3 years. Is it too complicated, or can't be done? Anyone have any ideas? There must be a way to do this. Thanks

Please or to participate in this conversation.