2014-07-02 5 views
6

Ich versuche, die folgende Abfrage für die Verarbeitung verschachtelter Sätze (siehe here) in SQLAlchemy zu implementieren. Worum ich mich ärgere, ist, wie man die depth Berechnung in der Haupt SELECT Abfrage verwendet (die von der Sub SELECT Abfrage abhängt) innerhalb der HAVING Klausel am Ende.Verwenden von Beschriftungen in der HAVING() - Klausel in SQLAlchemy

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth 
FROM nested_category AS node, 
    nested_category AS parent, 
    nested_category AS sub_parent, 
    (
      SELECT node.name, (COUNT(parent.name) - 1) AS depth 
      FROM nested_category AS node, 
        nested_category AS parent 
      WHERE node.lft BETWEEN parent.lft AND parent.rgt 
        AND node.name = 'PORTABLE ELECTRONICS' 
      GROUP BY node.name 
      ORDER BY node.lft 
    )AS sub_tree 
WHERE node.lft BETWEEN parent.lft AND parent.rgt 
    AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt 
    AND sub_parent.name = sub_tree.name 
GROUP BY node.name 
HAVING depth <= 1 
ORDER BY node.lft; 

Ich fühle mich wie ich bin sehr nahe bei der Verwendung von:

node = aliased(Category)                    
parent = aliased(Category)                   
sub_parent = aliased(Category) 

sub_tree = DBSession.query(node.name,                 
    (func.count(parent.name) - 1).label('depth')).\             
    filter(node.lft.between(parent.lft, parent.rgt)).\            
    filter(node.name == category_name).\                
    group_by(node.name).\                    
    order_by(node.lft).subquery()                  

children = DBSession.query(node.name,                 
    (func.count(parent.name) - (sub_tree.c.depth + 1)).label('depth')).\        
    filter(node.lft.between(parent.lft, parent.rgt)).\ 
    filter(node.lft.between(sub_parent.lft, sub_parent.rgt)).\          
    filter(sub_parent.name == sub_tree.c.name).\              
    group_by(node.name).having(depth <= 1).\              
    order_by(node.lft).all() 

Aber ich am Ende der Fehler bekommen:

NameError: global name 'depth' is not defined 

