2016-10-18 6 views
1

Propel 2でかなり簡単なクエリを実行しようとしています。私はPersonテーブルとPossessionテーブルを持っています。だから1人の本、1台の車を持っている人がいます。私はPropelに車を持っている人の車名とともにすべての人を返すような質問を書こうとしています。コードと結果のクエリは次のとおりです。PropelはJOINテーブルへのエイリアスを使用するときにクエリにCROSS JOINを追加します

$x = PersonQuery::create() 
    ->groupById() 
    ->leftJoinPossession() 
    ->addJoinCondition('Possession','Possession.possession_type = ?', 'car') 
    ->withColumn('Possession.possession_name', 'CarName') 
    ->where('Possession.possession_name IS NOT NULL') 
    ->find() 
    ->toString(); 

//resulting sql 
SELECT person.id, possession.possession_name as CarName 
FROM person 
LEFT JOIN possession ON (person.id=possession.person_id AND possession.possession_type = 'car') 
WHERE possession.possession_name IS NOT NULL; 

これは期待どおりに動作します。しかし、私は別名を使用する必要があるので、所有テーブルに複数の結合を行う必要があります(たとえば各人の本を入手する必要があります)。ここで私はちょうど所持テーブルの別名を使用する(とも一人一人のための本を取得するために)以前のクエリを変更するときに何が起こるかです:Propelが追加何らかの理由

$x = PersonQuery::create() 
    ->groupById() 
    ->leftJoinPossession('p') 
    ->addJoinCondition('p','p.possession_type = ?', 'car') 
    ->leftJoinPossession('p2') 
    ->addJoinCondition('p2','p2.possession_type = ?', 'book') 
    ->withColumn('p.possession_name', 'CarName') 
    ->withColumn('p2.possession_name', 'BookName') 
    ->where('p.possession_name IS NOT NULL') 
    ->find() 
    ->toString(); 

//resulting sql 
SELECT person.id, p.possession_name as CarName, p2.possession_name as BookName 
FROM person 
CROSS JOIN possession 
LEFT JOIN possession p ON (person.id=p.person_id AND p.possession_type = 'car') 
LEFT JOIN possession p2 ON (person.id=p2.person_id AND p2.possession_type = 'book') 
WHERE p.possession_name IS NOT NULL; 

あなたが見ることができるように、「CROSS JOINは「所有」をクエリに追加します。これはクエリの結果を変更するものではありませんが、非常に遅くなります。私が参加したテーブルのエイリアスを使用している間にPropelがCROSS JOINを使用しないように指示する方法についてのアイデアはありますか? ( 'where'句を取り除くと、CROSS JOINも消えます)

答えて

1

この問題の回避策が見つかりました。問題は、エイリアスを使用して同じテーブルに複数の左ジョインを実行しようとしたときに、Propelはそのテーブルへのクロスジョインも含めるということでした。しかし、最初の結合でエイリアスが使用されなかった場合、Propelはクロス結合を追加しないことに気付きました。

ので、合計する、これは働いていませんでした。

$x = PersonQuery::create() 
    ->groupById() 
    ->leftJoinPossession('p') 
    ->addJoinCondition('p','p.possession_type = ?', 'car') 
    ->leftJoinPossession('p2') 
    ->addJoinCondition('p2','p2.possession_type = ?', 'book') 
    ->withColumn('p.possession_name', 'CarName') 
    ->withColumn('p2.possession_name', 'BookName') 
    ->where('p.possession_name IS NOT NULL') 
    ->find() 
    ->toString(); 

//resulting sql 
SELECT person.id, p.possession_name as CarName, p2.possession_name as BookName 
FROM person 
CROSS JOIN possession 
LEFT JOIN possession p ON (person.id=p.person_id AND p.possession_type = 'car') 
LEFT JOIN possession p2 ON (person.id=p2.person_id AND p2.possession_type = 'book') 
WHERE p.possession_name IS NOT NULL; 

しかし、予想通り、これは働いていた:

