我想用雄辩的方式计算两个记录之间的差异。例如,我有以下表格:
----------------------------------
| Id | value | type |
----------------------------------
| 1 | 100 | FOO |
| 2 | 500 | FOO |
| 3 | 800 | FOO |
| 4 | 200 | BAR |
| 5 | 600 | BAR |
| 6 | 1000 | FOO |
----------------------------------
让我们说模型名称是FooBar,所以当我过滤表格时,例如,使用FooBar::where('type', 'FOO')->get();
我会得到以下结果:
----------------------------------
| Id | value | type | diff |
----------------------------------
| 1 | 100 | FOO | 0 |
| 2 | 500 | FOO | 400 | (500 - 100)
| 3 | 800 | FOO | 300 | (800 - 500)
| 6 | 1000 | FOO | 200 | (1000 - 800)
----------------------------------
现在,我可能更容易使用原始查询来实现这一点,比如声明变量来存储以前的记录(例如:SET @id:= 0并在SELECT语句中设置它)。但在这种情况下,如果可能的话,我更愿意使用Eloquent。
我目前的解决方法是循环结果集并手动计算,我担心它会影响性能。
有什么想法吗?
答案 0 :(得分:2)
我不介意雄辩的小型性能影响,而是循环播放 结果集计算差异..我的意思是,如果我有cmon 成千上万的记录,一个接一个地循环是一个粗略的想法
然后我对你感到惊讶 - 这是一个小型的性能测试:
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列和1M行的表。
结果是:
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
因此,“雄辩的小绩效影响”是:
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
$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和assotiative数组的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
您首选的解决方案是最慢和最不可靠的解决方案。所以你的问题的答案是你的问题的一个糟糕的解决方案。