Welche Art Sinn macht. Wenn ich having(depth <= 1) mit having(func.count('depth') <= 1 ersetzen, habe ich am Ende mit der folgenden HAVING Klausel oben erzeugt, die keine Ergebnisse (Wo% s Platzhalter ist (‚Tiefer‘, 1)):

HAVING count(%s) <= %s 

Was ich brauche es wirklich genau zu lesen wie ist das:

HAVING depth = 1 

Hat jemand irgendwelche Ideen?

Mein letzter Ausweg ist, um tatsächlich die rohe Abfrage durchführt, anstatt durch die ORM-Schicht zu gehen, aber ich würde wirklich lieber nicht, da ich so nahe bin ...

Vielen Dank im Voraus.

Edit:

Ich habe auch versucht, den folgenden Code, aber es gibt nicht die richtigen Ergebnisse (als ob die 'Tiefe' Label 0 immer):

node = aliased(Category)                    
parent = aliased(Category)                   
sub_parent = aliased(Category) 

sub_tree_depth = (func.count(parent.name) - 1).label('depth') 
depth = (func.count(parent.name) - (sub_tree_depth + 1)).label('depth') 

sub_tree = DBSession.query(node.name, 
    sub_tree_depth).\ 
    filter(node.lft.between(parent.lft, parent.rgt)).\ 
    filter(node.name == category_name).\ 
    group_by(node.name).\ 
    order_by(node.lft).subquery() 

children = DBSession.query(node.name, 
    depth).\ 
    filter(node.lft.between(parent.lft, parent.rgt)).\ 
    filter(node.lft.between(sub_parent.lft, sub_parent.rgt)).\ 
    filter(sub_parent.name == sub_tree.c.name).\ 
    group_by(node.name).having(depth <= 1).\ 
    order_by(node.lft).all() 

Die davon generierte HAVING-Klausel sieht so aus (categories_2 == parent in der ursprünglichen Abfrage):

Edit:

ich es dachte, könnte hilfreich sein, den erzeugten SQL aufzunehmen.

SQLAlchemy

node = aliased(Category) 
parent = aliased(Category) 
sub_parent = aliased(Category) 

sub_tree = DBSession.query(node.name, 
    (func.count(parent.name) - 1).label('depth')).\ 
    filter(node.lft.between(parent.lft, parent.rgt)).\ 
    filter(node.name == category_name).\ 
    group_by(node.name).\ 
    order_by(node.lft).subquery() 

depth = (func.count(parent.name) - (sub_tree.c.depth + 1)).label('depth') 
children = DBSession.query(node.name, depth).\ 
    filter(node.lft.between(parent.lft, parent.rgt)).\ 
    filter(node.lft.between(sub_parent.lft, sub_parent.rgt)).\ 
    filter(sub_parent.name == sub_tree.c.name).\ 
    group_by(node.name).having(depth <= 1).\ 
    order_by(node.lft) 

generiert SQL

'SELECT categories_1.name AS categories_1_name, count(categories_2.name) - (anon_1.depth + %s) AS depth 
FROM categories AS categories_1, categories AS categories_2, (SELECT categories_1.name AS name, count(categories_2.name) - %s AS depth 
FROM categories AS categories_1, categories AS categories_2 
WHERE categories_1.lft BETWEEN categories_2.lft AND categories_2.rgt AND categories_1.name = %s GROUP BY categories_1.name ORDER BY categories_1.lft) AS anon_1, categories AS categories_3 
WHERE categories_1.lft BETWEEN categories_2.lft AND categories_2.rgt AND categories_1.lft BETWEEN categories_3.lft AND categories_3.rgt AND categories_3.name = anon_1.name GROUP BY categories_1.name 
HAVING count(categories_2.name) - (anon_1.depth + %s) <= %s ORDER BY categories_1.lft' (1, 1, u'Institutional', 1, 1) 
+0

Haben Sie versucht, die gesamte Tiefe als Filter in 'HAVING' Klausel wie' HAVING (COUNT (parent.name) - 1) = 1' – Charlesliam

+0

Danke für die Antwort. Ich habe versucht, 'HAVING (COUNT (parent.name) - 1) = 1 'zu verwenden, die nicht die korrekten Ergebnisse zurückgegeben hat. Ich versuchte dann 'HAVING (COUNT (parent.name) - (sub_tree.depth + 1)) <= 1', was zu einem SQL-Fehler von 'Unbekannte Spalte' sub_tree.depth '.... – dhildreth

Antwort

5

Ihre SQL-Abfrage verwendet implizit beitritt, in SQLAlchemy müssen Sie sie explizit definieren.Ansonsten ist Ihr zweiter Versuch fast richtig ist:

node = aliased(Category) 
parent = aliased(Category) 
sub_parent = aliased(Category) 

sub_tree = DBSession.query(node.name, 
    (func.count(parent.name) - 1).label('depth')).\ 
    join(parent, node.lft.between(parent.lft, parent.rgt)).\ 
    filter(node.name == category_name).\ 
    group_by(node.name).\ 
    order_by(node.lft).subquery() 

depth = (func.count(parent.name) - (sub_tree.c.depth + 1)).label('depth') 
children = DBSession.query(node.name, depth).\ 
    join(parent, node.lft.between(parent.lft, parent.rgt)).\ 
    join(sub_parent, node.lft.between(sub_parent.lft, sub_parent.rgt)).\ 
    join(sub_tree, sub_parent.name == sub_tree.c.name).\ 
    group_by(node.name, sub_tree.c.depth).\ 
    having(depth <= 1).\ 
    order_by(node.lft).all() 

nicht überrascht sein, wenn Sie HAVING Klausel in den generierten SQL vollen Ausdruck anstelle seines Alias ​​wiederholt haben. Das liegt daran, dass Aliase dort nicht erlaubt sind, es ist nur die MySQL-Erweiterung, während SQLAlchemy versucht, SQL zu generieren, das in den meisten Fällen funktioniert.

+0

Ich schätze es wirklich . Allerdings bekomme ich die Fehlermeldung 'Unbekannte Spalte' anon_1.depth 'in' habende Klausel'. Ich kann nicht das gesamte generierte SQL posten, aber hier ist, was ich denke, das wichtige Stück ist: 'HAVING count (categories_2.name) - (anon_1.depth + 1) <= 1' – dhildreth

+0

Falls es nicht offensichtlich war, SQLAlchemy weist 'sub_tree' zu' anon_1' und 'categories_2' zu' parent' zu. – dhildreth

+0

Ich habe meine Antwort aktualisiert, zuerst habe ich die fehlenden Verknüpfungen nicht bemerkt ... Hoffentlich wird es jetzt funktionieren. –

Verwandte Themen