How to chunk results from a custom query in Laravel

前端 未结 8 1308
北海茫月
北海茫月 2020-12-17 18:37

I have a custom query that grabs data from the old system and maps it to models in the new system. The query looks like this:

$companies = DB::connection(\'leg

相关标签:
8条回答
  • 2020-12-17 19:19

    None of these answers worked for me. I created my own function based on @deyes answer.

    private static function chunk($query, $max, $function) {
        $counter = preg_replace('/SELECT (.*?) FROM/', 'SELECT COUNT(*) FROM', $query);
        $total = DB::connection('legacy')->select($counter)[0];
        $total = (array)$total;
        $total = $total['COUNT(*)'];
    
        $pages = ceil($total / $max);
    
        for ($i = 1; $i < ($pages + 1); $i++) {
            $offset = (($i - 1)  * $max);
            $start = ($offset == 0 ? 0 : ($offset + 1));
            $items = DB::connection('legacy')->select($query . ' LIMIT ' . $offset . ', ' . $max);
    
            $function($items);
    
            unset($items);
        }
    }
    

    Usage

    YourClass::chunk('SELECT * FROM tablename', 50, function($items) {
        //Work with $items.
    });
    

    Please note that this a simple quick fix and your query probably has to be fairly simple as I'm using search-replace to build a count query and I'm just tacking on LIMIT X, Y to the end of the query but it works for me.

    0 讨论(0)
  • 2020-12-17 19:21

    Update: March 2018 a new function was added to the query builder class. It's possible to achieve the same now using fromSub:

    $subQuery = DB::table('users')->where(...);
    
    DB::query()->fromSub($subQuery, 'alias')->orderBy('alias.id')->chunk(200, function ($chunk) {
        // Do something
    });
    

    And to use a different connection start with DB::connection('legacy')->query()


    Old answer: Found this question by accident, but a little trick that might come handy in some cases.

    $query = 'SELECT * FROM ... JOIN ... UNION ... WHATEVER ... GROUP BY';
    
    // This is the important part:
    $query = '(' . $query . ') somealias';
    
    DB::connection('legacy')->table(DB::raw($query))->chunk(1000, function($rows){
        // Do something
    });
    

    The query laravel executes then goes like this:

    select * from (...) somealias LIMIT ... OFFSET ...
    

    This should work at least in Laravel 5.1. But I don't see a reason why it shouldn't work in 4+.

    0 讨论(0)
  • 2020-12-17 19:23

    I believe you can use chunk on a query builder. E.g.

    DB::connection('legacy')->select("...")->chunk(200, function($companies){
        //do something with $companies
    });
    
    0 讨论(0)
  • 2020-12-17 19:28

    Try using the orderBy clause:

    DB::table('tbl')->where('num', '>', 3)->orderBy('id')->chunk(500, function($rows) {
        // process $rows
    });
    
    0 讨论(0)
  • 2020-12-17 19:30

    The chunk feature is only available for Eloquent models and QueryBuilder requests, e.g.

    DB::table('tbl')->where('num', '>', 3)->chunk(500, function($rows) {
        // process $rows
    });
    

    But it won't work for DB::select('...') request. You need to either use a QueryBuilder request, or use an underlying PDO object to query the database, e.g:

    $pdo = DB::getPdo();
    $sth = $pdo->prepare("SELECT ....");
    $sth->execute();
    while ($row = $sth->fetch(PDO::FETCH_ASSOC))
    {
        // ...
    }
    
    0 讨论(0)
  • 2020-12-17 19:35

    Try something like this:

    <?php
    
    $max = 100;
    $total = DB::connection('legacy')->select("...")->count();
    $pages = ceil($total / $max);
    for ($i = 1; $i < ($pages + 1); $i++) {
        $offset = (($i - 1)  * $max);
        $start = ($offset == 0 ? 0 : ($offset + 1));
        $legacy = DB::connection('legacy')->select("...")->skip($start)->take($max)->get();
        /* Do stuff. */
    }
    

    Basically duplicates what Laravel's Paginator does without the extra overhead.

    0 讨论(0)
提交回复
热议问题