sql >> Databasteknik >  >> RDS >> Mysql

Beräknar värdeskillnader mellan två poster i Eloquent

Då har jag en överraskning till dig - Här är ett litet prestationstest:

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 är en tabell med endast en int-kolumn och 1M rader.

Och resultatet är:

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

Så den "lilla prestationspåverkan från vältalig" är:

  • Nästan 20 gånger långsammare än att använda vanlig PDO och stdClass när du hämtar data från databasen.
  • Minst 100 gånger långsammare än stdClass när du läser egenskaper/attribut i en loop.

Så om du vill förbättra prestandan, byt till vanlig PDO när du hanterar stora mängder data eller använd åtminstone standardbyggaren.

Nu kan du fortfarande försöka göra jobbet i MySQL, men kravet att använda Eloquent skulle inte vara meningsfullt.

Men du kan prova en blandad version - Använd Eloquent för att bygga frågan, men konvertera den till Database\Query\Builder med getQuery() .

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

Men jag skulle alltid undvika att använda sessionsvariabler på det här sättet i programkod, eftersom jag har sett många av sådana lösningar som returnerar felaktiga/oväntade resultat efter en versionsuppgradering.

Fortfarande inte övertygad? Här är några andra tester:

Använda sessionsvariabler i en vältalig fråga konverterad till 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

PHP-lösning med konverterad vältalig fråga:

$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

PHP-lösning med vanlig PDO och 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

PHP-lösning med vanlig PDO och assosiativa arrayer:

$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

Din föredragna lösning är den långsammaste och minst pålitliga. Så svaret på din fråga är en dålig lösning på ditt problem.




  1. PlanetScale &Vitess:referensintegritet med äldre delade databaser

  2. MySql:Tinyint (2) vs tinyint(1) - vad är skillnaden?

  3. Löpande summor för flera kategorier i MySQL

  4. Identifiera och hantera uppdragskritiska åtkomstapplikationer under ett utvecklingsprojekt