2016-09-01 15 views
1

SQLAlchemyで少し遅く実行されていたクエリをセットアップして、それを最適化しようとしました。結果は、未知の理由で、暗黙のクロス結合を使用します。これは、大幅に遅くなり、間違った結果をもたらすことになります。私はテーブル名と引数を匿名化しましたが、そうでなければ変更はありませんでした。誰がこれがどこから来ているか知っていますか?SQLAlchemyは何の理由もなくクロス結合を出す

見つけやすくするために:新しいものと古いものとの差異は、新しいものが長いSELECTを持ち、どのジョインよりも前にWHEREの3つのテーブルすべてを記述していることです。

オリジナルコード:flask_sqlalchemy.get_debug_queriesによって記録されたよう

cust_name = u'Bob' 
proj_name = u'job1' 
item_color = u'blue' 
query = (db.session.query(Item.name) 
         .join(Project, Customer) 
         .filter(Customer.name == cust_name, 
           Project.name == proj_name) 
         .distinct(Item.name)) 

# some conditionals determining last filter, resolving to this one: 
query = query.filter(Item.color == item_color) 

result = query.all() 

オリジナル放出されたSQL:

QUERY: SELECT DISTINCT ON (items.name) items.name AS items_name 
FROM items JOIN projects ON projects.id = items._project_id JOIN customers ON customers.id = projects._customer_id 
WHERE customers.name = %(name_1)s AND projects.name = %(name_2)s AND items.color = %(color_1)s 
Parameters: `{'name_2': u'job1', 'state_1': u'blue', 'name_1': u'Bob'} 

新コード:flask_sqlalchemy.get_debug_queriesによって記録されたよう

cust_name = u'Bob' 
proj_name = u'job1' 
item_color = u'blue' 
query = (db.session.query(Item) 
        .options(Load(Item).load_only('name', 'color'), 
           joinedload(Item.project, innerjoin=True).load_only('name'). 
           joinedload(Project.customer, innerjoin=True).load_only('name')) 
        .filter(Customer.name == cust_name, 
           Project.name == proj_name) 
        .distinct(Item.name)) 

# some conditionals determining last filter, resolving to this one: 
query = query.filter(Item.color == item_color) 

result = query.all() 

新放出されるSQL :

QUERY: SELECT DISTINCT ON (items.nygc_id) items.id AS items_id, items.name AS items_name, items.color AS items_color, items._project_id AS items__project_id, customers_1.id AS customers_1_id, customers_1.name AS customers_1_name, projects_1.id AS projects_1_id, projects_1.name AS projects_1_name 
FROM customers, projects, items JOIN projects AS projects_1 ON projects_1.id = items._project_id JOIN customers AS customers_1 ON customers_1.id = projects_1._customer_id 
WHERE customers.name = %(name_1)s AND projects.name = %(name_2)s AND items.color = %(color_1)s 
Parameters: `{'state_1': u'blue', 'name_2': u'job1', 'name_1': u'Bob'} 

重要な場合は、基礎となるデータベースがPostgreSQLです。

クエリの元の目的はItem.nameである必要があります。最適化の試みは、私が考えているよりも実際には役に立たないように見えますが、joinedloadload_onlyなどを追加すると実際に役立つ場合に備えて、そのクロス結合がどこから来たのかを知りたいと思っています。

答えて

2

joinedloadjoinと異なるためです。 joinedload edエンティティは実質的に匿名であり、適用した後のフィルタは同じテーブルの異なるインスタンスを参照するため、customersprojectsが2回結合されます。

これまで通りjoinを実行しますが、contains_eagerを使用して、joinedloadのように見えるようにします。

query = (session.query(Item) 
       .join(Item.project) 
       .join(Project.customer) 
       .options(Load(Item).load_only('name', 'color'), 
         Load(Item).contains_eager("project").load_only('name'), 
         Load(Item).contains_eager("project").contains_eager("customer").load_only('name')) 
       .filter(Customer.name == cust_name, 
         Project.name == proj_name) 
       .distinct(Item.name)) 

あなたも `` .distinct(Item.name)を選択しているので、私は推測する、私が間違っているが、これはあなたのクエリ

SELECT DISTINCT ON (items.name) customers.id AS customers_id, customers.name AS customers_name, projects.id AS projects_id, projects.name AS projects_name, items.id AS items_id, items.name AS items_name, items.color AS items_color 
FROM items JOIN projects ON projects.id = items._project_id JOIN customers ON customers.id = projects._customer_id 
WHERE customers.name = %(name_1)s AND projects.name = %(name_2)s AND items.color = %(color_1)s 
+0

それはうまくいきました。たとえ速度が意味的に違っていても、何が起こっているかを知ることは良いことです。 –

1

あなたが達成しようとしていることはわかりませんが、テーブル間で内部結合を行い、特定の列のみを選択しようとしているようです。私はそれがあなたのクエリに重大な最適化をもたらすとは思わない

cust_name = u'Bob' 
proj_name = u'job1' 
item_color = u'blue' 
query = (db.session.query(Item.name) 
         .join(Project, Customer) 
         .filter(Customer.name == cust_name, 
           Project.name == proj_name) 
         .distinct(Item.name)) 

# Select the loaded columns 
query = query.add_columns(Item.name, Item.color, Project.name, Customer.name) 

# some conditionals determining last filter, resolving to this one: 
query = query.filter(Item.color == item_color) 

result = query.all() 

FWIW:

は、だから私はあなたのような何かをする必要があると思います。

+1

を与えます。あなたが最適化したいものとその方法を説明することができれば助けになるでしょうか? :) –

+1

私が欲しい唯一の列は 'Item.name'です。 'load_only'はおそらく余分なものがロードされないようにすることでクエリのスピードを上げる試みでした。それをさらに見ると、実際には最適化されているかどうかは不明ですが、最適化が役立つどこかに来た場合に、このクロス結合がどこから来たのかを知りたいです。それに応じて質問を編集する。 –

関連する問題