Commentaires

  1. Avatar de henrif
    • |
    • permalink
    Citation Envoyé par laurentSc
    Sinon, peux-ty donner le code complet de cette requête :
    Par exemple, pour 3 enregistrements :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    INSERT INTO `t_video` (`video_title`, `video_support`, `video_multilingual`, `video_chapter`, `video_year`, `video_summary`, `video_stock`) 
    VALUES 
      ('The Lord of the Rings - The Fellowship of the Ring', 'BLU-RAY', '1', '1', '2001', '', '10'), 
      ('The Lord of the Rings - The two towers', 'BLU-RAY', '1', '2', '2002', '', '0'), 
      ('The Lord of the Rings - The return of the King', 'DVD', '1', '3', '2003', '', '1')
    C'est la requête classique d'insertion d'un lot de valeurs dans une table , tel qu'on la trouve dans un dump sql d'une table (généré par exemple un export avec PhpMyAdmin).

    Mais peut être ai-je soulevé un faux problème ?
    En pratique, je ne vois pas de cas où on aurait besoin de faire une boucle sur l'insertion multiple de valeurs dans une même table hormis l'historisation de données, la synchronisation d'une table. Si le script php a déjà mis en forme les data dans une table la requête multiple comme ci dessus sera plus rapide (en veillant quand même à ne pas dépasser la taille maxi d'une requête sql).
    L’utilisation des emplacements nommés (:variable) ou des marqueurs de positionnement (?) est peut être plus pertinente sur des requêtes de sélection (SELECT...).

    EDIT du 27 Mai

    Avec un peu de réflexion, une instruction INSERT implique une écriture dans les fichiers de la table, ce qui est couteux en temps (accès aux fichiers...). La seule façon d'optimiser est de regrouper les insertions d'une même table à l'aide de la requête multiple indiquée au dessus, de façon à n'avoir qu'un seul accès en écriture. La couche PDO et le passage des valeurs par référence n'apporte rien puisque l'exécution de la requête entrainera une écriture sur les fichiers. Boucler sur des requêtes INSERT entraine plusieurs écritures donc une performance moindre.

    Par contre une requête de recherche (SELECT), surtout multi critères, nécessite des tris sur les tables d'index et la couche PDO par le mécanisme de préparation va mettre en cache ces opérations. Celles-ci ne seront pas refaites lors d'un nouvel appel à la même requête avec des valeurs différentes.
    Mis à jour 27/05/2020 à 10h03 par henrif (Après réflexion...)
  2. Avatar de laurentSc
    • |
    • permalink
    Si j'avais transmis la réponse de rawsrc, c'est qu'il vient de quitter dvp, et je le joins par e-mail...

    Sinon, peux-ty donner le code complet de cette requête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $sql     = <<<sql
    INSERT INTO t_video (video_title, video_support, video_multilingual, video_chapter, video_year, video_summary, video_stock) 
         VALUES (values1.....),
                     (values2....)...
    sql;
    J'ai pas le courage de l'écrire...
  3. Avatar de henrif
    • |
    • permalink
    Citation Envoyé par laurentSc
    Je transmets la réponse de rawsrc :

    Le mécanisme de préparation a un énorme avantage sur le passage d'une seul et unique SQL très longue, c'est l'optimisation et la mise en cache qui est faite par le moteur de base de données.
    Le plan d'exécution de la requête n'est calculé qu'une seule et unique fois. Après les mécanismes d'optimisations s'enclenchent automatiquement à chaque tour.
    Je préfère de loin utiliser les requêtes préparées paramétrées que du SQL brut quand il y a plusieurs tours de boucle à réaliser.
    Sans compter que question lecture du SQL tu y gagnes à être concis.
    Je reviens sur les performances de l'exemple donné , à savoir la boucle php:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $sql     = <<<sql
    INSERT INTO t_video (video_title, video_support, video_multilingual, video_chapter, video_year, video_summary, video_stock) 
         VALUES ({$in($title)}, {$in($support)}, {$in($multilingual, 'bool')}, {$in($chapter, 'int')}, {$in($year, 'int')}, {$in($summary)}, {$in($stock, 'int')})
    sql;
     
    foreach ($data as $film) {
        extract($film);
        $ids[] = $ppp->insert($sql);    // à la fin : [1, 2, 3] : nos 3 films ont bien été ajoutés
    }
    vs la requête unique avec plusieurs lignes de valeurs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // boucle de construction de la requête à partir du tableau $data pour obtenir 
    $sql     = <<<sql
    INSERT INTO t_video (video_title, video_support, video_multilingual, video_chapter, video_year, video_summary, video_stock) 
         VALUES (values1.....),
                     (values2....)...
    sql;
    
    $stmt2 = $dbh->prepare($query); 
    $stmt2->execute();
    Mon environnement et PHP 7 et Mysql 5, donc pas universel, mais assez courant pour les sites web.
    J'ai fait quelques mesures de temps d'exécution avec hrtime() et la première méthode prend nettement plus de temps : environ 2 fois plus pour 3 enregistrements , 5 fois plus pour 10, 15 fois plus pour 100 !
    La fonction mysqli $dbh->query($query) prend elle 2 fois plus de temps que PDO. Ouf !

    Bref, je ne suis pas convaincu de l'optimisation due au passage par référence des variables ?!...
  4. Avatar de laurentSc
    • |
    • permalink
    J'ai mis au point (c'est testé) une requête qui fait un insert et évite les doublons (si une clé unique est déjà présente dans la table, UPDATE à la place de INSERT) :

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    $ppp = new PDOPlusPlus(PDOPlusPlus::MODE_PREPARE_PARAMS);
                $in = $ppp->injector(); // injecteur de référence
                $ids = [];
                $sql = <<<sql
    INSERT INTO t_video ( video_title, video_support, video_multilingual, video_chapter, video_year, video_summary, video_stock) 
         VALUES ( {$in($title)}, {$in($support)}, {$in($multilingual,
                    'bool')}, {$in($chapter,
                    'int')}, {$in($year,
                    'int')}, {$in($summary)}, {$in($stock,
                    'int')})
         ON DUPLICATE KEY UPDATE video_title={$in($title)},video_support= {$in($support)},video_multilingual= {$in($multilingual,
                    'bool')},video_chapter={$in($chapter,
                    'int')},video_year={$in($year,
                    'int')},video_summary={$in($summary)},video_stock= {$in($stock,
                    'int')}
    sql;
     
                foreach ($data
                         as
                         $film)
                {
                    extract($film);
                    $ids[] = $ppp->insert($sql);    // à la fin : [1, 2, 3] : nos 3 films ont bien été ajoutés
                }
  5. Avatar de laurentSc
    • |
    • permalink
    Je transmets la réponse de rawsrc :

    Le mécanisme de préparation a un énorme avantage sur le passage d'une seul et unique SQL très longue, c'est l'optimisation et la mise en cache qui est faite par le moteur de base de données.
    Le plan d'exécution de la requête n'est calculé qu'une seule et unique fois. Après les mécanismes d'optimisations s'enclenchent automatiquement à chaque tour.
    Je préfère de loin utiliser les requêtes préparées paramétrées que du SQL brut quand il y a plusieurs tours de boucle à réaliser.
    Sans compter que question lecture du SQL tu y gagnes à être concis.
  6. Avatar de henrif
    • |
    • permalink
    Bonjour rawsrc,

    Avant tout, grand merci pour le partage.
    Je débute dans l'utilisation de PDO, je suis bien loin de maitriser les appels des fonctions de cette classe.

    J'ai une question sur le paragraphe 8.3 ÉCHAPPEMENT AVEC LE MÉCANISME DE PRÉPARATION ET PASSAGE DES VALEURS PAR RÉFÉRENCE, où il s'agit d'insérer plusieurs enregistrements dans la table sql.

    En terme de performance d'accès à la base sql, ne vaut-il pas mieux faire une seule requête avec plusieurs enregistrements, de la forme :
    INSERT INTO `table` (`col1`, `col2`, ...) VALUES
    ('value1col1',....), ('value2col1',....), ('value3col1',....)

    plutôt que de faire une boucle php générant plusieurs requêtes sql ?
  7. Avatar de laurentSc
    • |
    • permalink
    Bonjour,
    je tente d'utiliser cette nouvelle version. Tu m'as dit qu'il y a une nouvelle syntaxe (avec addChild). J'ai essayé d'adapter l'exemple d'utilisation de la version 1 avec cette syntaxe, mais c'est pas bon. De plus, dans ton billet, cette syntaxe n'est pas évoquée. Et mon essai n'est pas bon. Pourrais-tu le corriger ? (et compléter ton billet) :

    login.php :

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $page = new PhpEcho([DIR_ROOT, 'view Layout.php']);
    $page->addChild('body', [DIR_ROOT, 'view LoginForm.php'], [
        'url_submit' => '/index.php?page=loginsubmit',
        'login'      => 'rawsrc'
    ]);
    echo $page;

    j'ai aussi modifié loginform.php :

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <p>Veuillez vous identifier</p>
    <form method=post action="<?= URL_HOME.$this['url_submit'] ?>">
        <label>Identifiant</label>
        <input type="text" name="login" value="<?= $this['login'] ?>"><br>
        <label>Mot de passe</label>
        <input type="password" name="pwd" value=""><br>
        <input type="submit" name="submit" value="SE CONNECTER">
    </form>
    <br>
    <p style="display:<?= $this['show_error'] ?? 'none' ?>"><strong><?= $this['err_msg'] ?></strong></p>
    plus de notation appel de fonction.
    Visiblement, le tableau passé ligne 3 et 4 de login.php n'est pas attendu. Comment faire ? (J'ai bien pris la version sur GitHub)
  8. Avatar de rawsrc
    • |
    • permalink
    Bonjour,

    j'ai procédé à une importante mise à jour du code de la classe PDOPlusPlus :
    - ajout de la gestion complète des transactions : Paragraphe 12

    Bon code à tous

    rawsrc
  9. Avatar de rawsrc
    • |
    • permalink
    Bonjour,

    suite à un petit mot de CinePhil, j'ai corrigé quelques oublis et erreurs de syntaxe dans les codes d'exemple.

    Merci

  10. Avatar de rawsrc
    • |
    • permalink
    Bonjour,

    suite à des tests d'optimisation beaucoup plus poussés (montée de charge en particulier), j'ai constaté l'exécution d'un code redondant sur la liaison des helpers à l'instance de la classe courante.
    Redondance supprimée via un mécanisme de gestion de liaison des helpers.

    Mise à jour du code publiée dans ce billet et sur GitHub.
    Comme c'est une optimisation interne au fonctionnement de la classe, il n'y a aucun impact en terme de code externe sur l'utilisation de cette classe.
    Mis à jour 06/04/2020 à 10h18 par rawsrc
  11. Avatar de rawsrc
    • |
    • permalink
    Bonjour à tous,

    j'ai procédé à une importante mise à jour du code source de PDOPlusPlus.
    J'y ai intégré notamment la gestion complète des procédures stockées paramétrées ou pas, renvoyant zéro ou plusieurs jeux de données simultanément.

    Attention : à la différence de la précédente mouture, cette version ne gère plus la validation des valeurs nullables. C'est le seul changement qui risque de vous créer une légère incompatibilité avec la version précédente.
  12. Avatar de grunk
    • |
    • permalink
    AU temps pour moi j'ai regardé la source sur github en diagonale
  13. Avatar de rawsrc
    • |
    • permalink
    si si tu peux récupérer l'instance PDO courante, heureusement d'ailleurs :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ppp  = new PPP(PPP::MODE_SQL_DIRECT);
    $pdo  = $ppp->pdo();   // extraction de la ressource ouverte par PDOPlusPlus
    C'est dans le premier exemple de code
  14. Avatar de grunk
    • |
    • permalink
    Perso j'utilise jamais bindValue ce qui enlève une bonne partie de la lourdeur de PDO :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$param1,$param2]);
    Comme je considère que mes types doivent être bon avant d'arriver dans le SQL , le type du bind n'a pas d'importance.

    A défaut de dériver de PDO , il manque , je pense, à ta classe un moyen de récupérer l'instance de PDO en cours dans le cas ou quelqu’un voudrais faire quelque chose que ta classe ne permet pas ou qu'il ai besoin de la passer à un objet qui attend une instance de PDO.
  15. Avatar de rawsrc
    • |
    • permalink
    Bon, après un paquet de tests dans tous les sens, il semblerait que la récupération d'un paramètre OUT en 2 étapes fonctionne impec et semble "assez universelle".
    Donc, c'est sur cette voie que je vais m'engager pour la gestion des paramètres OUT et INOUT dans les appels aux procédures stockées avec la deuxième étape automatique, il va de soi : le SELECT @PARAM_OUT.
    Bien que sur les autres fonctionnalités, PDO s'en sort très honorablement, sur l'aspect OUT et INOUT, c'est franchement moisi. Du coup je laisse PDO et sa cuisine de côté. Ça crée trop de problèmes.

    Si vous avez d'autres pistes, n'hésitez pas à me les soumettre.
    Par ailleurs si vous avez un SGBDR qui ne comprend pas cette syntaxe, ça serait aussi cool de participer à ce fil.

    Merci
  16. Avatar de rawsrc
    • |
    • permalink
    euh je suis en train de mener des tests avec les procédure stockées et PDO, et ben c'est un beau bordel dès qu'il y a des paramètres OUT et INOUT. Les paramètres IN sont plutôt homogènes (ouf).
    Le fameux bitwise avec PDO::PARAM_INPUT_OUTPUT n'a pas l'air d'être pris en compte de la même manière selon les moteurs et même selon les versions quand il n'est carrément pas ignoré ou fasse tout planter.

    Euh, il le savent chez Zend que ça semble créer plus de problèmes que ça n'en résout ?
  17. Avatar de rawsrc
    • |
    • permalink
    Salut CinePhil

    ouaip, j'ai ça dans les tuyaux, cela fera l'objet d'une mise à jour. Je dois encore mener quelques tests plus poussés avant de publier.
    Pour l'instant, cela supporte le CRUDE (Create (INSERT), Read (SELECT), UPDATE, DELETE, EXECUTE).
  18. Avatar de CinePhil
    • |
    • permalink
    Bonjour,

    Pas essayé mais ça semble intéressant.

    As-tu essayé l'appel aux procédures stockées (CALL la_procédure (les, parametres, demandés, par, la, procedure) avec la subtilité des IN, OUT et INOUT) ?
  19. Avatar de rawsrc
    • |
    • permalink
    Citation Envoyé par laurentSc
    $new_id = $ppp->insert($sql); tu as écrit $film['multilngues']. Manque un i.
    Merci c'est corrigé,

    Citation Envoyé par laurentSc
    Petite surprise : j'ai volontairement introduit une erreur dans la requête SQL, mais aucun message d'erreur affiché...Normal ou j'ai oublié de faire un truc ?
    Pour ce qui est des erreurs, quand cela échoue, la fonction renvoie null et l'erreur s'en va dans error_log(). Regarde le code source de la fonction insert() de la classe.
    Généralement, les erreurs de base de données sont les dernières qu'on remonte à la surface, il est préférable de les envoyer dans le fichier log.
    Mis à jour 21/03/2020 à 18h51 par rawsrc
  20. Avatar de laurentSc
    • |
    • permalink
    Super cette nouvelle classe, et preuve qu'un non-expert PHP peut l'utiliser : j'y suis arrivé

    Ayant démarré une application avec ta classe MyPDO (et ce que j'ai fait fonctionne), je ne pense pas essayer de la convertir (ne pas toucher ce qui marche !).
    J'ai néanmoins lu en entier ton billet (confirmation que je suis pas un expert, après avoir lu ton apparté) et faute de frappe dans ton code (vue en voulant tester) : l'insert avec valeurs préparées passées par valeur :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    $ppp  = new PPP(PPP::MODE_PREPARE_VALUES);
    $film = $data[1];
    $sql  = <<<sql
    INSERT INTO t_video (video_titre, video_support, video_multilingues, video_episode, video_annee, video_resume) VALUES (
        {$ppp($film['titre'], false)}, {$ppp($film['support'], false)}, {$ppp($film['multilngues'], 'bool', false)}, 
        {$ppp($film['episode'], 'int')}, {$ppp($film['annee'], 'int', false)}, {$ppp($film['resume'])}
    );
    sql;
    $new_id = $ppp->insert($sql);
    tu as écrit $film['multilngues']. Manque un i.

    Petite surprise : j'ai volontairement introduit une erreur dans la requête SQL, mais aucun message d'erreur affiché...Normal ou j'ai oublié de faire un truc ?
Page 1 sur 4 1234 DernièreDernière