简体   繁体   中英

Doctrine Update query with a sub-query

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.

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);
            // …

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM