sql >> Databáze >  >> RDS >> Mysql

Výpočet rozdílů hodnot mezi dvěma záznamy v Eloquent

Pak pro vás mám překvapení - Zde je malý test výkonu:

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 je tabulka s pouze jedním int sloupcem a 1 milionem řádků.

A výsledek je:

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

Takže „malý dopad na výkon od výmluvných“ je:

  • Téměř 20krát pomalejší než použití prostého PDO a stdClass při načítání dat z databáze.
  • Alespoň 100krát pomalejší než stdClass při čtení vlastností/atributů ve smyčce.

Pokud tedy chcete zlepšit výkon, přepněte při práci s velkým množstvím dat na obyčejné PDO nebo alespoň použijte výchozí Builder.

Nyní se stále můžete pokusit udělat práci v MySQL, ale požadavek na použití Eloquent by nedával smysl.

Můžete však zkusit smíšenou verzi – k vytvoření dotazu použijte Eloquent, ale převeďte jej na Database\Query\Builder pomocí getQuery() .

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

Ale vždy bych se vyvaroval používání proměnných relace tímto způsobem v kódu aplikace, protože jsem viděl mnoho takových řešení, která po aktualizaci verze vracela nesprávné/neočekávané výsledky.

Stále nejste přesvědčeni? Zde jsou některé další testy:

Použití proměnných relace ve výmluvném dotazu převedeném na 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 řešení využívající převedený výmluvný dotaz:

$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 řešení s prostým PDO a 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 řešení s prostým PDO a asociativními poli:

$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

Vaše preferované řešení je nejpomalejší a nejméně spolehlivé. Takže odpověď na vaši otázku je špatným řešením vašeho problému.




  1. SELECT odlišné hodnoty pro více řádků stejného ID

  2. Přidání více parametrizovaných proměnných do databáze v c#

  3. Zkontrolujte, zda v tabulce MySQL existuje sloupec pomocí PHP

  4. UID – vrátí ID aktuální relace v Oracle