web-dev-qa-db-ja.com

複雑なWHEREクエリオブジェクトで「AND」と「OR」の複数のセットを組み合わせる方法は?

チェックする複雑なWHEREクエリを作成します。

If(条件1 = TRUE)AND(条件2 = TRUE)AND(条件3 = TRUE OR条件4 = TRUE)

したがって、最初の2つの条件は常にtrueでなければならず、次に条件3または4のいずれかがtrueでなければなりません。

私はコードで試しています:

->select ($db->quoteName(array('c.event','b.date','b.type','a.mob','a.joined','a.left')))
->from ($db->quoteName('app_mob_animal', 'a'))
->join('INNER', $db->quoteName('app_event_animal', 'b') . 'ON (' .$db->quoteName('a.mob'). '=' .$db->quoteName('b.mob').')')    
->join('INNER', $db->quoteName('app_events', 'c') . 'ON (' .$db->quoteName('b.event'). '=' .$db->quoteName('c.id').')') 
->join('INNER', $db->quoteName('app_mob', 'd') . 'ON (' .$db->quoteName('a.mob'). '=' .$db->quoteName('d.id').')')
->where (($db->quoteName('a.animal') . 'LIKE' . $db->quote($anid)), 'AND')
->where (($db->quoteName('a.joined') . '<=' . $db->quoteName('b.date')), 'AND') 
->where (($db->quoteName('a.left') . '>=' . $db->quoteName('b.date') .'OR'. ($db->quoteName('a.left') .'LIKE'. $db->quote($emptyDate))));

しかし、自分の応答を吐き出すと、2番目の条件を明確に破る結果が得られます。これは私の結果を除いてです、2番目の結果は基準を満たしていません。

 [0]=>
  object(stdClass)#891 (6) {
    ["event"]=>
    string(7) "weighed"
    ["date"]=>
    string(19) "2015-04-19 00:00:00"
    ["type"]=>
    string(3) "mob"
    ["mob"]=>
    string(2) "11"
    ["joined"]=>
    string(19) "2015-04-18 11:39:14"
    ["left"]=>
    string(19) "0000-00-00 00:00:00"
  }
  [1]=>
  object(stdClass)#888 (6) {
    ["event"]=>
    string(9) "vet visit"
    ["date"]=>
    string(19) "2015-03-31 11:46:08"
    ["type"]=>
    string(3) "mob"
    ["mob"]=>
    string(2) "11"
    ["joined"]=>
    string(19) "2015-04-18 11:39:14"
    ["left"]=>
    string(19) "0000-00-00 00:00:00"
  }

このクエリのWHERE部分の正しい構文は何ですか?

2
Hannah Smith

あなたのOR状態はそれを壊しています。今はそうです。

if condition1 = TRUE AND condition2 = TRUE AND condition3 = TRUE OR condition4 = TRUE

だからあなたは()条件3と4の周り。これにより、condition4trueです。

やってみる

->select ($db->quoteName(array('c.event','b.date','b.type','a.mob','a.joined','a.left')))
->from ($db->quoteName('app_mob_animal', 'a'))
->join('INNER', $db->quoteName('app_event_animal', 'b') . ' ON (' .$db->quoteName('a.mob'). '=' .$db->quoteName('b.mob').')')    
->join('INNER', $db->quoteName('app_events', 'c') . ' ON (' .$db->quoteName('b.event'). '=' .$db->quoteName('c.id').')') 
->join('INNER', $db->quoteName('app_mob', 'd') . ' ON (' .$db->quoteName('a.mob'). '=' .$db->quoteName('d.id').')')
->where ($db->quoteName('a.animal') . ' LIKE ' . $db->quote($anid), 'AND')
->where ($db->quoteName('a.joined') . '<=' . $db->quoteName('b.date'), 'AND') 
->where ('('.$db->quoteName('a.left') . '>=' . $db->quoteName('b.date') .' OR '. $db->quoteName('a.left') .' LIKE '. $db->quote($emptyDate).')');

クエリを印刷して、どのように構築されているかを確認できます。

たとえば、クエリ変数が$query、次に行います

echo $query->dump(); // Thanks to @DmitryRekun

そして、それは実行されるものすべてのクエリを出力します。このようにして、何が問題なのかを確認できます。単純なデバッグ用です。

2
Rene Korss

実用的なソリューションが受け入れられた後で投稿したいという衝動を抑えるつもりでしたが、コードの可読性、意図、簡潔さ、および(目立たない程度に)改善するためにクエリ構築プロセスを変更する方法を説明することに価値があると後で決めましたセキュリティ/機能を維持しながらパフォーマンス。また、個人的なコーディングの好みの問題にすぎないいくつかの提案についても説明します。私は事前に、ルネの答えは安全で完全に機能していると言いたいだけです。

クエリをコード化する方法とその理由を次に示します。

