web-dev-qa-db-fra.com

Comment combiner plusieurs ensembles de «ET» et de «OU» dans un objet de requête complexe WHERE?

Je veux créer une requête complexe WHERE qui vérifie:

Si (condition1 = TRUE) ET (condition2 = TRUE) ET (condition3 = TRUE OR condition4 = TRUE)

Donc, les deux premières conditions DOIVENT toujours être vraies et ensuite, la condition 3 ou 4 DOIT être vraie.

J'essaie avec le code:

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

Mais lorsque je crache mes réponses, j'obtiens des résultats qui vont clairement à l'encontre de la deuxième condition. Voici une exception de mes résultats, le second ne remplit pas les critères.

 [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"
  }

Quelle est la syntaxe correcte pour la partie WHERE de cette requête?

2
Hannah Smith

Votre condition OR la casse. En ce moment c'est.

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

Donc vous n'avez pas () autour des conditions 3 et 4. Cela donnera également des résultats là où seulement condition4 est true.

Essayer

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

Vous pouvez imprimer une requête pour voir comment elle est construite.

Par exemple, si votre variable de requête est $query, alors fais

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

et il va imprimer la requête complète ce qui est exécuté. De cette façon, vous pouvez voir ce qui ne va pas. Juste pour le débogage simple.

2
Rene Korss

J'allais limiter mon envie de poster après l'acceptation d'une solution de travail, mais j'ai par la suite décidé qu'il était utile d'expliquer comment le processus de création de requêtes pouvait être modifié pour améliorer la lisibilité du code, les intentions, la brièveté et (à un degré imperceptible). performances tout en maintenant la sécurité/fonctionnalité. Je vais également faire quelques suggestions qui concernent uniquement les préférences personnelles en matière de codage. Je veux juste dire, à l'avance, que la réponse de René est sûre et complètement fonctionnelle.

Voici comment je peux coder la requête et ensuite pourquoi:

$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
}

C'est la requête rendue (à partir de ->dump()) qui fournit la logique de requête prévue:

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 = 'chaîne de l'animal') AND
(`a`.`left`> = b.date OR` a`.`left` = '2018-06-19')

Explications:

  • Les colonnes de la clause SELECT se présentent sous la forme d'un tableau, mais peuvent tout aussi bien être écrites sous la forme d'une chaîne unique de valeurs séparées par des virgules. Seul le nom de la colonne left [~ # ~] a besoin de [~ # ~] pour être encapsulé en arrière - cela peut être fait en toute sécurité avec des backticks codés en dur ou avec un qn() appel.
  • Pour résumer cette référence crédible , seuls les noms de table, les noms de colonne et les alias (appelons-les "entités de base de données") qui remplissent une ou plusieurs des conditions suivantes [~ # ~] doit [~ # ~] être encapsulé à l'envers:
    1. contient des caractères d'espacement
    2. contient des caractères extérieurs à cette liste blanche: lettres latines, chiffres, trait de soulignement, dollarsign
    3. contient MySQL réservé mots-clés .
  • Compte tenu des critères ci-dessus, il est conseillé de définir les noms de table et de colonne de manière à ce qu'ils ne contiennent que des lettres latines, des chiffres, des traits de soulignement et des symboles de signature ET évitez d'utiliser des mots réservés. En outre, cela clarifierait l’intention de codage pour éviter d’utiliser également les mots non réservés. Dans le message d'origine, left est un mot réservé. event, date et type sont des mots-clés MySQL, mais pas réservés. OMI, éliminer les appels de fonction qui ne fournissent aucun avantage est une bonne habitude de codage.
  • Écrire des entités dans tous les mots en minuscules et MySQL en majuscules est un excellent moyen de différencier instantanément les entités et les fonctions/mots réservés de MySQL.
  • J'utilise qn() comme raccourci pour quoteName(), q() pour quote() et innerJoin() pour join("INNER",...) pour la brièveté.
  • Je n'aime pas utiliser plusieurs appels where() dans une seule requête, car cela s'écarte trop de la construction de requête brute. Vous voyez, chaque nouveau innerJoin() écrit une nouvelle ligne INNER JOIN, Mais les appels répétés where() ne font que prolonger la clause unique. J'utilise andWhere () non seulement pour le paramètre "glue interne" (OR) mais aussi parce qu'il enveloppe ses expressions entre parenthèses et force la valeur initiale where() expressions à mettre entre parenthèses pour isoler le regroupement logique. Si vous souhaitez écrire plusieurs expressions dans un appel where() (ou select()), je vous recommande de lui fournir un array() de conditions.
  • [~ # ~] surtout [~ # ~] Il y a deux utilisations de LIKE dans la requête d'origine, mais elles ne sont pas utilisées. utilisé pour effectuer une correspondance partielle. Je ne sais pas s'il s'agissait d'une coïncidence ou d'une idée fausse sur la façon de comparer une colonne et une chaîne, mais ce n'est pas une bonne pratique et cela risque de semer la confusion dans l'esprit des futurs chercheurs. Si votre logique est de faire correspondre une chaîne complète, utilisez l'opérateur de comparaison =. Si votre logique est de faire une correspondance partielle, utilisez LIKE avec une chaîne incluant _ Ou %. Consultez ce guide de la documentation Joomla syntaxe recommandée pour citer une variable dans votre expression LIKE.
  • Enfin, si quelqu'un préfère le "sucre syntaxique" de AS entre les tables/colonnes et leurs alias respectifs, utilisez-le.
1
mickmackusa