Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Multiple Unions

Tags:

php

laravel-4

I'm having an issue adding a query with multiple Unions the "laravel way".

The I am trying to accomplish a query equivalent to the one generated by the following:

$ipsql = "";
for ($n = 1; $n < $total_networks; $n++) {
    $ipsql .= "(SELECT * FROM ip WHERE network = " . $n . " AND used = 0 LIMIT 5)
            UNION ALL";
}
if ($n == $total_networks) {
    $ipsql .= "(SELECT * FROM ip WHERE network = " . $n . " AND used = 0 LIMIT 3) ORDER BY ip_addr";
}

I haven't found an option for unions with Eloquent so I was attempting to use the query builder for this particular section but I keep running into an issue while using the builders unionAll.

Using this:

$ip_list = DB::table('ips')->where('network', '=', '0')->where('used', '=', '0')->limit(5);
        for($n = 1; $n < $network_count; $n++){
            $ip_list = DB::table('ips')->where('network', '=', $n)->where('used', '=', '0')->limit(5)->unionAll($ip_list);
        }
        $ips = $ip_list->get();

I keep getting a MySQL syntax error:

     SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax;
     check the manual that corresponds to your MySQL server version for the right syntax to use near
     'union all ((select * from `ips` where `network` = ? and `used` = ? limit 5) unio' at line 1 
    (SQL:
         (select * from `ips` where `network` = 16 and `used` = 0 limit 5) union all ((select * from `ips`
         where `network` = 15 and `used` = 0 limit 5) union all ((select * from `ips` where `network` = 14
         and `used` = 0 limit 5) union all ((select * from `ips` where `network` = 13 and `used` = 0 limit 5)
         union all ((select * from `ips` where `network` = 12 and `used` = 0 limit 5) union all ((select *
         from `ips` where `network` = 11 and `used` = 0 limit 5) union all ((select * from `ips` where
         `network` = 10 and `used` = 0 limit 5) union all ((select * from `ips` where `network` = 9 and
         `used` = 0 limit 5) union all ((select * from `ips` where `network` = 8 and `used` = 0 limit 5)
 union all ((select * from `ips` where `network` = 7 and `used` = 0 limit 5) union all ((select * from
         `ips` where `network` = 6 and `used` = 0 limit 5) union all ((select * from `ips` where `network` =
         5 and `used` = 0 limit 5) union all ((select * from `ips` where `network` = 4 and `used` = 0 limit
         5) union all ((select * from `ips` where `network` = 3 and `used` = 0 limit 5) union all ((select *
         from `ips` where `network` = 2 and `used` = 0 limit 5) union all ((select * from `ips` where
         `network` = 1 and `used` = 0 limit 5) union all (select * from `ips` where `network` = 0 and `used`
         = 0 limit 5)))))))))))))))))

I can see from the error that its nesting each new union call which is creating the syntax issue. I tried accomplishing the same task with DB::raw but seem to be goofing that somewhere also. Is there a way to accomplish this thats better suited to laravel? Thanks for looking!

like image 454
Tom McDonald Avatar asked Sep 19 '14 00:09

Tom McDonald


1 Answers

Your unionAll calls are indeed getting nested. One solution is to create a subquery in the for loop, and then unionAll that subquery to the main query after it's been defined. Then you run get on the whole shebang when you're done, like so:

$ips_list = DB::table('ips')->where('network', '=', '1')->where('used', '=', '0')->limit(5);

for($n = 1; $n < $total_networks; $n++){
    $ip_list_subquery = DB::table('ips')
             ->where('network', '=', $n)
             ->where('used', '=', '0')
             ->limit(5);
    $ips_list = $ips_list->unionAll($ip_list_subquery);
}
$ips = $ips_list->get();

So, effectively, you're chaining the unionAll calls:

$a->unionAll($b)->unionAll($c)->unionAll($d)...

rather than nesting them:

$a->unionAll($b->unionAll($c->unionAll($d...))))

like image 59
damiani Avatar answered Oct 20 '22 00:10

damiani