sql >> データベース >  >> RDS >> Mysql

Eloquentの2つのレコード間の値の差を計算する

    それから私はあなたに驚きを持っています-ここに小さなパフォーマンステストがあります:

    class Seq extends Eloquent {
        protected $table = 'helper.seq';
        protected $primaryKey = 'i';
    }
    
    Route::get('/loop', function () {
        $limit = 10000;
    
        $st = microtime(true);
        $data = Seq::orderBy('i')->take($limit)->get();
        var_dump(microtime(true) - $st);
    
        $st = microtime(true);
        foreach ($data as $row) {
            $row->i;
        }
        var_dump(microtime(true) - $st);
    
        $pdo = DB::getPdo();
        $st = microtime(true);
        $data2 = $pdo
            ->query("select * from helper.seq order by i limit $limit")
            ->fetchAll(PDO::FETCH_OBJ);
        var_dump(microtime(true) - $st);
    
        $st = microtime(true);
        foreach ($data2 as $k => $row) {
            if ($k == 0) {
                $row->diff = 0;
            } else {
                $row->diff = $row->i - $data2[$k-1]->i;
            }
        }
        var_dump(microtime(true) - $st);
    });
    

    helper.seq は、int列が1つと行が100万行しかないテーブルです。

    結果は次のとおりです。

    0.779045s <- Fetch from DB with Eloquent
    
    1.022058s <- Read Eloquent data (Only one column and do nothing with it)
    
    0.020002s <- Fetch from DB with PDO
    
    0.009999s <- Calculate all diffs in a loop
    

    したがって、「雄弁なパフォーマンスへの小さな影響」は次のとおりです。

    • プレーンPDOとstdClassを使用するよりも約20倍遅くなります データベースからデータをフェッチするとき。
    • stdClassより少なくとも100倍遅い ループ内のプロパティ/属性を読み取るとき。

    したがって、パフォーマンスを向上させたい場合は、大量のデータを処理するときにプレーンPDOに切り替えるか、少なくともデフォルトのビルダーを使用してください。

    これで、MySQLでその仕事を試すことができますが、Eloquentを使用するという要件は意味を成しません。

    ただし、混合バージョンを試すことはできます-Eloquentを使用してクエリを作成しますが、それをDatabase\Query\Builderに変換します getQuery()を使用 。

    $fooBars = FooBar::where('type', 'FOO')->orderBy('id')
        ->getQuery()
        ->select(['*', DB::raw('coalesce(`value` - @last, 0)'), DB::raw('@last := `value`')])
        ->get();
    

    しかし、バージョンのアップグレード後にそのようなソリューションの多くが間違った/予期しない結果を返すのを見たので、アプリケーションコードでこのようにセッション変数を使用することは常に避けます。

    まだ納得できませんか?その他のテストは次のとおりです。

    Database\Query\Builderに変換されたEloquentクエリでのセッション変数の使用 :

    $st = microtime(true);
    $data = Seq::getQuery()
        ->select(['*', DB::raw('coalesce(i - @last, 0)'), DB::raw('@last := i')])
        ->orderBy('i')->take($limit)->get();
    var_dump(microtime(true) - $st);
    
    // runtime: 0.045002s
    

    変換されたEloquentクエリを使用したPHPソリューション:

    $st = microtime(true);
    $data2 = Seq::getQuery()->orderBy('i')->take($limit)->get();
    foreach ($data2 as $k => $row) {
        if ($k == 0) {
            $row->diff = 0;
        } else {
            $row->diff = $row->i - $data2[$k-1]->i;
        }
    }
    var_dump(microtime(true) - $st);
    
    // runtime: 0.039002
    

    プレーンPDOとstdClassを使用したPHPソリューション

    $st = microtime(true);
    $data3 = $pdo
        ->query("select * from helper.seq s1 order by i limit $limit")
        ->fetchAll(PDO::FETCH_OBJ);
    foreach ($data3 as $k => $row) {
        if ($k == 0) {
            $row->diff = 0;
        } else {
            $row->diff = $row->i - $data3[$k-1]->i;
        }
    }
    var_dump(microtime(true) - $st);
    
    // runtime: 0.035001s
    

    プレーンなPDOと連想配列を使用したPHPソリューション:

    $st = microtime(true);
    $data4 = $pdo
        ->query("select * from helper.seq s1 order by i limit $limit")
        ->fetchAll(PDO::FETCH_ASSOC);
    foreach ($data4 as $k => $row) {
        if ($k == 0) {
            $row['diff'] = 0;
        } else {
            $row['diff'] = $row['i'] - $data4[$k-1]['i'];
        }
    }
    var_dump(microtime(true) - $st);
    
    // runtime: 0.027001s
    

    推奨されるソリューションは、最も低速で信頼性が最も低いソリューションです。したがって、あなたの質問に対する答えは、あなたの問題に対する悪い解決策です。




    1. 区切り文字に基づくT-SQL分割文字列

    2. InnoDBのAuto_increment値?

    3. 'X'キロメートル(またはマイル)内の都市の検索

    4. 分散ADを使用してOracleEBSのパッチ適用時間を短縮する方法