当数组太大时,Elequent whereIn 无法使用数组

Meh*_*ran 13 php mysql laravel

我有一个像这样的数组:

$array = ['1' , '2' ,'100'];

我的查询是:

$query = Customer::whereIn('id', $array)->get();

$array太大(1500 项及以上)时,它不起作用并返回空值。

我还增加了max_allowed_packetMySQL 配置。但问题没有解决。

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;在查询之前执行,或者在配置文件中全局设置它。(不要直接在命令行上执行查询,因为这是基于会话的修改)


Mih*_*hai 8

使用whereIntegerInRaw确保数组只是整数,但跳过准备

Customer::whereIntegerInRaw('id', $array)->get();
Run Code Online (Sandbox Code Playgroud)


Abd*_*afz 0

将查询分成块怎么样

  $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)