Publié le 31 Janvier 2012

J'utilise souvent MySQL avec l'outil de commande en ligne: mysql, et naturellement j'ai souvent besoin de retrouver une commande précédente.

Si la commande est assez récente on peut naviguer dans l'historique avec les flèches haut et bas.

Si la commande est plus ancienne on peut également effectuer une recherche précise en tapant:

CTRL+R puis en entrant les caractères à rechercher

Sinon pour les puristes, MySQL maintient un fichier

.mysql_history (par défaut dans le 'home' de l'utilisateur)

avec la liste de toutes les commandes effectuées par l'utilisateur.

(il est possible de modifier le fichier utilisé en modifiant la variable MYSQL_HISTFILE).

Plus de détails ici: http://dev.mysql.com/doc/refman/5.5/en/mysql-history-file.html

Voir les commentaires

Rédigé par Bliz

Publié dans #MySQL

Repost 0

Publié le 27 Janvier 2012

Je travaille avec une table similaire à celle-ci:

+-------+---------------------+------+-----+---------+-------+
| Field | Type                | Null | Key | Default | Extra |
+-------+---------------------+------+-----+---------+-------+
| user  | varchar(40)         | NO   | PRI | NULL    |       |
| topic | varchar(40)         | NO   | PRI | NULL    |       |
| posts | tinyint(4) unsigned | NO   |     | NULL    |       |
+-------+---------------------+------+-----+---------+-------+

Elle contient la nombre de post effectué par chaque utilisateur dans un topic donné. Voici son contenu:

+-------+------------+-------+
| user  | topic      | posts |
+-------+------------+-------+
| Bliz  | Java       |    32 |
| Bliz  | MySQL      |    24 |
| Bliz  | Linux      |    12 |
| Bob   | Java       |     7 |
| Bob   | MySQL      |    56 |
| Bob   | Excel      |    28 |
| Brice | Java       |     2 |
| Brice | Excel      |     4 |
| Brice | Powerpoint |    36 |
+-------+------------+-------+

Je veux connaître la liste des utilisateurs ayant publié dans chaque topic (avec une ligne par topic). Pour ceci il faut utiliser la fonction MySQL GROUP_CONCAT:

SELECT topic, GROUP_CONCAT(user) AS users FROM topics GROUP BY topic;

et on obtient:

+------------+----------------+
| topic      | users          |
+------------+----------------+
| Excel      | Bob,Brice      |
| Java       | Bliz,Bob,Brice |
| Linux      | Bliz           |
| MySQL      | Bliz,Bob       |
| Powerpoint | Brice          |
+------------+----------------+

Cool ! Pour finir 2 petites astuces sur la fonction GROUP_CONCAT:

Pour changer le séparateur (par défaut ',') il faut le spécifier à l'aide du mot clé SEPARATOR et on peut également ordonner la liste des users à l'aide de la clause ORDER BY. On peut donc les afficher par quantité de posts avec la requête suivante:

SELECT topic, GROUP_CONCAT(user ORDER BY posts DESC SEPARATOR '>') AS users FROM topics GROUP BY topic;

+------------+----------------+
| topic      | users          |
+------------+----------------+
| Excel      | Bob>Brice      |
| Java       | Bliz>Bob>Brice |
| Linux      | Bliz           |
| MySQL      | Bob>Bliz       |
| Powerpoint | Brice          |
+------------+----------------+

Voir les commentaires

Rédigé par Bliz

Publié dans #MySQL

Repost 0

Publié le 26 Janvier 2012

Lors de la modification de la structure d'une table MySQL (ajout d'un champ par exemple) il arrive qu'on doive modifier également la clé primaire (par exemple pour inclure le nouveau champ dans la clé).

On peut y arriver en utilisant 2 commandes ALTER TABLE:

La première pour supprimer la clé primaire existente:

ALTER TABLE DROP PRIMARY KEY;

puis une deuxième pour créer la nouvelle clé:

ALTER TABLE ADD PRIMARY KEY (`col_1`, `col_2`, `col_3`);

Voir les commentaires

Rédigé par Bliz

Publié dans #MySQL

Repost 0

Publié le 25 Janvier 2012

Comme cela fait plusieurs fois que je rencontre la technique du double check sur mon projet et qu'à chaque fois elle est erronée j'ai décidé d'apporter un peu de clarification ici.

Tout d'abord la technique du double-check consiste à faire une double vérification pour éviter de rentrer dans un block synchronized à priori inutile:

public class Singleton{

   private static Singleton instance = null
   private static Object lock = new Object(); 

