Meh*_*ran 13 php mysql laravel
我有一个像这样的数组:
$array = ['1' , '2' ,'100'];
我的查询是:
$query = Customer::whereIn('id', $array)->get();
当$array
太大(1500 项及以上)时,它不起作用并返回空值。
我还增加了max_allowed_packet
MySQL 配置。但问题没有解决。
PHP v7.33、laravel v7.19、MySQL v 5.7
小智 16
好吧,这花了我连续 6 个小时,但我设法找到了问题的深层解释!
事实上问题不在于 eloquent 本身,而在于 eloquent 和 db 的结合来优化查询。
Eloquent 确实使用 准备了所有查询PDO::prepare()
。对于 whereIn() 查询,结果类似于
PREPARE SELECT * FROM model WHERE id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, .....);
Run Code Online (Sandbox Code Playgroud)
在他这边,数据库(mySQL、mariaDB)有一个名为in_predicate_conversion_threshold
专门优化 IN() 子句的变量。默认情况下,该值为1000
,这意味着查询将转换为暗示子查询的优化版本。
因此,在 IN 子句中绑定超过 999 个项目可能会导致错误,其中数据库将返回 0 个结果而不会出现错误。
解决方法 1:在 Laravel 端使用原始查询。
解决方法 2:使用未准备的语句。
解决方法 3:对选择或更新进行分块。
解决方法 4:增加或删除 IN 优化的阈值(值 0 = 无优化)。
对于最后一点,只需SET in_predicate_conversion_threshold = 0;
在查询之前执行,或者在配置文件中全局设置它。(不要直接在命令行上执行查询,因为这是基于会话的修改)
使用whereIntegerInRaw确保数组只是整数,但跳过准备
Customer::whereIntegerInRaw('id', $array)->get();
Run Code Online (Sandbox Code Playgroud)
将查询分成块怎么样
$chunk = 500;
$query = Customer::query()
for ($i = 0, $j = count($array); $i < $j; $i += $chunk) {
$temp = array_slice($array, $i, $i + $chunk);
$query->whereIn('id', $temp);
}
$result = $query->get();
Run Code Online (Sandbox Code Playgroud)