【问题标题】:Querying 65k+ records with whereIn使用 whereIn 查询 65k+ 条记录
【发布时间】:2020-03-22 02:25:18
【问题描述】:

我有一个导出功能,我想使用给定的 ID 查询和导出。我找到了使用skip()take() 进行查询的解决方案,但是现在whereIn('id', $ids) 给了我错误,因为它现在有85k 条记录。

PDOException: SQLSTATE[HY000]: 一般错误: 1390 Prepared statement contains too many placeholder

我使用的解决方案是:

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 排序并取决于那里的顺序?

【问题讨论】:

  • 如何获取数组内容??如果您通过选择构建数组,则不需要 IN 子句
  • 我添加了一个编辑,显示负责它的控制器
  • 我不在lavavel,所以如果你需要,我可以建议一个基于纯sql的解决方案
  • 你显然应该使用 JOIN
  • @scaisEdge 谢谢,但我想知道 Laravel 的查询构建器方式。 - @YourCommonSense 你会如何使用 Larave 查询生成器来做呢?

标签: mysql laravel laravel-query-builder


【解决方案1】:

试试这个:在您的数据库配置文件中,在sql 或您用来连接数据库的驱动程序下,像这样添加一个字段options

'options'=> [PDO::ATTR_EMULATE_PREPARES => true]

PHP API 实际上说 (check this):

一些驱动程序不支持原生预处理语句或对它们的支持有限。

另一个解决方案应该是更改驱动程序,但我认为向 database.php 添加一行更容易做到

可能的副作用报告:这可能会将返回值转换为字符串,如果您使用=== 作为比较运算符,这将影响您的代码。您可以使用模型中的protected $casts 变量来解决这个问题(如果您使用的是 Laravel)

【讨论】:

  • 哇——这成功了。这到底是做什么的?有副作用吗?
  • 很高兴看到它有效...它到底是做什么的,我不知道,但 PHP API 没有说明任何副作用,我将在回答
  • 是的,我也在那里快速阅读过 - PDO::ATTR_EMULATE_PREPARES - 启用或禁用准备好的语句的模拟
  • 是的,我的意思是我不知道那句话中的“仿真”是什么意思。但是,如果这解决了您的问题,请将问题标记为关闭
  • 我发现了这个的副作用。它将所有值转换为字符串,例如 int 35"35" 和 boolean true"true"
猜你喜欢
  • 2011-01-21
  • 2015-07-06
  • 1970-01-01
  • 2015-12-12
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多