$x = PersonQuery::create() 
    ->groupById() 
    ->leftJoinPossession() //changed 
    ->addJoinCondition('Possession','Possession.possession_type = ?', 'car') //changed 
    ->leftJoinPossession('p2') 
    ->addJoinCondition('p2','p2.possession_type = ?', 'book') 
    ->withColumn('p.possession_name', 'CarName') 
    ->withColumn('p2.possession_name', 'BookName') 
    ->where('p.possession_name IS NOT NULL') 
    ->find() 
    ->toString(); 

//resulting sql 
SELECT person.id, p.possession_name as CarName, p2.possession_name as BookName 
FROM person 
LEFT JOIN possession ON (person.id=posession.person_id AND posession.possession_type = 'car') 
LEFT JOIN possession p2 ON (person.id=p2.person_id AND p2.possession_type = 'book') 
WHERE p.possession_name IS NOT NULL; 
1

なぜあなたは加入していますか?私があなたの記事を正しく理解していれば、実際に車を持っている人のための結果だけが必要です。だから、次は動作するはずです:

PersonQuery::create() 
    ->withColumn('Possession.possession_name', 'CarName') 
    ->usePossessionQuery() 
    ->filterByPossessionType('car') 
    ->endUse() 
    ->find() 
    ->toString(); 

useXxxQuery()もより簡単にあなたが同じことを達成することができるはずですので、別名を与えますが、することができます:)

EDIT:複数持つ例加わり、 2番目の例のとおり:

PersonQuery::create() 
    ->withColumn('possession_car.possession_name', 'CarName') 
    ->withColumn('possession_book.possession_name', 'BookName') 
    ->usePossessionQuery('possession_car') 
    ->filterByPossessionType('car') 
    ->endUse() 
    ->usePossessionQuery('possession_book') 
    ->filterByPossessionType('book') 
    ->endUse() 
    ->find() 
    ->toString(); 
+0

は、私は複数所持テーブルにジョインを使用できるようにする必要があります。

一方、ここにあなたがこれらの条件をチェックし、必要な例外をスローしてみましょうヘルパーコードと要旨です。元の投稿の2番目のコードブロックを編集して、私が少し明確にする必要があることを示しました。私がしようとしていることのためにあなたの方法でクエリを構築するとは思えません。 – zeke

+1

私はそれがなぜないかわかりません。前述したように、usePossessionQueryのエイリアスを指定することができます(これは最初のパラメータです)。複数の結合の例を使って投稿を更新します。 – chocochaos

0

は、これは間違いなくバグのように見えます。あなたはそれを報告しましたか?

一般的に、PropelはどこのCriteriasが指しているリレーション/エイリアスを確認し、これらのすべてのテーブルがクエリに含まれていることを確認します。そのリレーション/エイリアスに既存のジョイン句がない場合、それにはCROSS JOINが含まれます。

->where()メソッドには、(filterByX hasのような)メソッドパラメータを介して提供される関係エイリアスはなく、propelは 'p' where()条件の一部が所有テーブルにマップされているので、関連エイリアス(これはバグ)を使ってその結論に至ったという事実を拾わないので、propelはwhere()がチェックしたいと考えるpossessionテーブル(エイリアスなし)と比較して、クロスジョインが含まれています。

あなたの問題を回避するには、唯一のため、このバグのために動作し、書くことが明確になります:

->where('possession.possession_name IS NOT NULL') 

...ここで、()は、特定の関係のエイリアスではなく、エイリアシングされていない所持テーブルを参照していることを明確にする。

私が正しいだ場合は、第二の別名参照すれば、あなたの回避策が機能しないでください。

->where('p2.possession_name IS NOT NULL') 

を(それはまだ最初の関係を参照します)PS私見、Propelの代わりに、例外をスローする必要

多くの場合、クロスジョインは、クエリメソッドのタイプミスまたは誤った使用の望ましくない結果であるため、このオートマティックのクロスジョイン - クエリの「固定」が行われます。 https://gist.github.com/motin/2b00295ca71bb876f9873712544dd077

関連する問題