Запрос Doctrine Update с подзапросом

Я пытаюсь выполнить запрос, похожий на следующий, используя доктрину 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», вы можете мне помочь?

Заранее спасибо.


person fmarinheiro    schedule 21.07.2011    source источник
comment
Вы уверены, что хотите обновить все строки из таблицы? Этот тип запроса на обновление может привести к другим данным в столбце amount в зависимости от порядка, используемого для обновления. (Я предполагаю, что a и b являются псевдонимами одной и той же таблицы. Это правильно или это две разные таблицы?)   -  person ypercubeᵀᴹ    schedule 22.07.2011
comment
Привет @ypercube, a и b не являются псевдонимами для разных таблиц. Таблица a предназначена для клиентов, а таблица b — для ее учетных записей.   -  person fmarinheiro    schedule 25.07.2011


Ответы (2)


Я не уверен, есть ли на это ограничение, но я помню, как когда-то боролся с этим. В конце концов я получил работу с:

$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.

person Tom    schedule 21.07.2011
comment
Да, это помогло. Я нашел это решение через несколько минут после публикации вопроса. Во всяком случае, это решение для меня, спасибо - person fmarinheiro; 25.07.2011

Годы спустя, но может помочь.

Да ]

Если вам нужно/хотите/должны, вы можете использовать Конструктор запросов для выполнения запроса на обновление с оператором подвыборки вместо непосредственного использования нижележащего уровня соединения.
Идея состоит в том, чтобы использовать QueryBuilder дважды.

  • Создайте оператор select для вычисления нового значения.
  • Создайте фактический запрос на обновление для отправки в базу данных, в котором вы будете вводить прежний select DQL, как вы ожидали, чтобы выдать один запрос к базе данных.

Пример ]

Дано приложение, где пользователи могут продавать объекты. В каждой сделке участвуют покупатель и продавец. После завершения транзакции продавцы и покупатели могут оставить отзыв о том, как прошла сделка с их контрагентом.
Вам может понадобиться таблица User, таблица Review и таблица Транзакции.
Таблица пользователей содержит поле с именем рейтинг, в котором будет храниться средний рейтинг пользователя. В таблице Review хранится идентификатор транзакции, идентификатор автора (отправившего отзыв), значение (от 0 до 5). Наконец, транзакция содержит ссылку как для продавца, так и для покупателя.

Теперь предположим, что вы хотите обновить средний рейтинг пользователя после того, как ответная часть оставила отзыв. Запрос на обновление будет вычислять средний рейтинг пользователя и помещать результат в значение свойства User.rating.
Я использовал следующий фрагмент с Doctrine 2.5 и Symfony3. . Поскольку работа связана с пользователями, имеет смысл создать новую общедоступную функцию с именем updateRating( User $user) внутри репозитория AppBundle\Entity\UserRepository.php.

/**
 * 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);
            // …
person Stphane    schedule 29.11.2016