web-dev-qa-db-ja.com

複雑なWHEREクエリで「AND」と「OR」を組み合わせる-パート2

私は過去2日間、このクエリで髪の毛を抜いてきており、他の2つの同様の投稿を運が悪かったので読んだり、もう一度読んだりしています。

シナリオ:ユーザーから2つの入力を収集しています。テストの送信時に使用したテストIDコードと電子メールアドレスです。テストコードは数字であり、最初に文字を含めることができますが、テスト機能はこの数字を省略する場合があるため、クエリの変数に文字を追加します(「newtidcode」の理由は以下に表示されます)。また、提出時に最大4つのメールアドレスをテスターに​​提供することもできます(4つのメールの可能性をテストする理由)。

これが私のクエリです:

    $query->select($db->quoteName(array('tidcode', 'cdcode','email1', 'email2', 'email3', 'email4', 'status')))
        ->from($db->quoteName('#__tid_codes'))
        ->where($db->quoteName('tidcode').' = '.$db->quote($tidcode), 'OR')
        ->where($db->quoteName('tidcode').' = '.$db->quote($newtidcode), 'AND')
        ->where('('.$db->quoteName('email1').' = '.$db->quote($email).' OR '.$db->quoteName('email2').' = '.$db->quote($email).' OR '.$db->quoteName('email3').' = '.$db->quote($email).' OR '.$db->quoteName('email4').' = '.$db->quote($email).')');

ここで作成されたSQLステートメント:

SELECT `tidcode`,`cdcode`,`email1`,`email2`,`email3`,`email4`,`status` FROM `#__tid_codes` WHERE `tidcode` = '10348' OR `tidcode` = 'F10348' OR (`email1` = '[email protected]' OR `email2` = '[email protected]' OR `email3` = '[email protected]' OR `email4` = '[email protected]')

ご覧のとおり、ORは、Eメールが2番目の「where」ステートメントで指定されたANDである必要があります。前に、以下のような条件配列を使用しようとしても、同じことに遭遇しました。

$conditions1 = array(
    $db->quoteName('tidcode')." = ".$db->quote($tidcode),
    $db->quoteName('tidcode')." = ".$db->quote($newtidcode)
);
$conditions2 = array (
    $db->quoteName('email1')." = ".$db->quote($email),
    $db->quoteName('email2')." = ".$db->quote($email),
    $db->quoteName('email3')." = ".$db->quote($email),
    $db->quoteName('email4')." = ".$db->quote($email)
);

これは、この複雑さに対するJDatabaseクエリの私の最初の進出です。このアプリケーションは標準のHTMLとPHPで正常に動作していますが、Joomlaに移植しようとしています。どんな助けでも大歓迎です。

6
TJM

whereの2番目のパラメーターは$glueと呼ばれ、期待どおりに機能しません。

これはwhere関数のソースコードです。

public function where($conditions, $glue = 'AND')
{
    if (is_null($this->where))
    {
        $glue = strtoupper($glue);
        $this->where = new JDatabaseQueryElement('WHERE', $conditions, " $glue ");
    }
    else
    {
        $this->where->append($conditions);
    }
    return $this;
}

クエリに追加した最初の "WHERE"の$ glueのみが必要であることがわかります。

この問題についてさらに詳しい情報を見つけました here が、情報が古くなっている可能性があります。

解決

2つの変数$ conditions1と$ conditions2を保持し、次のようにクエリを作成します。

$query->select($db->quoteName(array('tidcode', 'cdcode','email1', 'email2', 'email3', 'email4', 'status')))
    ->from($db->quoteName('#__tid_codes'))
    ->where(implode(' OR ', $conditions1))
    ->where('(' . implode(' OR ', $conditions2) . ')');

これは、このクエリになります:

SELECT `tidcode`,`cdcode`,`email1`,`email2`,`email3`,`email4`,`status` FROM `#__tid_codes` WHERE `tidcode` = '10348' OR `tidcode` = 'F10348' AND (`email1` = '[email protected]' OR `email2` = '[email protected]' OR `email3` = '[email protected]' OR `email4` = '[email protected]')
6
fruppel

これは少し厄介ですが、複雑なwhere()式につまずいたり、演算子の優先順位がわからない場合は、少なくとも1回はこのドキュメントを読んでください。

あなたの質問は、WHERE句の条件をどのように評価すべきかについて少し曖昧です。このページは、言及するのに適した場所です Operator Precedence 。特に明記されていない限り、優先順位は必要に応じて機能しているとみなされます。

ただし、そのルールの影響について説明する前に、まずWHERE句の3番目と最後の部分のロジック(メールのマッチング)を要約します。

_`email1` = '[email protected]'
OR `email2` = '[email protected]'
OR `email3` = '[email protected]'
OR `email4` = '[email protected]'
_

...もっと簡潔に書くことができます(D.R.Y。コーディング慣行):

_'[email protected]' IN (`email1`, `email2`, `email3`, `email4`)
_

次に、演算子の優先順位をその圧縮式を使用して示します。

  • Atidcodeの比較になります
  • Bnewtidcodeの比較になります
  • Cemailの比較になります

[〜#〜]および[〜#〜][〜#〜]または[〜の前に評価されます#〜]s(前のハイパーリンクを参照)

_A OR B AND C_はA OR (B AND C)と同じです...そして、それがあなたの望んでいることだと思います。

_A OR B_を_AND C_の前に評価したい場合は、_(A OR B) AND C_のように括弧で明示する必要があります。


つまり、2つの異なるJoomla構文ソリューションを提供します。

単一のWHEREメソッド呼び出し:(多くの行幅ですが、生のSQLのように読み取ります)

_->where("tidcode = " . $db->q($tidcode) . " OR tidcode = " . $db->q($newtidcode) . " AND " . $db->q($email) . " IN (email1, email2, email3, email4)");
_

2つのWHEREメソッド呼び出し:(行の幅を減らしましたが、生のSQLのようには見えません)

_->where("tidcode = " . $db->q($tidcode), "OR")
->where("tidcode = " . $db->q($newtidcode) . " AND " . $db->q($email) . " IN (email1, email2, email3, email4)");
_

どちらも生成します:

_WHERE tidcode = '10348' OR tidcode = 'F10348' AND '[email protected]' IN (email1, email2, email3, email4)
_


比較のため、次のスニペットは最初にORを評価します!

_->where("tidcode = " . $db->q($tidcode))
->extendWhere("OR", "tidcode = " . $db->q($newtidcode))
->extendWhere("AND", $db->q($email) . " IN (email1, email2, email3, email4)");
_

そして

_->where("tidcode = " . $db->q($tidcode))
->orWhere("tidcode = " . $db->q($newtidcode))
->andWhere($db->q($email) . " IN (email1, email2, email3, email4)");
_

生成する($query->dump()を使用):

_WHERE 
(
(tidcode = '10348') OR 
(tidcode = 'F10348')) AND 
('[email protected]' IN (email1, email2, email3, email4))
_

...[〜#〜]しかし[〜#〜]、もしあなたが[〜#〜]するなら[〜#〜]は最初にORを評価し、次にINを使用してWHERE句の最初の2つの条件を再パッケージ化します。

_->where(
    array(
        "tidcode IN (" . implode(", ", $db->q(array($tidcode, $newtidcode))) . ")",
        $db->q($email) . " IN (email1, email2, email3, email4)"
    )
);
_

生成されるもの:

_WHERE tidcode IN ('10348', 'F10348') AND '[email protected]' IN (email1, email2, email3, email4)
_
1
mickmackusa