使用 whereIn 查询 65k+ 条记录
Querying 65k+ records with whereIn
我有导出功能,我想使用给定的 ID 进行查询和导出。我找到了使用 skip()
和 take()
查询的解决方案,但是现在 whereIn('id', $ids)
给我错误,因为它现在有 85k 条记录。
PDOException: SQLSTATE[HY000]: General error: 1390 Prepared statement contains too many placeholders
我使用的解决方案是:
public function handle() {
$max = 5000;
$total = $this->givenIds->count();
$pages = ceil($total / $max);
for ($i = 1; $i < ($pages + 1); $i++) {
$offset = (($i - 1) * $max);
$start = ($offset == 0 ? 0 : ($offset + 1));
MyModel::whereIn('id', $givenIds)
->orderBy('created_at', 'desc')
->skip($start)
->take($max)
->get();
// $this->generateTempCsv();
}
// $this->combineTempCSVs()
}
这在没有瓶颈的情况下工作正常 - 直到占位符错误太多。现在我收到这个错误,我尝试了:
$query = MyModel::orderBy('created_at', 'desc')
foreach ($this->givenIds->chunk(2000) as $i => $chunk) {
if ($i == 0) {
$query = $query->whereIn('id', $chunk);
} else {
$query = $query->orWhereIn('id', $chunk);
}
}
这给出了同样的错误。
如果我想使用 givenIds 查询,并且仍然想保持 "orderBy - created_at" 没有瓶颈我应该怎么做?
givenIds来自Controller(如上代码为Job):
$givenIds = MyModel::select('id', 'created_at')
->where(function($q) use ($dateRange, $queries) {
$this->applyQueryFilters($q, $queries, $dateRange);
})
->orderBy('created_at', 'desc')
->pluck('id');
Exporter::dispatch($givenIds)->onQueue('export');
PS:我使用$givenIds
的原因是因为有过滤器,首先我在应用过滤器后得到相关的id,然后将$givenIds
传递到作业中。获取 $givenIds
时是否需要按 id 排序并取决于那里的顺序?
试试这个:在您的数据库配置文件中,在 sql
或您用来连接到数据库的驱动程序下,添加一个字段 options
像这样
'options'=> [PDO::ATTR_EMULATE_PREPARES => true]
PHP API 事实上说 (check this):
Some drivers do not support native prepared statements or have limited support for them.
另一种解决方案应该是更改驱动程序,但我想在 database.php 中添加一行更容易做到
可能的副作用报告: 这可能会将 return 值转换为字符串,如果您使用 ===
作为比较运算符,这将影响您的代码.您可以使用模型中的 protected $casts
变量解决此问题(如果您使用的是 Laravel)
我有导出功能,我想使用给定的 ID 进行查询和导出。我找到了使用 skip()
和 take()
查询的解决方案,但是现在 whereIn('id', $ids)
给我错误,因为它现在有 85k 条记录。
PDOException: SQLSTATE[HY000]: General error: 1390 Prepared statement contains too many placeholders
我使用的解决方案是:
public function handle() {
$max = 5000;
$total = $this->givenIds->count();
$pages = ceil($total / $max);
for ($i = 1; $i < ($pages + 1); $i++) {
$offset = (($i - 1) * $max);
$start = ($offset == 0 ? 0 : ($offset + 1));
MyModel::whereIn('id', $givenIds)
->orderBy('created_at', 'desc')
->skip($start)
->take($max)
->get();
// $this->generateTempCsv();
}
// $this->combineTempCSVs()
}
这在没有瓶颈的情况下工作正常 - 直到占位符错误太多。现在我收到这个错误,我尝试了:
$query = MyModel::orderBy('created_at', 'desc')
foreach ($this->givenIds->chunk(2000) as $i => $chunk) {
if ($i == 0) {
$query = $query->whereIn('id', $chunk);
} else {
$query = $query->orWhereIn('id', $chunk);
}
}
这给出了同样的错误。
如果我想使用 givenIds 查询,并且仍然想保持 "orderBy - created_at" 没有瓶颈我应该怎么做?
givenIds来自Controller(如上代码为Job):
$givenIds = MyModel::select('id', 'created_at')
->where(function($q) use ($dateRange, $queries) {
$this->applyQueryFilters($q, $queries, $dateRange);
})
->orderBy('created_at', 'desc')
->pluck('id');
Exporter::dispatch($givenIds)->onQueue('export');
PS:我使用$givenIds
的原因是因为有过滤器,首先我在应用过滤器后得到相关的id,然后将$givenIds
传递到作业中。获取 $givenIds
时是否需要按 id 排序并取决于那里的顺序?
试试这个:在您的数据库配置文件中,在 sql
或您用来连接到数据库的驱动程序下,添加一个字段 options
像这样
'options'=> [PDO::ATTR_EMULATE_PREPARES => true]
PHP API 事实上说 (check this):
Some drivers do not support native prepared statements or have limited support for them.
另一种解决方案应该是更改驱动程序,但我想在 database.php 中添加一行更容易做到
可能的副作用报告: 这可能会将 return 值转换为字符串,如果您使用 ===
作为比较运算符,这将影响您的代码.您可以使用模型中的 protected $casts
变量解决此问题(如果您使用的是 Laravel)