$anid = "animal's string";  // hypothetical
$emptyDate = "2018-06-21";  // hypothetical
$db = JFactory::getDBO();
try {
    $query = $db->getQuery(true)
                ->select(
                    array(
                        "c.event",
                        "b.date",
                        "b.type",
                        "a.mob",
                        "a.joined",
                        $db->qn("a.left")
                    )
                )
                ->from("app_mob_animal a")
                ->innerJoin("app_event_animal b ON a.mob = b.mob")
                ->innerJoin("app_events c ON b.event = c.id")
                ->innerJoin("app_mob d ON a.mob = d.id")
                ->where(
                    array(
                        "a.joined <= b.date",
                        "a.animal = " . $db->q($anid)
                    )
                )
                ->andWhere(
                    array(
                        $db->qn("a.left") . " >= b.date",
                        $db->qn("a.left") . " = " . $db->q($emptyDate)
                    ),
                    "OR"
                );
    echo $query->dump();
    $db->setQuery($query);
    if (!$result = $db->loadAssocList()) {  // declare variable and check for empty array
        echo "<div>No Qualifying Rows</div>";
    } else {
        foreach ($result as $row) {
            // ... do what you like with $row['event'] etc.  (table names/alias are omitted from the resultset keys)
        }
    }
} catch (Exception $e) {
    echo "<div>Syntax error, please contact the developer</div>";
    // echo $e->getMessage();  // <-- not to be displayed publicly
}

これは、目的のクエリロジックを提供するレンダリングされたクエリ(->dump()から)です。

SELECT c.event、b.date、b.type、a.mob、a.joined、 `a`.`left`
FROM app_mob_animal a
INNER JOIN app_event_animal b ON a.mob = b.mob
INNER JOIN app_events c ON b.event = c.id
INNER JOIN app_mob d ON a.mob = d.id
どこ
(a.joined <= b.date AND a.animal = 'animal \' s string ')AND
( `a`.`left`> = b.date OR` a`.`left` = '2018-06-19')

説明:

  • SELECT句の列は配列形式ですが、カンマ区切り値の単一の文字列と同じように簡単に書き込むことができます。 left列名のみ[〜#〜] [〜#〜]をバックティックラップする必要があります-これは、ハードコードされたバックティックまたはqn()呼び出し。
  • この信頼できる参照 を要約するには、次の条件の1つ以上を満たすテーブル名、列名、エイリアス(「データベースエンティティ」と呼びましょう)のみ[〜#〜]必須[〜#〜]バックティックラップする必要があります:
    1. 空白文字が含まれています
    2. このホワイトリスト外の文字が含まれています:ラテン文字、数字、アンダースコア、ドル記号
    3. MySQLReservedKeywords を含みます。
  • 上記の基準を踏まえて、ラテン文字、数字、アンダースコア、ドル記号のみを含むようにテーブルと列の名前を設計し、予約語の使用を避けることをお勧めします。さらに、予約されていない単語も使用しないようにするコーディング意図を明確にします。元の投稿では、leftは予約語です。 eventdate、およびtypeはMySQLキーワードですが、予約されていません。 IMO、メリットのない関数呼び出しを排除することは、良いコーディング習慣です。
  • エンティティをすべて小文字で記述し、MySQLワードをすべて大文字で記述することは、エンティティとMySQL関数/予約語を瞬時に区別する優れた方法です。
  • qn()の省略形としてquoteName()を使用しています。q()の場合はquote()innerJoin()の場合はjoin("INNER",...)です。
  • 1つのクエリで複数のwhere()呼び出しを使用したくないのは、生のクエリ構成からの逸脱が多すぎるためです。ご覧のとおり、新しいinnerJoin()は新しいINNER JOIN行を書き込みますが、繰り返しwhere()を呼び出すと、単一の句が拡張されます。 andWhere() を使用しているのは、 "inner glue"パラメータ(OR)だけでなく、式をかっこで囲み、最初のwhere()式を括弧で囲み、論理グループを分離します。 where()(またはselect())の呼び出しに複数の式を記述したい場合は、array()の条件を指定することをお勧めします。
  • [〜#〜]重要な[〜#〜]元のクエリにはLIKEの2つの使用方法がありますが、それらを実行するために使用されていません部分一致。これが偶然であったのか、列と文字列を比較する方法についての誤解であったのかはわかりませんが、これはベストプラクティスではなく、将来の研究者を混乱させる可能性があります。ロジックが文字列全体を一致させることである場合は、=比較演算子を使用します。ロジックが部分一致を行う場合は、LIKEを使用して、_または%を含む文字列を指定します。 LIKE式で変数を引用する場合は、Joomlaドキュメンテーション 推奨構文 のこのガイドを参照してください。
  • 最後に、テーブル/列とそれぞれのエイリアスの間でASの「構文糖」を好む場合は、必ずそれを使用してください。
1
mickmackusa