Instruction MERGE

Ça fait un petit moment maintenant que je vois des instructions de ce genre, dans des scripts de données :

IF EXISTS (SELECT * FROM Client WHERE Nom = 'Legrand' AND Prenom = 'Lucas')
	BEGIN
		PRINT 'UPDATE'
		UPDATE Client
		SET Email = 'lucas.legrand@live.com'
		WHERE Nom = 'Legrand' AND Prenom = 'Lucas'
	END
ELSE
	BEGIN
		PRINT 'INSERT'
		INSERT INTO Client
		VALUES ('Legrand', 'Lucas',
				'lucas.legrand@live.com',
				'107 rue de Lille, 75007 PARIS')
	END

Le gros problème, c’est que s’il y a beaucoup de données (surtout de références), ça va multiplier les blocs comme des petits pains (ce qui peut être sympa, mais ce n’est pas le sujet).

C’est là que le sauveur arrive, sous la forme de l’instruction MERGE.

Pour obtenir l’équivalent avec MERGE, on écrira :

MERGE Client AS target
USING (VALUES 
   ('lucas.legrand@live.com', 'Legrand', 'Lucas', '107 rue de Lille, 75007 PARIS'),
   ('julienne.jeremi@hotmail.fr', 'Jeremi', 'Julienne', '146 rue de Lyon, 75012 PARIS'))
	AS source (Email, Nom, Prenom, Adresse)
ON (target.Nom = source.Nom AND target.Prenom = source.Prenom)
WHEN MATCHED
	THEN UPDATE
		SET target.Email = source.Email
WHEN NOT MATCHED BY target
	THEN INSERT (Nom, Prenom, Email, Adresse)
	VALUES (source.Nom, source.Prenom, source.Email, source.Adresse);

-- Ou, en utilisant une structure (temporaire ou non) existante
	
CREATE TABLE #temp (
   Nom varchar(50) NULL,
   Prenom varchar(50) NULL,
   Email varchar(50) NULL,
   Adresse varchar(250) NULL)

INSERT INTO #temp
VALUES 
   ('lucas.legrand@live.com', 'Legrand', 'Lucas', '107 rue de Lille, 75007 PARIS'),
   ('julienne.jeremi@hotmail.fr', 'Jeremi', 'Julienne', '146 rue de Lyon, 75012 PARIS');
