Mysql
 sql >> Base de données >  >> RDS >> Mysql

Calcul des différences de valeur entre deux enregistrements dans Eloquent

Alors j'ai une surprise pour vous - Voici un petit test de performance :

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 est un tableau avec une seule colonne int et 1 million de lignes.

Et le résultat est :

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

Ainsi, le "petit impact sur les performances d'eloquent" est :

  • Près de 20 fois plus lent qu'avec PDO simple et stdClass lors de la récupération des données de la base de données.
  • Au moins 100 fois plus lent que stdClass lors de la lecture de propriétés/attributs dans une boucle.

Donc, si vous souhaitez améliorer les performances, passez à PDO simple lorsque vous traitez de grandes quantités de données ou utilisez au moins le constructeur par défaut.

Maintenant, vous pouvez toujours essayer de faire le travail dans MySQL, mais l'obligation d'utiliser Eloquent n'aurait aucun sens.

Cependant, vous pouvez essayer une version mixte - Utilisez Eloquent pour créer la requête, mais convertissez-la en Database\Query\Builder avec getQuery() .

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

Mais j'éviterais toujours d'utiliser les variables de session de cette façon dans le code d'application, car j'ai vu beaucoup de ces solutions renvoyer des résultats erronés/inattendus après une mise à niveau de version.

Toujours pas convaincu ? Voici quelques autres tests :

Utilisation de variables de session dans une requête Eloquent convertie en Database\Query\Builder :

$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

Solution PHP utilisant la requête Eloquent convertie :

$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

Solution PHP avec PDO simple et stdClass

$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

Solution PHP avec PDO simple et tableaux associatifs :

$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

Votre solution préférée est la plus lente et la moins fiable. La réponse à votre question est donc une mauvaise solution à votre problème.