   public static Singleton getInstance(){
      if(instance == null) {
         synchronized(lock){
            if(instance == null) {
               instance = new Singleton();
            }
         }
      }
      return instance;
   }

   // autres méthodes 

Le double check (avant et après le bloc synchronized) permet d'éviter le synchronized si la variable a déjà été initialisée. Si l'instance est en cours de création par un premier thread elle peut être vu comme non nulle par un second thread alors qu'elle ne sera pas encore initialisée et donc on risque de rencontrer un comportement assez imprévisible.

Le problème comme souvent avec les threads c'est que ça peut marcher (et c'est même surement le cas la plus part du temps). Cependant il est possible que les optimisations de la JVM et un certain ordonnancement des threads entraîne une mauvaise publication de la variable instance.

Pour résoudre ce problème il faut déclarer la variable instance comme volatile:

private static volatile Singleton instance = null;

Le mot clé volatile indique que cette variable peut être visible par d'autre thread et donc la JVM s'assurera de l'initialisation complète avant de publier la référence.

L'accès à une variable volatile est un peu plus couteux que pour une variable classique (non volatile) mais ça reste tout de même plus performant qu'un accès synchronized.

Cette technique est souvent utilisé pour un singleton mais il existe d'autres techniques pour créer un singleton de manière sûre et sans utiliser de variable volatile ou de bloc synchronized.

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 23 Janvier 2012

Tout le monde connait la commande DELETE sous ça forme simple:

DELETE FROM ma_table WHERE mon_champ = 'ma_valeur';

Le problème c'est lorsqu'on ne connaît pas directement la valeur à mettre dans le WHERE, c'est à dire qu'on a besoin de faire une jointure sur une autre table pour déterminer les lignes à effacer.

Prenons un cas concret avec les 2 tables suivantes :

  • 'customers' qui contient une liste de client avec leur coordonnées
  • 'orders' qui contient une liste de commande

Le lien entre ces 2 tables s'effectue à l'aide de l'id du client.

Maintenant comment effacer tous les clients qui n'ont pas effectuer de commande. J'ai besoin d'une jointure entre les tables et bien c'est possible en utilisant USING:

DELETE FROM customers USING customers LEFT JOIN orders  ON (customers.id = orders.customer_id) WHERE orders.customer_id IS NULL;

ou

DELETE customers FROM customers LEFT JOIN orders  ON (customers.id = orders.customer_id) WHERE orders.customer_id IS NULL;

Voir les commentaires

Rédigé par Bliz

Publié dans #MySQL

Repost 0

Publié le 20 Janvier 2012

Je dois souvent enregistrer des données dans une base de données (ici MySQL) mais je ne sais pas forcément si ces données existent déjà en base.

Pourtant c'est cette information qui permet de choisir la requête à effectuer: INSERT ou UPDATE.

La solution la plus simple consiste à faire 2 requêtes:

Un SELECT pour vérifier l'existence des données puis un INSERT ou UPDATE en fonction du résultat de la première requête.

Mais ce n'est pas du tout optimum car on fait 2 requêtes au lieu d'une et je ne parle même pas du cas où on veut faire un multi-INSERT du genre:

INSERT INTO ma_table (a, b, c) VALUES (1, 2, 3), (4, 5, 6), ...

Heureusement MySQL a prévu le cas et nous fournit un magnifique: ON DUPLICATE KEY ce qui donne:

INSERT INTO ma_table (a, b, c) VALUES (1, 2, 3), (4, 5, 6) ON DUPLICATE KEY UPDATE c = VALUES(a)+VALUES(b);

Ici VALUES retourne la valeur du champ correspondant. Par exemple:

VALUES(a) retourne 1 pour (1,2,3) et 4 pour (4,5,6).

Voir les commentaires

Rédigé par Bliz

Publié dans #MySQL

Repost 0

Publié le 19 Janvier 2012

J'utilise SVN avec eclipse et j'aime bien voir le résultat des commandes SVN dans la console mais comme je suis un peu étourdi j'oublie toujours d'ouvrir la console avant de lancer une commande SVN.

Et bien il suffisait d'un petit réglage de propriétés dans Eclipse pour que la console s'ouvre automatiquement.

Il faut aller dans :

Window > Preferences > Team > SVN > Console

et sélectionner "On output" pour "Show console automatically"

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 17 Janvier 2012

find est une commande shell très puissante pour rechercher des fichiers mais souvent on veut effectuer une action sur les fichiers trouvés.

Par exemple on peut effectuer un "grep" pour ne retenir que les fichiers contenant une certaines chaînes de caractères:

