Partage
  • Partager sur Facebook
  • Partager sur Twitter

Meilleur moyen pour une requête SQL "flexible" ?

    19 septembre 2019 à 12:09:30

    Bonjour à tous,

    La problématique étant assez délicate à expliquer avec quelques mots clés, je ne sais pas vraiment si elle a déjà été posée ici. N'hésitez pas à me renvoyer vers les sujets concernés si tel est le cas ;)

    Admettons la chose suivante : j'ai une table SQL qui associe un `id` avec un `nom` et un `prenom`. J'aimerais faire une fonction qui va me chercher des lignes dans cette table, en fonction du nom exact de la personne :

    function fetchPeople(PDO $dbh, $nom) {
    	$req = $dbh->prepare('SELECT * FROM people WHERE nom = ?');
    	$req->execute(array($nom));
    	return $req->fetchAll();
    }

    Jusqu'ici, rien de bien original, c'est une fonction tout à fait classique. Maintenant admettons que je veuille une nouvelle fonction qui me permet de rechercher des lignes en fonction du nom ET du prénom exact de la personne. Je pourrais faire une deuxième fonction s'appelant `fetchPeople2()`, mais sur l'échelle de la lisibilité et de la propreté, on ne vole pas très haut... Autre solution, ajouter une deuxième variable dans ma fonction, qui aurait une valeur par défaut (ne correspondant à aucun prénom) et adapter la requête en fonction de cette deuxième variable :

    function fetchPeople(PDO $dbh, $nom, $prenom = "null") {
    	if($prenom == "null") { // Alors on fait une recherche avec un seul critère : le nom
    		$req = $dbh->prepare('SELECT * FROM people WHERE nom = ?');
    		$req->execute(array($nom));
    		return $req->fetchAll();
    	}
    	else { // On inclut les deux critères dans la recherche
    		$req = $dbh->prepare('SELECT * FROM people WHERE nom = ? AND prenom = ?');
    		$req->execute(array($nom, $prenom));
    		return $req->fetchAll();
    	}
    }

    C'est la solution que j'utilise actuellement, mais elle me plaît guère pour deux raisons :

    • L'enchaînement de `if`, `else if` et `else` n'est pas très propre
    • Si la base même de la requête change, il faut la modifier dans tous les blocs 

    Ma question est donc la suivante : existe-t-il une valeur particulière que je pourrais attribuer à défaut à la variable `$prenom` (au lieu de `null`) pour dire à SQL : "Recherche toutes les personnes qui ont tel nom, peu importe leur prénom", et ainsi transformer mon code comme cela (en supposant que le mot magique est `everything`) :

    function fetchPeople(PDO $dbh, $nom, $prenom = "everything") {
    	$req = $dbh->prepare('SELECT * FROM people WHERE nom = ? AND prenom = ?'); // Toutes les personnes qui ont un nom particulier, *peu importe* leur prénom (everything)
    	$req->execute(array($nom, $prenom));
    	return $req->fetchAll();
    }

    Ou bien, avez-vous une suggestion ?

    Merci ! :)

    -
    Edité par plagoutte 19 septembre 2019 à 12:12:51

    • Partager sur Facebook
    • Partager sur Twitter
      19 septembre 2019 à 12:18:28

      Salut,

      1) pas "null" mais null sans les "

      2) active les exceptions PDO pour développer et les erreurs PHP

      3) tu peux créer une requête en concaténant les morceaux de chaine en fonction des paramètres (je te laisse chercher un peu)

      • Partager sur Facebook
      • Partager sur Twitter
      Anonyme
        19 septembre 2019 à 12:25:05

        Déjà fetchPeoplene donne aucune indication sur les conditions en jeu. Si tu voulais créer deux méthodes elles devraient sans doute s'appeler fetchPeopleByLastname et fetchPeopleByFullname.

        Si tu choisis de les mutualiser tu peux faire beaucoup mieux qu'un copier-coller : dans le cas où un prénom est passé tu as juste à ajouter un morceau de requête et l'ajouter à execute.

        Sinon, pour simplifier la mutualisation ou même remplacer tes fonctions tu peux envisager un query builder.

        • Partager sur Facebook
        • Partager sur Twitter
          19 septembre 2019 à 12:29:25

          Merci pour ta réponse :)

          1) pas "null" mais null sans les "

          Il faudra que j'essaye, mais que se passe-t-il si je passe un string vide en paramètre de execute() ? Je ne suis pas certain que mysql apprécie beaucoup, c'est pourquoi j'avais pensé mettre entre guillemets :)

          2) active les exceptions PDO pour développer et les erreurs PHP

          Oui bien sûr, les morceaux de code que j'ai mis dans mon post initial ne sont là que pour illustrer, je ne voulais pas les charger avec des éléments inutiles à la question.

          3) tu peux créer une requête en concaténant les morceaux de chaine en fonction des paramètres (je te laisse chercher un peu)

          Ça fait effectivement un peu plus propre (même si l'utilisation d'autant de if que de paramètres est requis) que ma première solution, on partirait sur quelque chose comme ça :

          function fetchPeople(PDO $dbh, $nom, $prenom = NULL) {
          	$sql = "SELECT * FROM people WHERE nom = ?";
          	$parameters = array($nom);
          	
          	if(!is_null($prenom)) {
          		$sql .= " AND prenom = ?";
          		array_push($parameters, $prenom);
          	}
          	
          	$req = $dbh->prepare($sql);
          	$req->execute(array($parameters));
          	return $req->fetchAll();
          }

          MatTheCat a écrit:

          Déjà fetchPeoplene donne aucune indication sur les conditions en jeu. Si tu voulais créer deux méthodes elles devraient sans doute s'appeler fetchPeopleByLastname et fetchPeopleByFullname.

          Si tu choisis de les mutualiser tu peux faire beaucoup mieux qu'un copier-coller : dans le cas où un prénom est passé tu as juste à ajouter un morceau de requête et l'ajouter à execute.

          Sinon, pour simplifier la mutualisation ou même remplacer tes fonctions tu peux envisager un query builder.


          Merci, je vais jeter un oeil aux query builders ;) Les noms des fonctions étaient clairement choisis arbitrairement ici, c'est clair qu'ils ne sont pas représentatifs (mais ce n'était pas vraiment l'objectif).

          -
          Edité par plagoutte 19 septembre 2019 à 12:31:31

          • Partager sur Facebook
          • Partager sur Twitter
            19 septembre 2019 à 12:36:22

            Si tu envoie une chaine vide, la requête se fera sur une recherche de chaine vide => le résultat sera vide s'il n'existe pas de donnée avec prénom = NULL (par exemple)

            teste ceci et tu verras comme SQL est "magique ;) :

            SELECT
                *
            FROM
                people
            WHERE
                nom = '<un_nom_qui_existe_en_base>'
            AND
                prenom = null



            -
            Edité par christouphe 19 septembre 2019 à 12:38:05

            • Partager sur Facebook
            • Partager sur Twitter
            Anonyme
              19 septembre 2019 à 12:51:55

              @christouphe ta requête ne retournera jamais rien parce que NULL n'est égal à rien en SQL.

              C'est pour ça qu'il existe IS [NOT] NULL.

              -
              Edité par Anonyme 19 septembre 2019 à 12:53:12

              • Partager sur Facebook
              • Partager sur Twitter
                19 septembre 2019 à 13:10:24

                MatTheCat a écrit:

                @christouphe ta requête ne retournera jamais rien parce que NULL n'est égal à rien en SQL.

                C'est pour ça qu'il existe IS [NOT] NULL.

                -
                Edité par MatTheCat il y a 14 minutes


                Sûr ;) non parce que MySQL m'aurait menti alors :p

                Montrer zone SQL

                MySQL a retourné un résultat vide (aucune ligne). (Traitement en 0.0010 secondes.)
                select * from membre where pseudo = null
                • Partager sur Facebook
                • Partager sur Twitter
                Anonyme
                  19 septembre 2019 à 13:28:33

                  Pas compris ta réponse.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    19 septembre 2019 à 13:40:32

                    juste que Mysql comprend très bien la requête que j'ai fourni plus haut (et tous les moteurs SQL aussi), il renvoie un résultat vide, car il n'a rien trouvé

                    ;)

                    -
                    Edité par christouphe 19 septembre 2019 à 13:43:36

                    • Partager sur Facebook
                    • Partager sur Twitter
                    Anonyme
                      19 septembre 2019 à 14:24:44

                      Roooh christouphe tu ne peux pas prouver à quelqu'un qu'il a tort en lui montrant que ton résultat est celui qu'il avait prédit. Remplace ton = par <> et tu verras. Ou lis la doc.

                      -
                      Edité par Anonyme 19 septembre 2019 à 14:25:18

                      • Partager sur Facebook
                      • Partager sur Twitter
                        19 septembre 2019 à 14:36:58

                        :D

                        sa requête est avec = ;)

                        Bon je chipote tu as raison, c'est juste qu'il faut rester dans le périmètre.

                        Mais je suis joueur ;)

                        La preuve:

                        • Type de serveur : MySQL
                        • Version du serveur : 5.7.11 - MySQL Community Server (GPL)
                        MySQL a retourné un résultat vide (aucune ligne). (Traitement en 0.0010 secondes.)
                        select * from membre where pseudo <> null

                        -
                        Edité par christouphe 19 septembre 2019 à 14:37:16

                        • Partager sur Facebook
                        • Partager sur Twitter
                        Anonyme
                          19 septembre 2019 à 14:42:12

                          Donc selon toi MySQL ne trouve aucun pseudo NULL, et aucun pseudo différent de NULL ? Ferais-tu tes tests sur une table vide ?

                          -
                          Edité par Anonyme 19 septembre 2019 à 14:42:48

                          • Partager sur Facebook
                          • Partager sur Twitter
                            19 septembre 2019 à 14:46:45

                            Non, non ma table contient bien des données par contre, je ne demande pas un pseudo "null" mais la valeur null <=> rien | vide et ça un moteur SQL sait le gérer

                            Bref tout ça pour dire que le mot clé null (et pas la chaine de caractère "null") est bien compris :D

                            • Partager sur Facebook
                            • Partager sur Twitter
                            Anonyme
                              19 septembre 2019 à 14:52:07

                              Tain donc ça ne te fais même pas réagir qu'une condition et son contraire renvoient les mêmes résultats ?

                              On n'utilise pas NULL dans une condition. La requête que tu as proposée ne renverra AUCUN résultat quelles que soient les données présentes dans la table.

                              • Partager sur Facebook
                              • Partager sur Twitter
                                19 septembre 2019 à 15:13:04

                                MatTheCat a écrit:

                                Tain donc ça ne te fais même pas réagir qu'une condition et son contraire renvoient les mêmes résultats ?

                                On n'utilise pas NULL dans une condition. La requête que tu as proposée ne renverra AUCUN résultat quelles que soient les données présentes dans la table.


                                Je suis d'accord, et c'est bien le principe de NULL, ne pas faire tout planter si tu envoie une requête avec NULL, le moteur renvoie un résultat vide et basta.

                                Je ne vois ps ce qui te choque en fait. Si tu gères bien tes champs, les miens sont NOT NULL en définition, une recherche avec une valeur NULL renvoie un résultat vide <=> aucun tuple avec un pseudo NULL et c'est logique puisque j'interdis qu'un pseudo soit null et c'est un résultat cohérent.

                                • Partager sur Facebook
                                • Partager sur Twitter
                                Anonyme
                                  19 septembre 2019 à 15:24:30

                                  Explique moi alors pourquoi une recherche avec une valeur différente de NULL ne retourne rien puisque ton champ est NOT NULL.

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    19 septembre 2019 à 15:30:18

                                    Depuis que j'ai testé pgsql je peux dire que mysql nous fais avoir des mauvais automatisme mais c'est renforcé par l'article suivant : https://sqlpro.developpez.com/tutoriel/dangers-mysql-mariadb/

                                    Mais la par exemple pouvoir tester que c'est NULL alors que la colonne est NOT NULL c'est déjà, désolé du terme, débile mais en plus que <> NULL retourne rien est une aberration.

                                    Sinon l'auteur en est où ? :)

                                    -
                                    Edité par quenti77 19 septembre 2019 à 15:30:46

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                    Anonyme
                                      19 septembre 2019 à 15:41:24

                                      Cette gestion de NULL fait partie du standard donc on va éviter le troll.

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        19 septembre 2019 à 15:44:02

                                        Ça reste débile. Et non je troll pas surtout avec le lien de l'article que j'ai mis et sur le fait que mysql nous fais faire de mauvaise chose.

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          19 septembre 2019 à 15:44:43

                                          Seuls les imbéciles ne changeant pas d'avis, j'ai réalisé d'autres tests dont le fameux

                                          SELECT * FROM membre WHERE pseudo IS NOT NULL

                                          qui renvoie bien l'intégralité de la table, mea culpa <> null aurait dû renvoyer la même chose.

                                          Bref, cela me fait penser que le parser de mysql fait passer le mot à droite de = comme un texte et pas comme un mot clé, c'est assez zarb en effet, et j'avoue que je me suis trompé ;)

                                          Bref attendons l'auteur et ses tests :D

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            19 septembre 2019 à 22:36:29

                                            pifou25 a écrit:

                                            Hello,

                                            juste pour la blague, voila ce qui arrive quand on écrit "null" au lieu du mot clé null sans quote :D 

                                            https://www.01net.com/actualites/un-hacker-a-voulu-faire-le-malin-et-tromper-les-cameras-avec-une-plaque-d-immatriculation-null-1752145.html 


                                            :D :D :D
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              20 septembre 2019 à 10:08:29

                                              Donc en effet, passer en argument une variable n'ayant aucune valeur n'est pas une bonne idée :)

                                              Concaténer des morceaux de requête en fonction des arguments passés dans la fonction me semble être assez efficace. Pour les tables disposant d'un grand nombre de champs, j'ai fait une petite variante de mon code précédent, qui permet d'éviter les définitions de fonctions à rallonge :

                                              function fetchPeople(PDO $dbh, array &$conditions) {
                                              	$sql = "SELECT * FROM people WHERE id NOT NULL"; // Le WHERE id NOT NULL ici n'a d'utilité que pour introduire le mot clé WHERE, pour que le foreach n'ait plus qu'à introduire AND
                                              	$parameters = array();
                                              	$availableColumns = array("first_name", "last_name"); // Champs disponibles
                                              	
                                              	foreach($conditions as $column => $value) { // Pour chaque critère passé en argument de la fonction
                                              		if(in_array($column, $availableColumns)) { // On vérifie que le champ passé en argument existe bien (élimination de tout risque d'injection XSS)
                                              			$sql .= " AND " . $column . " = ?";
                                              			array_push($parameters, $value);
                                              		}
                                              	}
                                              	
                                              	$req = $dbh->prepare($sql);
                                              	$req->execute(array($parameters));
                                              	return $req->fetchAll();
                                              }
                                              
                                              /* Exemple :
                                              
                                              $people = fetchPeople($dbh, array(
                                              	"first_name" => "Jean-Marc",
                                              	"last_name" => "DUPONT));
                                              	
                                              */

                                              Je ne l'ai pas testé en conditions réelles, quelques petits ajustements sont peut être nécessaires

                                              pifou25 a écrit:

                                              Hello,

                                              juste pour la blague, voila ce qui arrive quand on écrit "null" au lieu du mot clé null sans quote :D 

                                              https://www.01net.com/actualites/un-hacker-a-voulu-faire-le-malin-et-tromper-les-cameras-avec-une-plaque-d-immatriculation-null-1752145.html 


                                              Ah oui en effet :lol:

                                              -
                                              Edité par plagoutte 20 septembre 2019 à 10:47:56

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                              Anonyme
                                                20 septembre 2019 à 10:15:26

                                                Ouais non s'il y a plusieurs WHERE dans ta requête ça ne fonctionnera pas !

                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  20 septembre 2019 à 10:47:14

                                                  Ah oui exact, c'est corrigé ! :p merci
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter

                                                  Meilleur moyen pour une requête SQL "flexible" ?

                                                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                  × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                  • Editeur
                                                  • Markdown