-- A noter : le ";" pour séparer les deux instructions
MERGE Client AS target
USING (SELECT Email, Nom, Prenom, Adresse FROM #temp) AS source
ON (target.Nom = source.Nom AND target.Prenom = source.Prenom)
WHEN MATCHED
	THEN UPDATE
		SET target.Email = source.Email
WHEN NOT MATCHED BY target
	THEN INSERT (Nom, Prenom, Email, Adresse)
	VALUES (source.Nom, source.Prenom, source.Email, source.Adresse);
	
DROP TABLE #temp

-- Fonctionne aussi avec les CTE
WITH clients AS (SELECT TOP 10 Nom, Prenom, Email, Adresse FROM Client)
MERGE Client AS target
USING (SELECT Email, Nom, Prenom, Adresse FROM clients) AS source
ON (target.Nom = source.Nom AND target.Prenom = source.Prenom)
WHEN MATCHED
	THEN UPDATE
		SET target.Email = source.Email
WHEN NOT MATCHED BY target
	THEN INSERT (Nom, Prenom, Email, Adresse)
	VALUES (source.Nom, source.Prenom, source.Email, source.Adresse);

Je ne vais pas détailler les trois blocs puisqu’ils font des choses équivalents, mais attirer l’attention sur le premier et en particulier les lignes 7, et 10.

Pour la ligne 6 : c’est une jointure tout ce qu’il y a de plus banal, comme celle que l’on peut réaliser avec l’instruction « JOIN » (ou « INNER JOIN », c’est la même chose).

Pour les lignes 7 et 10 : on peut voir qu’il est possible de réaliser des actions si les lignes correspondent à la jointure.

Ainsi, il est possible de faire des actions :

  • Si elles correspondent : « WHEN MATCHED », il est alors possible d’utiliser les instructions UPDATE ou DELETE
  • Si elles ne correspondent pas : « WHEN NOT MATCHED BY « , il est alors possible d’utiliser les instructions UPDATE et DELETE

Par défaut, l’action dans le « NOT MATCHED » s’applique à ce qui est spécifié après le « BY ».
Ainsi, il ne faut pas non plus le spécifier par la suite.

Note : dans le 2nd cas, il FAUT ajouter le point-virgule pour séparer l’instruction MERGE de la création/alimentation de la table temporaire.
Si ce n’est pas fait, vous aurez droit à une erreur :

Msg 325, Level 15, State 1, Line 6
Syntaxe incorrecte près de 'MERGE'. Vous devrez peut-être affecter au niveau de compatibilité de la base de données actuelle une valeur plus élevée pour activer cette fonctionnalité. Reportez-vous à l'aide relative à l'option SET COMPATIBILITY_LEVEL de ALTER DATABASE.
Msg 156, Level 15, State 1, Line 7
Syntaxe incorrecte vers le mot clé 'AS'.
Catégories:Développement, Sql Server

TechDays 2015

MS - TechDays 2015
Petit billet pour annoncer que les inscriptions mais surtout le détail des sessions est disponible (277 pour le moment).
C’est le 10, 11 et 12 Février, toujours au Palais des Congrès à Paris.

Comme à chaque fois, le plus dur va être de faire des choix ! XD

Le virage Microsoft vers l’Open Source se fait sentir (exemple : Keynote Opensource).

Bilan 2014

Tout d’abord, bonne année à tout le monde. Tout plein de bonnes choses, bonne santé, toussa toussa !
Et plein de projets sympas, aussi, c’est un bon moyen de passer une année bien.

Concernant ce blog, c’est la quatrième année.
Une année bien plus chargée que je ne le pensais.
L’avantage, c’est la reconnaissance au boulot. L’inconvénient, c’est d’être impliqué sur plus de choses.
Autant c’est intéressant, autant parfois, c’est épuisant (navigation entre projets, sujets abordés rapidement…).

Voici, comme chaque année, un petit bilan de ce blog.

Lire la suite…

Catégories:Divers

[C#] Utiliser des tables temporaires avec Entity Framework

Ça fait maintenant quelques temps que je travaille avec Entity Framework et, s’il est très bien, il pêche majoritairement sur un point (dans le cadre de mon usage) : le traitement du volume.
Donc, dans ce billet, je vais présenter une petite solution montrant ce que j’ai fait pour combler ce problème.

Lire la suite…

Catégories:.Net, C#, Développement

FDD Night – Vidéos

Fier d'être développeurAprès trois semaines de vacances, après une semaine (et c’est pas fini) d’optimisations de performances (je tenterais d’en reparler, si j’arrive à organiser le truc !), voici un premier petit billet pour se remettre en selle.

Donc, on commence par relayer les différentes vidéos de la FDD Night.

Et du coup, on pourra aller aussi, directement, sur Youtube, pour revoir toutes les vidéos.

FDD Night

Fier d'être développeur Le 07/10/2014, il y a eut un événement de l’association Fier d’Être Développeur : FDD Night le 7 octobre à 18h15.
J’y était, en voici un petit retour.

Mais avant, deux choses à noter : j’ai commencé le billet durant l’événement, même s’il y a références/relecture/reformulation (du genre pour faire des phrases, c’est bien les phrases), ça reste du « à chaud ». Ensuite, l’application WordPress sur WP8 m’a lâchement lâchée après 335 mots. Après, je ne pouvais plus scroller pour afficher le reste. #tristesse #déception #désappointement comme on dit.

Lire la suite…

Entity Framework et NULL

Décidément, y a des fois où j’ai pas de bol :)

Le scénario est assez simple : j’ai une méthode qui doit aller chercher une ou plusieurs entrées en base de données.
Le critère de recherche est sur un champ nullable.

(Note : c’est la même table que là : ADO.Net Entity Data Model et champs avec valeur par défaut)

Du genre :

public IEnumerable<MATABLE> GetValue(SAMPLESEntities context, string search)
{
    return context.MATABLE.Where(m => m.COLONNE2 == search);
}

Jusque là, rien de bien gênant.
Mais…est si la variable search est nulle ???

Voici la requête exécutée par EF dans le cas de la méthode ci-dessus :

exec sp_executesql N'SELECT 
   [Extent1].[PK] AS [PK], 
   [Extent1].[COLONNE1] AS [COLONNE1], 
   [Extent1].[COLONNE2] AS [COLONNE2], 
   [Extent1].[COLONNE3] AS [COLONNE3], 
   [Extent1].[CREATION] AS [CREATION]
   FROM [dbo].[MATABLE] AS [Extent1]
   WHERE [Extent1].[COLONNE2] = @p__linq__0',
N'@p__linq__0 varchar(8000)',@p__linq__0=NULL

Et voici la requête exécutée si on fait

context.MATABLE.Where(m => m.COLONNE2 == null);
SELECT 
   [Extent1].[PK] AS [PK], 
   [Extent1].[COLONNE1] AS [COLONNE1], 
   [Extent1].[COLONNE2] AS [COLONNE2], 
   [Extent1].[COLONNE3] AS [COLONNE3], 
   [Extent1].[CREATION] AS [CREATION]
   FROM [dbo].[MATABLE] AS [Extent1]
   WHERE [Extent1].[COLONNE2] IS NULL

La différence est notable, non ?
Pourquoi ?
Parce que !
…NULL n’est pas une valeur ! (et c’est pas nouveau ^^)

Il faut donc gérer dans le code C# la possibilité que la variable soit nulle.
Voici un billet bien foutu qui en décrit : NULL value handling in Entity Framework.

A noter, tout de même que l’article parle d’un context (SAMPLESEntities, dans l’exemple) héritant de DbContext. Si le votre hérite de ObjectContext, eh bah c’est mort.
Dans ce cas, il faudra tester la valeur à la main pour réaliser le conditionnement qui va bien.

Un truc du genre :

return string.IsNullOrEmpty(search)
    ? context.MATABLE.Where(m => m.COLONNE2 == null)
    : context.MATABLE.Where(m => m.COLONNE2 == search);
Catégories:.Net, C#, Développement
Suivre

Recevez les nouvelles publications par mail.

Rejoignez 60 autres abonnés