find /var/log -name "monApp*.log" | xargs grep "ERROR"

pour n'afficher que les lignes d'erreurs dans tous les fichiers de log de monApp.

Ici on effectue un grep sur chaque fichier retourné à l'aide de xargs.

On peut également spécifier directement la commande à effectuer dans la commande find avec l'option -exec. Mais là la syntaxe est un peu particulière:

find /var/log -mtime +7 -type f -name "*.log" -exec rm -f {} \;

l'option -exec doit être la dernière car tout ce qui suit sera considéré comme faisant parti de la commande à exécuter.

les {} représente le fichier courant et la commande doit se terminer par \;

C'est pas très parlant au premier abord mais ça marche nickel.

Donc ici on efface tous les fichiers de logs de plus de 7 jours présent dans /var/log

Voir les commentaires

Rédigé par Bliz

Publié dans #Script

Repost 0

Publié le 11 Janvier 2012

La fonction FIND_IN_SET est l'inverse de la fonction SUBSTRING_INDEX. Elle permet de retourner la position d'un champ dans une chaîne csv.

Par exemple:

SELECT FIND_IN_SET('field3', 'field1,field2,field3,field4');

retourne

3.

Par contre contrairement à SUBSTRING_INDEX où on pouvait spécifier le séparateur à utiliser, FIND_IN_SET ne marche qu'avec le séparateur ','. Ce n'est pas trop grave car on peut toujours utiliser la fonction REPLACE pour remplacer un séparateur quelconque par une virgule.

On peut donc combiner FIND_IN_SET et SUBSTRING_INDEX pour interroger une chaine csv. Imaginons que le format csv est le suivant:

id,nom,prénom,code_postal,ville,téléphone,date_inscription

et que la table data contienne un champ csv qui respecte ce format on va pouvoir extraire tous les noms des données csv avec la requête suivante:

SELECT SUBSTRING_INDEX( SUBSTRING_INDEX(data.csv, ',', FIND_IN_SET('nom', 'id,nom,prénom,code_postal,ville,téléphone,date_inscription'), ',', -1) FROM data;

Par contre c'est un peu long à écrire mais on peut définir une fonction pour simplifier le code:

DELIMITER //
CREATE DEFINER=`root`@`localhost` FUNCTION `csv_value`(csv_value TEXT, field_name VARCHAR(32)) RETURNS VARCHAR(100)
BEGIN
   RETURN SUBSTRING_INDEX(SUBSTRING_INDEX(csv_value, ',', FIND_IN_SET(field_name, 'id,nom,prénom,code_postal,ville,téléphone,date_inscription')), ',', -1);
END
//
DELIMITER ;

et du coup la requête devient:

SELECT csv_value(data.csv, 'nom') FROM data;

Et on peut même l'utiliser dans un WHERE pour ne récupérer que les utilisateurs habitant à Paris:

SELECT csv_value(data.csv, 'nom') AS nom, csv_value(data.csv, 'prénom') AS prenom FROM data WHERE csv_value(data.csv, 'ville) LIKE 'Paris';

C'est presque comme ci on avait une vraie table SQL!

Voir les commentaires

Rédigé par Bliz

Publié dans #MySQL

Repost 0

Publié le 6 Janvier 2012

Dans une table mysql on stock un champ 'data' qui contient en fait des chaînes de caractères au format csv.

Jusque là tout va bien jusqu'au moment où on veut récupérer la valeur disons du 4ème champs du csv.

J'ai pensé dans un premier temps à bricoler un truc avec LIKE et des '%' mais j'ai trouvé la fonction SUBSTRING_INDEX de mysql qui s'utilise de la manière suivante:

SELECT SUBSTRING_INDEX('data1,data2,data3,data4,data5,data6', ',', 4);

et voici le résultat :

data1,data2,data3,data4

Oui c'est pas exactement ce que je voulais mais on s'en approche. En fait on récupère tout le début de la chaîne jusqu'au champ 4.

Reste plus qu'à effacer le début de la chaîne, et justement SUBSTRING_INDEX s'utilise aussi avec des index négatifs pour compter à partir de la fin de la chaîne et donc:

SELECT SUBSTRING_INDEX('data1,data2,data3,data4', ',', -1);

donne:

data4

Yes! Si on combine les 2 appels ça donne:

SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('data1,data2,data3,data4,data5,data6', ',', 4), ',', -1);

C'est un peu long à écrire mais on peut créer une fonction pour l'occasion afin de simplifier l'écriture des requêtes.

Voir les commentaires

Rédigé par Bliz

Publié dans #MySQL

Repost 0