2012-03-28 6 views
5

Buduję aplikację w Symfony 1.4 i Doctrine 1.2 ORM. Jestem całkiem nowy w doktrynie ORM i rozumiem, ale nie mogę rozwiązać tego problemu.Kwerenda do Symfony Doctrine do obliczania pozycji po grupie według podzapytania

Mam tabelę wyników użytkowników (mbScoreByGenre), gdzie jeden identyfikator użytkownika może mieć wiele rekordów wyników użytkownika dla jednego parent_genre. ie - wiele dla wielu

Moim celem jest znalezienie rangi konkretnego użytkownika w oparciu o jego skumulowane wyniki dla danego parent_genre_id i user_id. Mój algorytm rankingu używa podkwerendy i miałem wiele problemów z budowaniem kwerendy doktryny, która działa.

Oto mój schemat doktryna dla 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. Najpierw próbowałem zrobić coś takiego:

$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'); 

but I got the following error Fatal error: Maximum function nesting level of '100' reached, aborting! in \lib\vendor\symfony-1.4.14\lib\plugins\sfDoctrinePlugin\lib\vendor\doctrine\Doctrine\Query\Tokenizer.php on line 303. I don't understand how to build the subquery so that it works.

B. Więc zmieniłem i spróbował innego podejścia

$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 
    )'); 

Ale mam ten błąd: Wszystkie wybrane pola w kwerendzie Sql muszą być w formacie tableAlias.fieldName. Powodem, dla którego użyłem Doctrine_RawSql jest przeczytanie, że doktryna 1.2 nie obsługuje podzapytań w Od. Dla tego podejścia nie mogłem dowiedzieć się, jak odwołać się do kolumny "total_score" w formacie tableAlias.fieldName. Czy muszę dodać pusty składnik odwołujący się do tabeli podzapytania zwróconej dla "total_score"?

C. Na koniec spróbowałem uruchomić podkwerendę jako zapytanie do doktryny i obliczyć pozycję, licząc wiersze obiektów doktryny zwrócone przez zapytanie.

$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(); 

Ale to daje mi błąd:

SQLSTATE[HY000]: General error: 1111 Invalid use of group function. Is it because groupBy('s.user_id') and GROUP BY p.user_id, both p and s refer to the same model?

Zrobiłem mnóstwo przetrząsać internet w poszukiwaniu odpowiedzi, ale nie wydaje się znaleźć odpowiedzi na każdy z 3 podejść.

Każda pomoc będzie świetna. Doceniam to.

+0

51 poglądami i nie odpowiedzi? Czy jest coś, co mogę zrobić, aby wyjaśnić to pytanie? Każda pomoc byłaby bardzo cenna. – frankp221

Odpowiedz

2

Może, nie zrozumiałem w pełni tego, czego naprawdę potrzebujesz, ale czy wypróbowałeś klauzulę HAVING? Klauzula WHERE nie obsługuje funkcji zagregowanych, takich jak SUMA(). Próbowałem ten kod i to działało i wrócił pewne wartości, ale nie mogę powiedzieć na pewno, czy to jest to, czego potrzebujesz:

$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); 

Jeśli tak nie jest, co trzeba - próbować wyjaśnić dokładniej.

+0

Wielkie dzięki starl1ng, problem polegał na tym, że WHERE nie obsługuje funkcji zagregowanych. Musiałem również umieścić groupBy przed klauzulą ​​where. Wynikowa tabela zawiera listę wszystkich rekordów, które wyprzedzają bieżący identyfikator użytkownika. Aby uzyskać ranking, po prostu liczę wiersze i dodam 1. Jeszcze raz dziękuję. – frankp221

0

Nie należy zagnieżdżać zapytań pod względem warunków (lub w twoim przypadku) przy użyciu surowego sql. Zamiast tego użyj createSubquery(), aby jawnie powiedzieć doktrynie o podzapytaniu. Pomoże to również w bardziej złożonych sytuacjach, w których doktryna po prostu nie może już obsługiwać głęboko zagnieżdżonych zapytań SQL.Więc zapytanie powinno wyglądać mniej więcej tak:

$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().")"); 

Innym przykładem może być tu:

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

Powiązane problemy