带子查询的 Doctrine Update 查询

发布于 2024-11-26 03:44:04 字数 468 浏览 1 评论 0原文

我正在尝试使用 Doctrine dql 执行类似于以下查询的查询:

Doctrine_Query::create()
              ->update('Table a')
              ->set('a.amount',
                    '(SELECT sum(b.amount) FROM Table b WHERE b.client_id = a.id AND b.regular = ? AND b.finished = ?)',
                    array(false, false))
              ->execute();

但它会引发 Doctrine_Query_Exception 并显示消息:“未知组件别名 b”

是关于在“set”子句中使用子查询的限制,您可以吗给我一些帮助吗?

提前致谢。

I'm trying to execute a query, similar to the following one, using doctrine dql:

Doctrine_Query::create()
              ->update('Table a')
              ->set('a.amount',
                    '(SELECT sum(b.amount) FROM Table b WHERE b.client_id = a.id AND b.regular = ? AND b.finished = ?)',
                    array(false, false))
              ->execute();

But it rises a Doctrine_Query_Exception with the message: "Unknown component alias b"

Is restriction about using sub-queries inside the 'set' clause, can you give me some help?

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

晚风撩人 2024-12-03 03:44:04

几年后但可能会有所帮助。

是]

如果您需要/想要/必须,您可以使用Querybuilder来执行具有子选择语句的更新查询,而不是直接使用底层连接层。
这里的想法是使用 QueryBuilder 两次。

  • 构建一个 select 语句来计算新值。
  • 构建实际的更新查询以提交到数据库,您将在其中注入前一个选择 DQL,如您所期望的那样,以便发出单个数据库请求。

示例]

给定一个用户可以出售物品的应用程序。每笔交易都涉及买方和卖方。交易结束后,卖家和买家可以留下评论,了解对方的交易进展情况。
您可能需要一个用户表、一个审核表和一个交易表。
用户表包含一个名为评级的字段,它将保存用户的平均评级。 Review 表存储事务 ID、作者 ID(提交评论的人)、值(从 0 到 5)。最后,交易包含对卖方和买方的参考。

现在假设您想在对方提交评论后更新用户的平均评分。更新查询将计算用户的平均评分,并将结果作为 User. rating 属性的值。
我将以下代码片段与 Doctrine 2.5Symfony3 结合使用。由于这项工作是关于用户的,因此我在 AppBundle\Entity\UserRepository.php 存储库中创建一个名为 updateRating( User $user) 的新公共函数是有意义的。

/**
 * Update the average rating for a user
 * @param User $user The user entity object target
 */
public function updateRating( User $user )
{
    // Compute Subrequest. The reference table being Transaction, we get its repository first.
    $transactionRepo = $this->_em->getRepository('AppBundle:Transaction');
    $tqb = $postRepo->createQueryBuilder('t');
    #1 Computing select
    $select = $tqb->select('SUM(r.value)/count(r.value)')
        // My Review table as no association declared inside annotation (because I do not need it elsewhere)
        // So I need to specify the glue part in order join the two tables
        ->leftJoin('AppBundle:Review','r', Expr\Join::WITH, 'r.post = p.id AND r.author <> :author')
        // In case you have an association declared inside the Transaction entity schema, simply replace the above leftJoin with something like
        // ->leftJoin(t.reviews, 'r')
        // Specify index first (Transaction has been declared as terminated)
        ->where( $tqb->expr()->eq('t.ended', ':ended') )
        // The user can be seller or buyer
        ->andWhere( $tqb->expr()->orX(
            $tqb->expr()->eq('t.seller', ':author'),
            $tqb->expr()->eq('t.buyer', ':author')
        ));
    #2 The actual update query, containing the above sub-request
    $update = $this->createQueryBuilder('u')
        // We want to update a row
        ->update()
        // Setting the new value using the above sub-request
        ->set('u.rating', '('. $select->getQuery()->getDQL() .')')
        // should apply to the user we want
        ->where('u.id = :author')
        // Set parameters for both the main & sub queries
        ->setParameters([ 'ended' => 1, 'author' => $user->getId() ]);
    // Get the update success status
    return $update->getQuery()->getSingleScalarResult();
}

