【问题标题】:Is there a way to add a LIMIT to an UPDATE query in Doctrine ORM?有没有办法在 Doctrine ORM 中为 UPDATE 查询添加限制?
【发布时间】:2017-05-27 11:26:15
【问题描述】:

我正在使用 Doctrine 2.5.x,但在让 LIMIT 子句用于 UPDATE 查询时遇到问题。它总是更新所有匹配的记录(即它似乎忽略了 LIMIT 子句)。

setMaxResults() 与 UPDATE 查询一起使用时似乎没有效果。

作为一种快速解决方法,我使用的是原生 MySQL 查询,但这不是最佳解决方案。

我尝试了这些示例,但没有一个有效:

Doctrine update query with LIMIT

https://recalll.co/app/?q=doctrine2%20-%20Doctrine%20update%20query%20with%20LIMIT

带有setMaxResults() 的QueryBuilder(不起作用):

$qb = $em->createQueryBuilder();

$query = $qb->update('\Task\Entity', 't')
    ->set('t.ClaimedBy', 1)
    ->where('t.Claimed IS NULL')
    ->getQuery();
$query->setMaxResults(20);

$this->log($query->getSQL());

希望有人可以帮助找到比原生查询更好的解决方案。它带走了 ORM 的全部好处。

是否可以在 UPDATE 语句中使用 LIMIT 子句?

【问题讨论】:

  • 阅读Doctrine Query BuilderLimiting the Result。 PS:实际上你的帖子有点跑题了,因为它听起来更像是一个教程请求。
  • 感谢您的反馈,但我不是菜鸟。您是否测试过建议的链接?它不工作。它适用于 SELECT 但不适用于更新查询。我可以发布伪 DQL 或 QueryBuilder 代码,但这不会改变 setMaxResults 不适用于更新查询的事实。所以我不明白为什么它离题了。不过还是谢谢
  • 是的,为了更易于理解和切题,只需使用您遇到的示例查询构建器更新您的问题。阅读How to Ask a Question on StackOverflow 以便能够相应地改进您的问题。
  • 完成。好奇你现在是否明白为什么它不应该工作。
  • 乍一看您的 QueryBuilder 示例,您忘记了 $query->execute(); 查询(但也许您只是忘记复制/粘贴它)。其余的似乎是对的。

标签: php symfony doctrine-orm doctrine


【解决方案1】:

简而言之,不,因为 SQL 规范不支持UPDATE ... LIMIT ...,所以任何试图实现可移植性的 ORM 都不应该允许您这样做。


还请查看MySQL Reference Manual 本身说明UPDATE ... LIMIT ... 不是标准SQL 构造:

MySQL Server 支持一些您可能在其他 SQL DBMS 中找不到的扩展。请注意,如果您使用它们,您的代码将无法移植到其他 SQL 服务器。在某些情况下,您可以使用以下形式的 cmets 编写包含 MySQL 扩展但仍可移植的代码:

  • SQL 语句语法
    • UPDATE 和 DELETE 语句的 ORDER BY 和 LIMIT 子句。

所以本质上,因为您要实现的不是标准 SQL,所以 ORM 将没有可移植的方式来实现它,并且可能根本不会实现它。


抱歉,您试图通过 DQL 实现的目标是不可能的,因为:

Ocramius 于 2014 年 9 月 2 日发表评论
DQL 不允许限制 UPDATE 查询,因为它不可移植。

正如 DoctrineBundle 存储库的 this issue 所建议的,它的所有者 Marco Pivetta(他也恰好是 ORM 存储库的所有者)。

更多信息,虽然它可能需要一个很好的链接到正确的 ISO 规范文档,遗憾的是不能免费获得:

UPDATE 指令的 ISO 标准不允许UPDATE 中允许 LIMIT,其中 SELECT 当然是允许它的指令。

正如您自己提出的那样,ORM 的目的是不编写纯 SQL 以使其与 DBMS 兼容。如果不可能做到这一点,那么 ORM 没有实现它是有道理的。

