Symfony Doctrine Query для вычисления ранга после группы по подзапросу

Я создаю приложение в Symfony 1.4 и Doctrine 1.2 ORM. Я новичок в доктрине ORM и осваиваю ее, но не могу решить эту проблему.

У меня есть таблица оценок пользователей (mbScoreByGenre), где один идентификатор пользователя может иметь несколько записей оценок пользователей для одного parent_genre. то есть - многие ко многим

Моя цель — найти рейтинг конкретного пользователя на основе его совокупных баллов для заданных parent_genre_id и user_id. В моем алгоритме ранжирования используется подзапрос, и у меня было много проблем с созданием работающего запроса доктрины.

Вот схема моей доктрины для mbScoreByGenre

mbScoreByGenre:
  actAs:
    Timestampable: ~    
  columns:

    id:                 { type: integer, primary: true, autoincrement: true }
    user_id:            { type: integer, notnull: true }
    genre_id:           { type: integer, notnull: true } 
    parent_genre_id:    { type: integer, notnull: true } 
    score:              { type: float, notnull: true, default: 0  } 

A. Сначала я попытался сделать что-то вроде этого:

$q = Doctrine_Query::create()
    ->select('((SELECT COUNT(1) AS num 
        FROM 
        (SELECT SUM(mbScoreByGenre.score) 
        WHERE SUM(mbScoreByGenre.score) > SUM(s.score)
        AND mbScoreByGenre.parent_genre_id = '.$genre['parent_id'].'
        AND s.parent_genre_id = '.$genre['parent_id'].'
        GROUP BY mbScoreByGenre.user_id
        ) + 1)  AS rank')
    ->from('mbScoreByGenre s')
    ->where('s.user_id = ?', array($user_id))
    ->groupBy('s.user_id')
    ->orderBy('rank');

но я получил следующую ошибку. Фатальная ошибка: достигнут максимальный уровень вложенности функции «100», прерывание! в \lib\vendor\symfony-1.4.14\lib\plugins\sfDoctrinePlugin\lib\vendor\doctrine\Doctrine\Query\Tokenizer.php в строке 303. Я не понимаю, как построить подзапрос, чтобы он работал.

B. Тогда я изменился и попробовал другой подход

$q = new Doctrine_RawSql();
$q  ->addComponent('s', 'mbScoreByGenre')
    ->select('COUNT({*}) AS {rank}')
    ->from('(SELECT SUM(s.score) AS total_score
        FROM mb_score_by_genre s
        WHERE s.parent_genre_id = '.$genre['parent_id'].'
        GROUP BY s.user_id)
            ')
    ->where('total_score >= (
        SELECT SUM(s.score) 
        FROM mb_score_by_genre s
        WHERE s.parent_genre_id = '.$genre['parent_id'].'
        AND s.user_id = '.$user_id.'
        GROUP BY s.user_id
    )');

Но я получил эту ошибку: все выбранные поля в запросе Sql должны быть в формате tableAlias.fieldName. Причина, по которой я использовал Doctrine_RawSql, заключается в том, что я прочитал, что доктрина 1.2 не поддерживает подзапросы в From. Для этого подхода я не мог понять, как ссылаться на столбец «total_score» в формате tableAlias.fieldName. Должен ли я добавлять пустой компонент, который ссылается на таблицу подзапросов, возвращаемую для «total_score»?

C. Наконец, я попытался просто запустить подзапрос как запрос доктрины и вычислить ранг, подсчитывая строки объекта доктрины, возвращаемые запросом.

$q = Doctrine_Query::create()
    ->select('SUM(s.score)')
    ->from('mbScoreByGenre s')
    ->where('s.parent_genre_id = ?', $genre['parent_id'])
    ->andWhere('SUM(s.score) > (
        SELECT SUM(p.score) 
        FROM mbScoreByGenre p
        WHERE p.parent_genre_id = '.$genre['parent_id'].'
        AND p.user_id = '.$user_id.'
        GROUP BY p.user_id
    )')
    ->groupBy('s.user_id'); 

    $result = $q->execute();

Но это дает мне ошибку:

SQLSTATE[HY000]: Общая ошибка: 1111 Недопустимое использование групповой функции. Это потому, что groupBy('s.user_id') и GROUP BY p.user_id, p и s относятся к одной и той же модели?

Я провел тонну поиска ответов в Интернете, но не могу найти ответы ни для одного из трех подходов.

Любая помощь будет здорово. Ценить это.


person frankp221    schedule 28.03.2012    source источник
comment
51 просмотр и ни одного ответа? Могу ли я что-то сделать, чтобы сделать вопрос более ясным? Любая помощь приветствуется.   -  person frankp221    schedule 04.04.2012


Ответы (2)


Может быть, я не совсем понял, что вам действительно нужно, но вы пробовали предложение HAVING? Предложение WHERE не поддерживает агрегатные функции, такие как SUM(). Я попробовал этот код, и он сработал и вернул некоторые значения, но я не могу точно сказать, что это то, что вам нужно:

$q = Doctrine_Query::create()
  ->select('count(*)')
  ->from('mbScoreByGenre s')
  ->where('s.parent_genre_id = ?', $genre['parent_id'])
  ->having("SUM(s.score) > (
    SELECT SUM(p.score) 
    FROM mbScoreByGenre p 
    WHERE p.parent_genre_id = {$genre['parent_id']}
      AND p.user_id = {$user_id})")
->groupBy('s.user_id');

$result = $q->execute(array(), Doctrine::HYDRATE_SCALAR);
var_dump($result);

Если это не то, что вам нужно - постарайтесь объяснить точнее.

person starl1ng    schedule 04.04.2012
comment
Большое спасибо, starl1ng, проблема заключалась в том, что WHERE не поддерживает агрегатные функции. Мне также пришлось поставить groupBy перед предложением where. Результирующая таблица дает мне список всех записей, которые опережают текущий user_id. Чтобы получить рейтинг, я просто считаю строки и добавляю 1. Еще раз спасибо. - person frankp221; 18.04.2012

Вы не должны вкладывать подзапросы в условия (или в вашем случае), используя необработанный sql. Вместо этого используйте createSubquery(), чтобы явно сообщить доктрине о подзапросе. Это также поможет вам в более сложных сценариях, когда доктрина больше не может обрабатывать глубоко вложенные необработанные запросы sql. Таким образом, ваш запрос должен выглядеть примерно так:

$q = Doctrine_Query::create()
    ->select('count(*)')
    ->from('mbScoreByGenre s')
    ->where('s.parent_genre_id = ?', $genre['parent_id'])
    ->groupBy('s.user_id')
;

$subquery = $q->createSubquery()
     ->select("SUM(p.score)")
     ->from("FROM mbScoreByGenre p")
     ->where("p.parent_genre_id = ?", $genre['parent_id'])
     ->andWhere("p.user_id = ?", $user_id)
;

$q->having("SUM(s.score) > (".$subquery->getDql().")");

Другой пример можно найти здесь:

http://www.philipphoffmann.de/2012/08/taming-doctrine-subqueries/

person philipphoffmann    schedule 01.09.2012