有没有办法在Doctrine ORM中向UPDATE查询添加LIMIT?

时间:2017-05-27 11:26:16

标签: php symfony doctrine-orm doctrine

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

与UPDATE查询一起使用时,

setMaxResults()似乎无效。

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

我试过这些例子,但没有一个正在运作:

Doctrine update query with LIMIT

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

QueryBuilder与setMaxResults()(不起作用):

$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子句吗?

2 个答案:

答案 0 :(得分:2)

简而言之,不,因为SQL规范不支持UPDATE ... LIMIT ...,因此尝试实现可移植性的ORM都不允许您这样做。

请同时查看MySQL Reference Manual本身,指出UPDATE ... LIMIT ...不是标准的SQL结构:

  

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

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

因此,本质上是因为您尝试实现的不是标准SQL,ORM将无法以可移植的方式实现它,并且可能根本不会实现它。

很抱歉,但是你想通过DQL实现的目标是不可能的,因为:

  

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

正如其所有者Marco Pivetta在DoctrineBundle存储库的this issue中所建议的那样(他也恰好是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

答案 1 :(得分:1)

正如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)
        }
    }

}