Outils pour utilisateurs

Outils du site


basededonnees:mysql

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
basededonnees:mysql [25/12/2014 21:16]
julp [Convertir une colonne VARCHAR pour dates au format fr (d/m/Y) en DATE]
basededonnees:mysql [22/03/2017 17:19] (Version actuelle)
julp
Ligne 39: Ligne 39:
 )</code> )</code>
 C'est l'"équivalent" PHP de : C'est l'"équivalent" PHP de :
-<code php>$out = str_replace(array('FT', 'PV', 'NF'), array('Fiche Technique', 'Procès Verbal', 'Norme'), $in);</code>+<code php>$out = str_replace(array('FT', 'PV', 'NF'), array('Fiche Technique', 'Procès Verbal', 'Norme'), $in)
 +# ou, mieux : 
 +$out = ['FT' => 'Fiche Technique', 'Procès Verbal', 'NF' => 'Norme'][$in];</code>
 Par contre : ça revient éventuellement à "réinventer" le type ENUM Par contre : ça revient éventuellement à "réinventer" le type ENUM
  
Ligne 52: Ligne 54:
 ====== Les timezones et comment régler l'heure ====== ====== Les timezones et comment régler l'heure ======
  
-Sachez tout d'abord que MySQL dispose de deux types différents par rapport aux date/heure : +Déplacé sur [[http://www.julp.fr/blog/posts/5-les-timezones-et-comment-regler-l-heure]]
-  * TIMESTAMP +
-  * DATETIME (comprend DATE et TIME) +
- +
-Ils diffèrent par ce qu'ils impliquent : +
-  * le type TIMESTAMP est surtout destiné à son initialisation automatique (INSERT) et à sa mise à jour automatique lorsqu'une modification est apportée à la ligne (UPDATE) +
-  * le type DATETIME est bien moins limité (de '1000-01-01 00:00:00' à '9999-12-31 23:59:59') que TIMESTAMP (de '1970-01-01 00:00:01' UTC à '2038-01-19 03:14:07' UTC) +
-  * le format TIMESTAMP est convertie de la timezone MySQL courante vers UTC à l'insertion et, inversement, de UTC à la timezone en cours, au SELECT. À l'inverse, le format DATETIME est figé : il correspond à une date/heure pour une timezone donnée (vue qu'elle n'est pas associée à la date, cette information est perdue par la suite au niveau de MySQL) : le DATETIME restera tel qu'il est, à l'INSERT comme au SELECT, il n'y a aucune conversion interne d'une timezone à une autre (UTC). Cependant, notez qu'au besoin, vous pouvez faire vous-mêmes les conversions de timezone via la fonction [[http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_convert-tz|CONVERT_TZ]] qu'intègre MySQL. +
- +
-Après cette brève présentation des types date/heure, il est possible de modifier à la volée la timezone courante au lieu d'hériter de celle par défaut (appelée //SYSTEM//). Pour ce faire, il faut modifier au niveau session la variable nommée ... //time_zone// par une commande SET. Mais, problème, par défaut, vous ne pouvez assigner à cette variable qu'un décalage +/-heure:minute (exemple ''%%set time_zone = '+2:00';%%'') et non utiliser directement des noms comme Europe/Paris à moins d'importer les timezones au sein de MySQL. Autant dire que c'est gênant pour deux raisons : +
-  * les importer prend peu de temps et de connaissances mais encore faut-il avoir les accès requis (en clair, sur du mutualisé, c'est tout bonnement impossible) +
-  * pour travailler sur les timezones au niveau de MySQL, c'est obligatoire. Comment, sinon, gérez les heures d'été et d'hiver ? Si ces dates sont importantes et pour beaucoup de trafic, vous vous connectez dans la nuit de ces changements d'heures pour apporter la modification dans vos sources ? Non, c'est ingérable. +
- +
-Donc, pour importer ces fameuses timezones : +
-  * sous Unixoïdes : +
-    Importer les données de votre propre système en entrant la commande : ''%%mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql%%'' +
-    - Relancer MySQL +
-  * sous Windows : +
-    - Arrêter le serveur MySQL s'il est lancé +
-    - Se rendre sur http://dev.mysql.com/downloads/timezones.html +
-    - Télécharger le fichier qui correspond au lien //POSIX standard Time zone description tables, version 20XXn// +
-    - Dézipper l'archive obtenue dans le sous-répertoire mysql où sont entreposées vos bases de données (voir la valeur de la directive //datadir// de votre fichier //my.ini// - ces tables existent déjà, écrasez-les après les avoir préalablement copiées, sait-on jamais) +
-    - Redémarrer MySQL +
- +
-Tester que tout fonctionne : +
-<code>mysql> set time_zone = 'Europe/London'; +
-Query OK, 0 rows affected (0.00 sec) +
- +
-mysql> select now(); +
-+---------------------+ +
-| now()               | +
-+---------------------+ +
-| 2013-07-26 15:57:20 | +
-+---------------------+ +
-1 row in set (0.00 sec) +
- +
-mysql> set time_zone = 'Europe/Paris'; +
-Query OK, 0 rows affected (0.00 sec) +
- +
-mysql> select now(); +
-+---------------------+ +
-| now()               | +
-+---------------------+ +
-| 2013-07-26 16:57:20 | +
-+---------------------+ +
-1 row in set (0.00 sec)</code> +
- +
-Quant à ceux qui ne peuvent que subir, tout ce que vous pouvez faire, c'est remplacer vos NOW() et autres fonctions MySQL par celles de votre langage. Par exemple, pour PHP, qui embarque toutes ces informations de fuseaux horaire, il suffira de faire un préalable ''%%date_default_timezone_set('Europe/Paris');%%'' en début de code et de remplacer les NOW() de vos requêtes par ''%%date('Y-m-d H:i:s')%%'' ou autre DateTime.+
  
 ====== Optimisation : éviter des SELECT inutiles ====== ====== Optimisation : éviter des SELECT inutiles ======
  
-===== Insérer ou ne rien faire si l'élément existe déjà ===== +Déplacé sur [[http://www.julp.fr/blog/posts/6-optimisation-eviter-des-select-inutiles]]
- +
-Si vous avez un élément "unique" et qu'il est identifié comme tel au niveau de MySQL par une clé primaire ou une contrainte unique (UNIQUE KEY) alors vous pouvez utiliser une requête de type INSERT IGNORE. +
- +
-Pour l'exemple, prenons le cas d'une table d'utilisateurs minimaliste, avec id (clé primaire) plus login qui doit être unique : +
-<code sql>CREATE TABLE `users`( +
-    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, +
-    `login` VARCHAR(80) NOT NULL, -- CHARACTER SET '' COLLATE 'utf8_unicode_ci', +
-    PRIMARY KEY(`id`), +
-    UNIQUE KEY(`login`) +
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;</code> +
- +
-Comment s'assurer à l'inscription d'un nouvel utilisateur que le login n'est pas déjà pris par quelqu'un d'autre ? C'est simple, vous tentez de faire silencieusement l'insertion et vous regardez ensuite si une ligne a été insérée ou non (mysql(i) : mysql(i)_affected_rows, pdo : PDO::rowCount) : +
- +
-<code php><?php +
-$bdd = new PDO(/* ... */); +
- +
-$insert = $bdd->prepare('INSERT IGNORE INTO users(login) VALUES(?)'); +
-$insert->execute(array($_POST['login'])); +
- +
-if ($insert->rowCount()) { +
-    // ok, on a un nouvel inscrit +
-} else { +
-    // erreur : le login est déjà utilisé +
-+
-</code> +
- +
-(le code est en PHP mais le principe peut être adapté à tout langage) +
- +
-IGNORE n'est qu'une option pour que lorsque l'on tente d'insérer un "doublon", la requête n'échoue pas. Elle est destinée à considérer qu'une tentative d'insertion de doublon est un cas prévu par notre programme et non une erreur (vous en obtiendriez une sans IGNORE). +
- +
-Par contre, cette solution ne convient pas si vous avez plusieurs contraintes d'unicité et que vous voulez savoir laquelle a empêché l'insertion. En effet, si vous voulez notamment indiquer à l'utilisateur si c'est l'adresse email ou le login qui est déjà pris, vous devriez faire un SELECT normal par contrainte unique. Je dis "devriez" parce qu'il est éventuellement possible, pour un INSERT sans l'option IGNORE, de parser le message d'erreur où la colonne ou contrainte ou index empêchant l'insertion est mentionné, solution, qui, à mon sens, n'est pas très viable (et encore moins portable). +
- +
-===== Insérer si inexistant sinon mettre à jour ===== +
- +
-Comme ci-dessus, si chaque ligne peut être identifiée de manière unique par la valeur d'une ou plusieurs colonnes, définies comme clé primaire (PRIMARY KEY) ou comme clé unique (UNIQUE KEY), alors il est possible de réaliser cette opération //insertion sinon mise à jour// en une seule requête de type INSERT ... ON DUPLICATE KEY UPDATE au lieu d'avoir à recourir à cet "algo" traditionnel : +
-<code sql>si SELECT COUNT(*) FROM ... WHERE colonne_unique = valeur renvoie une valeur non nulle (<> 0) +
-    alors UPDATE +
-    sinon INSERT</code> +
- +
-Pour illustrer, prenons une situation courante, réaliser un import, ici de joueurs, via un flux XML fourni par la fédération. Tout d'abord notre table, volontairement sommaire, joueurs : +
-<code sql>CREATE TABLE `joueurs`( +
-    `licence` VARCHAR(80) NOT NULL, +
-    `nom` VARCHAR(80) NOT NULL, +
-    `prenom` VARCHAR(80) NOT NULL, +
-    -- ... +
-    PRIMARY KEY(`licence`) +
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;</code> +
- +
-On reconnaît un joueur d'un autre via son numéro de licence, unique. Le but, en parsant ce flux, est double : +
-  - ajouter les nouveaux joueurs à notre base +
-  - mettre à jour éventuellement les informations personnelles de ceux que nous avions (changement d'adresse, classement, etc) +
-  - (on ne gère pas les retraits de licence - DELETE) +
- +
-<code php>$sxml = simplexml_load_file('http://.../joueurs.xml'); +
- +
-$bdd = new PDO(/* ... */); +
-$iou = $bdd->prepare('INSERT INTO joueurs(licence, nom, prenom) VALUES(:licence, :nom, :prenom) ON DUPLICATE KEY UPDATE nom = :nom, prenom = :prenom'); +
-$iou->bindParam('nom', $j->nom/*, PDO::PARAM_STR*/); +
-$iou->bindParam('prenom', $j->prenom/*, PDO::PARAM_STR*/); +
- +
-foreach ($sxml->joueur as $j) { +
-    $iou->execute(); +
-}</code> +
- +
-Encore une fois, c'est codé en PHP mais parfaitement transposable à tout langage puisque c'est la requête SQL qui compte.+
  
basededonnees/mysql.1419538611.txt.gz · Dernière modification: 25/12/2014 21:16 de julp