coincoins

SQL.LMD - Les modifications avancées



Aller plus loin : INSERT INTO SELECT, UPDATE avec jointure, etc...

1. INSERT INTO spéciaux
Insérer dans une table en utilisant le résultat d'une requête, c'est possible ...

Simple et efficace pour faire des copies de tables ou des extractions

On va utiliser le résultat d'une requête pour insérer de nouveaux enregistrements dans une table existante.

INSERT INTO uneTable SELECT une_requete ;

Attention, les champs issus de la requête doivent correspondre aux champs de la table ou, du moins, à ceux indiqués derrière INSERT INTO uneTable.

Voir aussi ce lien vers w3Schools::insert into select

Mieux, utilisons SELECT ... INTO ... FROM !!!

Attention les yeux!! Si vous n'êtes pas prêt, ne lisez pas la suite, votre cerveau pourrait s'embrouiller.

SELECT ...            -- liste de champs ou *
  INTO nouvelleTable  -- nom de la nouvelle table
FROM Liste_de_tables  -- liste normale de tables, avec éventuellement des jointures
...                   -- suite normale de la requete SELECT
;

La table nouvelleTable est créée à cette occasion contrairement à INSERT INTO où la table doit déjà exister.

Voir aussi ce lien vers w3Schools::select into (c'est pas le même)

Exemple : faire un backup des commande de plus d'un an

SELECT * 
  INTO gc_commande_backup_2023-11-12 -- on pourrait mettre : CONCAT("gc_commande_backup_",CURDATE())
FROM gc_commande
WHERE dateCde < SUBDATE(CURDATE(), INTERVAL 1 YEAR)  ;

Voir les fonctions DATE_SUB/SUBDATE, CURDATE et CONCAT dans les opérateurs exotiques ou sur le net

2. Update de fous
Ou comment mettre à jour une table avec une contrainte sur une seconde table

1) Imagine ...

On voudrait faire une surprise au client 'Timken' et augmenter la quantité de tous ses articles de 1% pour toutes ses commandes du joli mois de mai de cette année ...
Comment faire ? La date ni le nom du client ne sont mentionnés dans la table contenir qui est à modifier !!!

Alors ??? hummm?? pourtant on n'a pas le droit de mettre plusieurs tables derrière UPDATE, n'est ce pas?.

Eh bien voilà la solution! : on utilise des jointures avec JOIN et le tour est joué
Oh! la belle requête ...

UPDATE gc_contenir
  JOIN gc_commande ON gc_contenir.numCde = gc_commande.numCde --  
  JOIN gc_client ON gc_client.id = idClient                   -- 
SET qty=qty*1.01
WHERE YEAR(dateCde) = YEAR(CURDATE()) AND MONTH(dateCde) = 05 AND nomCli = 'Timken'
;

ATTENTION, Il n'y a qu'une seule table qui est effectivement mise à jour : gc_contenir !!

Remarquer en passant l'usage des fonctions YEAR(), MONTH() et CURDATE(), c'est mieux qu'un between tordu.

2) Autre (mauvaise) surprise pour les clients

Comment augmenter de 3% le prix des 10 articles les plus commandés ?
(re)Oh! la (re)belle requête ... encore plus sioux et compliquée à souhait qu'avant.

Observer le nommage de la table produite par la sous requête SELECT intégrée : TMP_meilleureVente

UPDATE gc_article
  JOIN
  (
     SELECT refArt, sum(qty) FROM gc_contenir -- sum est optionnel car présent dans ORDER BY
     GROUP BY refArt
     ORDER BY sum(qty) DESC
     LIMIT 0, 10     -- pour se limiter aux 10 premières lignes
  ) TMP_meilleureVente   -- nommer la sous-requête intégrée
    ON TMP_meilleureVente.refArt = gc_article.refArt
SET prixHTArt = prixHTArt*1.03
;

Remarquer en passant la sous-requête utilisée comme une table et (re)nommée en TMP_meilleureVente ; LIMIT permet de n'avoir que les 10 premières lignes

3) Dernier cas (après on va prendre un truc pour la tête)

Et si je veux modifier le champ champA d'une table Table_A avec le contenu du champ champB d'une autre table Table_B ?
On admettra que les deux tables sont jointes par les champs clés (primaires ou secondaires) champJointA et champJointB.

Observer la modification du champA : SET Table_A.champA = Table_B.champB

UPDATE Table_A
 JOIN Table_B ON Table_B.champJointB=Table_A.champJointA
SET Table_A.champA = Table_B.champB
;

Vous suivez ?

4) Tiens ? non, encore un exemple (on est mûr pour le mur)

Admettons qu'il y ai un champ totalHT dans la commande, comment mettre ce champ à jour avec la somme des montants des articles?
TADAAAAA .... voilà la réponse (c'est pas minion, ça ?).

Observer : SET gc_commande.totalHT = TMP_Montants.montant

UPDATE gc_commande
 JOIN (
       SELECT cdeNum, sum(qty*prixHTArt) AS montant
       FROM gc_contenir JOIN gc_article ON gc_c.refAret = gc_article.refArt
       GROUP BY cdeNum
  ) TMP_Montants ON Montants.cdeNum = gc_commande.cdeNum
SET gc_commande.totalHT = TMP_Montants.montant
;

Y a quelqu'un ?    Ah? non !    Ils sont tous morts. ... dommage, je m'amusais bien, pourtant.
La prochaine fois, je le fais en alsacien ou en breton, ou en corse. yess    je suis le roi de la jointure