我正在使用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子句吗?
答案 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
答案 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)
}
}
}