另请注意,在 MYSQL 以外的其他 SQL 变体上,限制实际上是 SELECT 子句的一部分:

select * from demo limit 10

将在 SQL Server 中转换为

select top 10 from demo

或者在 Orcale 中

select * from demo WHERE rownum = 1

另见:https://stackoverflow.com/a/1063937/2123530

【讨论】:

  • 感谢您的链接。那个也看过。仍然没有解释或官方反馈不支持此功能。为什么“限制”应该在选择而不是更新时可用的问题仍然对我开放。当然,更多的人需要选择限制,否则 ORM 将毫无用处。但它也没有更新限制它的那种没用。
  • @Rob 据我了解,即使简短,ORM 所有者的回答也非常清楚。我更新了我的答案,以至少反映我对它的理解。
  • 感谢您非常详细的解释。看来它真的不支持。然后需要执行本机查询。不喜欢这种方法,但使用选择然后一个一个地更新它们对我来说不是一个真正的解决方案。感谢您的支持。
【解决方案2】:

正如 b.enoit.be 在他的回答中已经说明的那样,this is not possible 在 Doctrine 中,因为在 UPDATE 语句中使用 LIMIT 是不可移植的(仅在 MySQL 中有效)。

希望有人可以帮助找到比原生查询更好的解决方案。它带走了 ORM 的全部好处。

我认为您是在将业务规则与持久性混合在一起(幸运的是,ORM 不能很好地处理这一点)。

让我解释一下: 更新实体的状态不一定是业务规则。更新最大值20 个实体(这 20 个是从哪里来的?)。

为了解决这个问题,您应该通过将业务规则和持久性分离到服务中来正确分离它。

class TaskService
{

    private $taskRepository;

    public function __construct(TaskRepository $taskRepository)
    {
        $this->taskRepository = $taskRepository;
    }

    public function updateClaimedBy()
    {
        $criteria = ['Claimed' => null];
        $orderBy = null;

        // Only update the first 20 because XYZ
        $limit = 20;

        $tasks = $taskRepository->findBy($criteria, $orderBy, $limit);

        foreach($tasks as $task) {
            $task->setClaimedBy(1)
        }
    }

}

【讨论】:

  • 很好地试图理解底层问题。也就是说,当我看到像您展示的那样的代码时,那个 20 数字尖叫着现在的神奇数字,我觉得它需要一个 const 或作为服务中注入的参数。
  • @b.enoit.be 你是绝对正确的!但是,为简单起见,我不会将其添加到答案中。但是,这将是正确的方法,是的。
  • @Pete 我很欣赏你的工作,但对我来说这是题外话。从来都不是业务规则或持久性的问题。我在存储库中有一个接受限制参数的函数。我知道您的解决方案有效,但它是一个“花费”更多执行时间的解决方案。所以它不是我的解决方案。问题是关于更新限制。对于 SELECT 查询,此“不可移植”功能已实现,因此您的陈述对我来说毫无意义。而且我不明白为什么我在这里混合业务规则......所以当你不能帮助这个话题时,请离开这个线程。谢谢
  • @Rob “对于 SELECT 查询,实现了这个“不可移植”功能,所以你的语句对我来说毫无意义”也许是因为 SELECT 查询中的 LIMIT 可移植的?这句话毫无意义,因为它将苹果与橙子进行了比较。 “所以,当你无法就这个话题提供帮助时,请退出这个话题”当我把空闲时间花在“你的”话题上时,我觉得这很粗鲁。
  • 是的,即使您无法在 Oracle 或 MSSQL 上运行生成的查询,它也已实现...所以您不了解全部内容。这里 setMaxResult 知道 dbms。您曾经使用过 Oracle 或 MSSQL 吗?限制还有其他解决方案。我认为像学说这样的抽象层应该能够处理这些“边缘情况”
猜你喜欢
  • 2021-09-02
  • 2013-01-30
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 2013-02-20
  • 2015-08-11
  • 1970-01-01
  • 2022-11-24
相关资源
最近更新 更多