现在从控制器

            // … Update User's rating
            $em->getRepository('AppBundle:User')->updateRating($member);
            // …

Years later but may help.

Yes ]

If you need/want/have to, you can use the Querybuilder to execute an update query having a sub select statement, instead of using directly the underlying connection layer.
The idea here is to use the QueryBuilder twice.

  • Build a select statement to compute the new value.
  • Build the actual update query to submit to the database, in which you will inject the former select DQL, as you expected in order to issue a single database request.

Example ]

Given an application where users can sell objects. Each transaction involves a buyer and a seller. After a transaction ends, sellers and buyers can leave a review on how went the deal with their counter part.
You might need a User table, a Review table and a Transaction table.
The User table contains a field named rating which will hold the average rating for a user. The Review table stores a transaction id, the author id (who submitted the review), a value (from 0 to 5). Finally, the transaction contains a reference for both the seller and the buyer.

Now let's say you would like to update the average rating for a user after a review has been submitted by the counter part. The update query will compute the average rating for a user and put the result as the value of the User.rating property.
I used the following snippet with Doctrine 2.5 and Symfony3. Since the work is about users, I makes sense to create a new public function called updateRating( User $user) inside the AppBundle\Entity\UserRepository.php repository.

/**
 * Update the average rating for a user
 * @param User $user The user entity object target
 */
public function updateRating( User $user )
{
    // Compute Subrequest. The reference table being Transaction, we get its repository first.
    $transactionRepo = $this->_em->getRepository('AppBundle:Transaction');
    $tqb = $postRepo->createQueryBuilder('t');
    #1 Computing select
    $select = $tqb->select('SUM(r.value)/count(r.value)')
        // My Review table as no association declared inside annotation (because I do not need it elsewhere)
        // So I need to specify the glue part in order join the two tables
        ->leftJoin('AppBundle:Review','r', Expr\Join::WITH, 'r.post = p.id AND r.author <> :author')
        // In case you have an association declared inside the Transaction entity schema, simply replace the above leftJoin with something like
        // ->leftJoin(t.reviews, 'r')
        // Specify index first (Transaction has been declared as terminated)
        ->where( $tqb->expr()->eq('t.ended', ':ended') )
        // The user can be seller or buyer
        ->andWhere( $tqb->expr()->orX(
            $tqb->expr()->eq('t.seller', ':author'),
            $tqb->expr()->eq('t.buyer', ':author')
        ));
    #2 The actual update query, containing the above sub-request
    $update = $this->createQueryBuilder('u')
        // We want to update a row
        ->update()
        // Setting the new value using the above sub-request
        ->set('u.rating', '('. $select->getQuery()->getDQL() .')')
        // should apply to the user we want
        ->where('u.id = :author')
        // Set parameters for both the main & sub queries
        ->setParameters([ 'ended' => 1, 'author' => $user->getId() ]);
    // Get the update success status
    return $update->getQuery()->getSingleScalarResult();
}

Now from the controller

            // … Update User's rating
            $em->getRepository('AppBundle:User')->updateRating($member);
            // …
围归者 2024-12-03 03:44:04

我不确定这是否有限制,但我记得不久前曾与此进行过斗争。我最终让它工作起来:

$q = Doctrine_Manager::getInstance()->getCurrentConnection();
$q->execute("UPDATE table a SET a.amount = (SELECT SUM(b.amount) FROM table b WHERE b.client_id = a.id AND b.regular = 0 AND b.finished = 0)");

看看这是否有效。请注意,此查询不会执行自动变量转义,因为它不是 DQL。

I'm not sure if there's a restriction on this but I remember fighting with this sometime ago. I eventually got it working with:

$q = Doctrine_Manager::getInstance()->getCurrentConnection();
$q->execute("UPDATE table a SET a.amount = (SELECT SUM(b.amount) FROM table b WHERE b.client_id = a.id AND b.regular = 0 AND b.finished = 0)");

See if that does the trick. Note that automatic variable escaping doesn't get executed with this query as it's not DQL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文