web-dev-qa-db-fra.com

Combinaison de "AND" et de "OR" dans une requête WHERE complexe - Partie 2

Je me suis tiré les cheveux avec cette requête pour les 2 derniers jours et ont lu et relu les 2 autres messages similaires sans aucune chance.

Scénario: Je recueille deux entrées d’un utilisateur: le code d’identification du test et l’adresse électronique qu’ils ont utilisée lors de la soumission du test. Le code de test est composé de chiffres et peut contenir une lettre au début, mais le centre de test peut laisser ce numéro, donc j'ajoute la lettre à une variable de la requête (le motif de "newtidcode" est indiqué ci-dessous). Ils peuvent également fournir jusqu'à 4 adresses électroniques au testeur lors de la soumission (raison pour laquelle j'ai testé sur 4 possibilités de messagerie).

Voici ma requête:

    $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).')');

Voici l'instruction SQL créée:

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]')

Comme vous pouvez le voir OR avant que l'e-mail ne soit un AND, comme spécifié dans la 2ème déclaration "where". J'essayais même d'utiliser des tableaux de conditions comme ci-dessous et je rencontrais toujours le même problème.

$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)
);

Ceci est ma première incursion dans JDatabase en interrogeant cette complexité. J'ai cette application qui fonctionne très bien en HTML et PHP standard, mais en essayant de porter sur Joomla. Toute aide est grandement appréciée.

6
TJM

Le deuxième paramètre de where, appelé $glue, ne fonctionne pas comme prévu.

C'est le code source de la fonction 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;
}

Vous pouvez voir que cela ne prend que la colle $ du premier "WHERE" que vous ajoutez à votre requête.

J'ai trouvé quelques informations supplémentaires sur ce problème ici , mais ces informations peuvent être obsolètes.

Solution

Conservez vos 2 variables, $ conditions1 et $ conditions2, puis construisez votre requête comme suit:

$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) . ')');

Ce qui aboutira à cette requête:

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

Ceci est un peu verbeux, mais si jamais vous vous retrouvez avec des expressions complexes where() ou si vous ne connaissez pas la priorité des opérateurs, veuillez le lire au moins une fois.

Votre question est légèrement ambiguë sur la manière dont les conditions de la clause WHERE devraient être évaluées. Cette page est un endroit approprié pour mentionner priorité de l'opérateur . Sauf indication contraire, nous pouvons tous supposer que la préséance fonctionne comme vous le souhaitez.

Cependant, avant de commencer à expliquer l'impact de cette règle, je souhaite tout d'abord condenser la logique de la 3ème/dernière partie de votre clause WHERE (le courrier électronique correspondant).

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

... peut être écrit beaucoup plus succinctement ( D.R.Y. pratique de codage) comme:

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

Maintenant, je vais démontrer la préséance de l'opérateur en utilisant cette expression condensée.

  • A sera la comparaison tidcode
  • B sera la comparaison newtidcode
  • C sera la comparaison email

[~ # ~] et [~ # ~] s sont évalués avant [~ # ~] ou [~ # ~] s (voir l'hyperlien précédent)

A OR B AND C Est identique à A OR (B AND C) ... et je pense que c'est ce que vous désirez.

Si vous voulez que A OR B Soit évalué avant AND C, Vous devez être explicite avec des parenthèses telles que: (A OR B) AND C.


Cela dit, je proposerai deux solutions Joomla-syntax différentes.

Appel de méthode WHERE unique : (beaucoup de largeur de ligne, mais se lit comme du brut sql)

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

Deux appels de méthode WHERE : (largeur de ligne réduite, mais se lit moins comme le brut sql)

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

Les deux produisent:

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


Pour comparaison, les extraits suivants évaluent le OR en premier! :

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

et

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

produire (via $query->dump()):

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

... [~ # ~] mais [~ # ~] , si vous [~ # ~] faites [~ # ~] veut d'abord évaluer OR, puis reconditionner les deux premières conditions de la clause WHERE avec IN également.

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

qui produit:

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