Partage
  • Partager sur Facebook
  • Partager sur Twitter

Insert where not exists else update ??

Toussa en SQL :o

Anonyme
    14 janvier 2019 à 22:58:13

    Bonjour à tous ! Et merci par avance et pour l'aide que vous m'avez déjà apportée :)


    J'ai crée un modèle de cure. Un cure peut avoir plusieurs étapes.
    Les cures sont stockés dans la table cure, et les étapes dans la table cure_etape (rien de fou^^).

    Je procède actuellement ainsi pour ajouter une étape :
    Si on est en train de modifier une étape (présence du get), on update, sinon on insert.

    if(isset($_GET['etape']) AND !empty($_GET['etape'])) {//Si on édite une étape On update
    	$query="UPDATE cure_etape 
    	LEFT JOIN cure ON cure_etape.id_cure=cure.id
    	SET cure_etape.etape=".mysqli_real_escape_string($bdd,intval($_POST['etape'])).",cure_etape.nom='".mysqli_real_escape_string($bdd,$_POST['nom'])."',cure_etape.description='".mysqli_real_escape_string($bdd,$_POST['description'])."' 
    	WHERE statut=0 AND id_membreo=".$_SESSION['session_id']." AND id_cure=".$id_cure." AND etape=".intval($_GET['etape']);
    	$msg='L\'étape du programme a été actualisé avec succès.';
    } else {
    	//Sinon on insert l'étape seulement si cette cure n'a pas déjà cette étape
    	//Et dans ce cas en fait il faudrait faire un update ou renvoyer une valeur qui permettrait d'afficher un message d'erreur.
    	$query="INSERT INTO cure_etape VALUES(".$id_cure.",".mysqli_real_escape_string($bdd,intval($_POST['etape'])).",'".mysqli_real_escape_string($bdd,$_POST['nom'])."','".mysqli_real_escape_string($bdd,$_POST['description'])."')";
    	$msg='Vous venez d\'ajouter une étape !';
    }

    Le soucis, voyez-vous, est évident, si je rentre deux étapes 3, conflit de canard, doublon, toussa ! :o
    Donc je me suis dit, sur mon INSERT, je vais faire en sorte de vérifier si une cure n'a pas déjà cette étape de pré-entrée avant de faire un insert.


    Alors j'ai essayé ça :

    INSERT INTO cure_etape VALUES(".$id_cure.",".mysqli_real_escape_string($bdd,intval($_POST['etape'])).",'".mysqli_real_escape_string($bdd,$_POST['nom'])."','".mysqli_real_escape_string($bdd,$_POST['description'])."') 
    WHERE NOT EXISTS (SELECT id_cure, etape FROM cure_etape WHERE cure_etape.id_cure=".$id_cure." AND cure_etape.id_etape=".intval($_POST['etape']).")

    Mais même si théoriquement ça marche (genre dans ma tête c'est cohérent), le mur de la réalité, rien de surprenant : Erreur de syntaxe près de 'WHERE NOT EXISTS (SELECT id_cure, etape FROM cure_etape WHERE cure_etape.id_cure' à la ligne 2

    Bon, mais alors que même cette idée ne marche pas, je me dis que ça se trouve, je peux diminuer mes conditions en php et faire une vérification qui permettrait de faire un update ou un insert en fonction de la présence de ces champs dans ma table !!!
    Et là ! là... Je me dis que j'ai besoin de vous pour m'expliquer comment écrire cette requête x)..

    Je vous avoue ne pas être très à l'aise avec les sous-requêtes, je pense en avoir besoin ici mais j'aurais besoin de vos lumières :)

    Merci encore et bonne soirée !

    -
    Edité par Anonyme 14 janvier 2019 à 22:59:39

    • Partager sur Facebook
    • Partager sur Twitter
      15 janvier 2019 à 10:50:57

      Bonjour,

      Côté PHP, !empty est redondant avec isset ... empty suffit ;)

      harvox a écrit:

      faire un update ou un insert en fonction de la présence de ces champs dans ma table

      Oui, avec MySQL tu peux faire INSERT INTO ... ON DUPLICATE KEY UPDATE ...

      Il suffit juste que tu définisses clairement ce qu'est une DUPLICATE KEY, donc que la table cure_etape dispose de contraintes suffisantes.

      Peux-tu nous décrire un peu plus en détail ton modèle (tables, colonnes, clés, contraintes) et ce qui pour toi est un doublon d'étape ?

      • Partager sur Facebook
      • Partager sur Twitter
      Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
      Anonyme
        15 janvier 2019 à 11:08:58

        Yes =)

        Dans ma table 'cure_etape' je veux insérer id_cure, etape, nom, description.

        S'il y a déjà une entrée avec id_cure=x et etape=y (dans la même entrée) alors on update nom et description, sinon on insert la nouvelle entrée.

        Donc si dans ma table j'ai id_cure=1 et id_etape=1 et que j'essaye d'entrer un nom et description sur l'étape de cette cure (qui existe déjà donc), il faudrait que ça l'update :)

        Merci !

        -
        Edité par Anonyme 15 janvier 2019 à 11:32:36

        • Partager sur Facebook
        • Partager sur Twitter
          15 janvier 2019 à 11:34:19

          Donc il te faut mettre une contrainte UNIQUE( id_cure, id_etape ) sur la table cure_etape.

          Puis la requête sera :

          INSERT INTO cure_etape ( id_cure, id_etape, nom, description )
          VALUES( ... )
          ON DUPLICATE KEY UPDATE
          	nom = ...,
          	prenom = ...

          Si le couple id_cure/ id_etape existe déjà, MySQL levera une erreur "Duplicate entry" et exécutera l'UPDATE.

          Mais je suis un peu dubitatif sur ton modèle ...

          Benzouye a écrit:

          Peux-tu nous décrire un peu plus en détail ton modèle (tables, colonnes, clés, contraintes)

          -
          Edité par Benzouye 15 janvier 2019 à 11:36:20

          • Partager sur Facebook
          • Partager sur Twitter
          Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
          Anonyme
            15 janvier 2019 à 13:06:11

            Je pense que je vois où tu veux en venir quand tu dis être dubitatif.
            Je pense que c'est mon WHERE qui t'as fais dire ça.

            J'ai ajouté les clés primaires sur id_cure et etape de la table cure_etape, ainsi qu'une clé unique sur chacun de ces deux champs.
            Maintenant, j'aimerais faire une sous-requête dans mon where pour aller vérifier si la cure qu'il veut modifier n'est pas une cure publique WHERE SELECT  statut=0 (non validé) AND id_membreo=".$_SESSION['session_id']" (c'est moi qui l'ai fait ! xD)

            J'ai donc testé ceci :
            INSERT INTO cure_etape(id_cure, etape, nom, description) 
            	VALUES(".$id_cure.",".$id_etape.",'".mysqli_real_escape_string($bdd,$_POST['nom'])."','".mysqli_real_escape_string($bdd,$_POST['description'])."')
            	ON DUPLICATE KEY UPDATE
            	nom='".mysqli_real_escape_string($bdd,$_POST['nom'])."',
            	description='".mysqli_real_escape_string($bdd,$_POST['description'])."'
            WHERE (SELECT id, statut, id_membreo FROM cure WHERE statut=0 AND id=".$id_cure." AND id_membreo=".$_SESSION['session_id'].")

            Mais mais mais... ma sous-requête foire :s

            Erreur de syntaxe près de 'WHERE (SELECT statut, id_membreo FROM cure WHERE statut=0 AND id_membreo=1)' à la ligne 6

            -
            Edité par Anonyme 15 janvier 2019 à 13:08:21

            • Partager sur Facebook
            • Partager sur Twitter
              15 janvier 2019 à 13:33:38

              Une requête INSERT INTO ne prend jamais de clause WHERE ...

              harvox a écrit:

              J'ai ajouté les clés primaires sur id_cure et etape de la table cure_etape, ainsi qu'une clé unique sur chacun de ces deux champs

              Non ... Il ne peut y avoir qu'une seule clé primaire par table, elle peut être posée sur une colonne (clé simple) ou plusieurs (clé composite).

              Par ailleurs, pour fonctionner comme présenté plus haut, l'index UNIQUE doit être composite, c'est à dire posé sur deux colonnes à la fois et pas deux index UNIQUE différents (un par colonne).

              Après si ta table cure_etape n'avait pas de clé primaire (ce qui laissait dubitatif), alors une clé primaire composite suffit :

              CREATE TABLE cure_etape (
              	id_cure INT UNSIGNED NOT NULL,
              	id_etape INT UNSIGNED NOT NULL,
              	nom VARCHAR(60) NOT NULL,
              	description TEXT,
              	PRIMARY KEY ( id_cure, id_etape )
              );
              
              INSERT INTO cure_etape ( id_cure, id_etape, nom, description )
              VALUES ( 1, 1, 'Test', 'Une étape de test' )
              ON DUPLICATE KEY UPDATE
              	nom = 'Pas dupliquée',
              	description = 'Une étape non modifiée';

              Avec cet exemple, la table est créée, vide, et on insère un premier couple cure 1/étape 1 dans la base. La table contient donc une ligne et le UPDATE n'est pas exécuté.

              Maintenant on rajoute :

              INSERT INTO cure_etape ( id_cure, id_etape, nom, description )
              VALUES ( 1, 1, 'Test modifié', 'Une étape de test modifiée' )
              ON DUPLICATE KEY UPDATE
              	nom = 'Test modifié',
              	description = 'Une étape de test modifiée';

              Le couple cure 1 / étape 1 existe déjà, l'UPDATE est exécuté et le mot "modifié" apparaît dans le nom est la description.

              harvox a écrit:

              vérifier si la cure qu'il veut modifier n'est pas une cure publique WHERE SELECT  statut=0 (non validé) AND id_membreo=".$_SESSION['session_id']"

              Ah oui ... là ça complique les choses ... je suppose que les colonnes statut et id_membreo sont dans la table cure ?

              Et si l'on est dans ce cas (statut = 0 et id_membre = x ) on fait quoi ? On insère/met à jour sinon non ?

              -
              Edité par Benzouye 15 janvier 2019 à 13:35:26

              • Partager sur Facebook
              • Partager sur Twitter
              Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
              Anonyme
                15 janvier 2019 à 14:30:40

                Merci, je comprend :)

                "Ah oui ... là ça complique les choses ... je suppose que les colonnes statut et id_membreo sont dans la table cure ?"


                Tout à fait :(

                "Et si l'on est dans ce cas (statut = 0 et id_membre = x ) on fait quoi ? On insère/met à jour sinon non ?"
                Dans ce cas on ne fait rien du tout, ça voudrait dire que le membre essaye de modifier une cure qui appartient à un autre membre.
                • Partager sur Facebook
                • Partager sur Twitter
                  15 janvier 2019 à 15:03:05

                  harvox a écrit:

                  vérifier si la cure qu'il veut modifier n'est pas une cure publique WHERE SELECT  statut=0 (non validé) AND id_membreo=".$_SESSION['session_id']"

                  Cette règle de gestion peut être gérée côté applicatif ou côté base de données.

                  Côté applicatif, cela signifie que l'on ne propose pas à l'utilisateur de modifier une cure validée ou qui n'est pas à lui ... dans un premier temps sur l'écran qui permet de modifier les cures et dans un second temps dans le code qui exécute la requête de mise à jour.

                  Côté SQL, cela signifie que l'on met un TRIGGER en place qui contrôle avant l'insertion (ou de la mise à jour) que l'utilisateur est autorisé et de lever une erreur le cas échéant. Cela implique par contre de passer l'id de l'utilisateur lors de l'insertion (ou de la mise à jour).

                  Je suis plutôt partisan de la deuxième solution, mais cela implique je pense une adaptation du modèle de données, que tu ne nous a pas communiqué :p

                  -
                  Edité par Benzouye 15 janvier 2019 à 15:05:02

                  • Partager sur Facebook
                  • Partager sur Twitter
                  Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
                    15 janvier 2019 à 17:37:08

                    Et la, la magie des MERGE de SQL Server apparaît ^^

                    • Partager sur Facebook
                    • Partager sur Twitter
                      15 janvier 2019 à 17:40:06

                      Tu peux détailler Tiffado ? Je suis curieux :p

                      • Partager sur Facebook
                      • Partager sur Twitter
                      Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
                        15 janvier 2019 à 18:05:08

                        J'ai pas lu tout le topic, mais pas rapport à la question initiale, un MERGE fait parfaitement le taff :

                        MERGE INTO table1 AS TARGET
                          USING table_reference AS SOURCE
                          ON (conditions)
                          WHEN MATCHED THEN
                            UPDATE SET table1.colonne1 = valeur1, table1.colonne2 = valeur2
                            DELETE WHERE conditions2
                          WHEN NOT MATCHED THEN
                            INSERT (colonnes1, colonne3) 
                            VALUES (valeur1, valeur3)

                        Avec la Table 1 étant la table que tu veux modifier, table_reference qui sont tes données que tu veux insérer/modifier.

                        Tu poses des conditions dans le ON, comme avec une simple jointure.

                        Et ensuite tu fais des matchs pour savoir dans quel cas tu te situes. avec 3 cas possibles je crois.

                        WHEN MATCHED THEN

                        => Quand le jointure existe, donc la donnée existe => UPDATE dans ton cas.

                        WHEN NOT MATCHED BY TARGET

                        => Condition non respecté car la table cible (table1) n'a pas la donnée => INSERT dans ton cas.

                        WHEN NOT MATCHED BY SOURCE

                        => Condition non respectée car la source (les données à insérer) n'a pas la donnée présente dans TARGET => Dans le cas ici, on ne fait rien. Mais c'est souvent utilisé quand on veut faire la fusion de 2 tables complètes, on peut par exemple faire un DELETE dans ce cas présent.

                        EDIT : En fait je confond toujours le BY SOURCE et le BY TARGET, donc il faut potentiellement inverser les 2.

                        On peut aussi simplement faire un WHEN NOT MATCHED

                        => Et dans ce cas, ca veut juste dire que la condition n'est pas respectée. Quand on ne doit pas faire la différence entre les 2 "NOT MATCHED", on se sert de ça.

                        La grosse difficulté ici serait d'avoir une table SOURCE (table_reference) en fait. Mais bon, une petite table temporaire faite rapidement avec toutes les données que tu veux INSERT/UPDATE, et le tour est joué, on l'a notre table.

                        Une page avec un exemple plus parlant avec des vrais données

                        https://mcherif.wordpress.com/2013/09/12/sql-server-t-sql-les-joies-de-linstruction-merge-ou-comment-effectuer-des-operations-mixtes-en-un-temps-deux-mouvements/

                        -
                        Edité par Tiffado 15 janvier 2019 à 18:11:41

                        • Partager sur Facebook
                        • Partager sur Twitter
                          15 janvier 2019 à 18:10:17

                          Merci !
                          • Partager sur Facebook
                          • Partager sur Twitter
                          Seul on va plus vite, ensemble on va plus loin ... A maîtriser : Conception BDD, MySQL, PHP/MySQL
                          Anonyme
                            17 janvier 2019 à 10:50:56

                            Salut !
                            Je ne connaissais pas du tout cette syntaxe, elle pourrait m'être pratique, merci beaucoup !
                            Cependant je n'arrive pas à la faire fonctionner, voici ma requête

                            MERGE cure_etape AS target
                            	  USING cure AS source ON (target.id_cure=source.id)
                            	  WHEN MATCHED THEN
                            	UPDATE SET target.etape = ".$id_etape.", target.nom = '".mysqli_real_escape_string($bdd,$_POST['nom'])."',
                            	target.description='".mysqli_real_escape_string($bdd,$_POST['description'])."'
                            	  WHEN NOT MATCHED THEN
                            	INSERT (id_cure, etape, nom, description)
                            	VALUES (".$id_cure.", ".$id_etape.",'".mysqli_real_escape_string($bdd,$_POST['nom'])."','".mysqli_real_escape_string($bdd,$_POST['description'])."')

                            J'obtiens ue erreur de syntaxe à la ligne 1 :x

                            Erreur de syntaxe près de 'MERGE cure_etape AS target USING cure AS source ON (target.id_cur' à la ligne 1

                            Et où suis-je censé entrer ma clause where : WHERE (SELECT id, statut, id_membreo FROM cure WHERE id=".$id_cure." AND statut=0 AND id_membreo=".$_SESSION['session_id'].") ?
                            J'ai pensé le mettre dans la condition de départ comme ceci :

                            MERGE cure_etape AS target
                            	  USING cure AS source ON (target.id_cure=source.id AND source.id=".$id_cure." AND source.statut=0 AND id_membreo=".$_SESSION['session_id'].")
                            	 WHEN MATCHED THEN
                            	UPDATE SET target.etape = ".$id_etape.", target.nom = '".mysqli_real_escape_string($bdd,$_POST['nom'])."',
                            		target.description='".mysqli_real_escape_string($bdd,$_POST['description'])."'
                            	 WHEN NOT MATCHED THEN
                            	INSERT (id_cure, etape, nom, description)
                            	VALUES (".$id_cure.", ".$id_etape.",'".mysqli_real_escape_string($bdd,$_POST['nom'])."','".mysqli_real_escape_string($bdd,$_POST['description'])."')



                            Merci encore !

                            -
                            Edité par Anonyme 17 janvier 2019 à 10:57:59

                            • Partager sur Facebook
                            • Partager sur Twitter
                              17 janvier 2019 à 11:26:07

                              Et bien, le soucis de MERGE, c'est que ca ne fonctionne que sous SQL Server (cf mon 1er message), ca ne passe pas sous els autres sgbd.
                              • Partager sur Facebook
                              • Partager sur Twitter
                              Anonyme
                                17 janvier 2019 à 11:49:54

                                Yes, tout s'explique alors haha, merci :)
                                Dommage, ça me semblait bien utile comme fonctionnalité.

                                -
                                Edité par Anonyme 17 janvier 2019 à 15:54:30

                                • Partager sur Facebook
                                • Partager sur Twitter

                                Insert where not exists else update ??

                                × 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