Aujourd’hui petite précision sur les IO (vous savez ce qui préocupe les DBAs toute la sainte journée Smile)

Il existe deux types d’accés :

  1. Physical IO : Accès au disque pour récupérer les pages de données.
    • Une fois récupérée la page est placée dans le Buffer Cache
  2. Logical IO : Accès direct à une page de données situé dans le Buffer Cache

Comment améliorer les performances ? En minimisant au possible les Physical IO.

  • Avoir assez de mémoire (ouais c’est évident, mais faut bien le préciser Winking smile)
  • Optimiser l’architecture physique et logique de votre base (les index !)
  • Optimiser vos requêtes pour éviter de trop avoir besoin de récupérer des page sur le disque

Comment faire pour “voir” les IO générés ?

 

SET STATISTICS IO ON

image

Ok, il fait 1240 Logical Read, pour 2 Physical Read

Vous noterez que Môôssieur ne récupère QUE 2 pages sur le disque alors que j’ai au préalable vidé les Buffers Cache.

Et bien il faut savoir que SQL SERVER utilise une technique de lecture anticipée (lors de la compilation de la requête) pour engranger des données en mémoire “Juste Au Cas Où” : Ce sont les Read-Ahead Reads

Je ré exécute cette requête, dans la foulée :

image

Là on remarque que SQL SERVER effectue bien ses lectures directement dans le cache SQL SERVER.

Que pouvez vous faire contre les Physical IO ?

Nous venons de voir que les Physical IO sont ceux qui coutent le plus cher. Mais que pouvez y faire ?

Et bien … RIEN ! (oui c’est balot)

Le seul moyen de réduire les IO Physical, c’est d’améliorer les IO Logical, enfin bref d’améliorer les IO en général !

Meilleurs requêtes, Plus de ram, Meilleurs indexs.

Quelles sont les outils d’investigations:

 

Profiler SQL

image

Notez que le profiler ne différencie pas les types d’IO (Physical, Logical, Read Ahead)

Vous avez donc une donnée qui représente les Logical Read.

 

DMV

Quelques DMV peuvent vous aider. Elles sont en général basées sur sys.sysprocesses :

Select db_name(sp.dbid) as ‘Database’
,cpu,physical_io
,[program_name]
,sql_handle,st.text
from sys.sysprocesses sp
cross apply
sys.dm_exec_sql_text(sp.sql_handle) st
where db_name(sp.dbid) not in (‘master’,'model’,'tempdb’,'msdb’)
order by cpu desc

 

Compteur de performances

Les compteurs relevés sont :

  • % Durée d’inactivité
  • % Temps d’écriture
  • % Temps de lecture

D’une manière générale, le % d’inactivité doit être toujours le plus proche de 100%

A contrario, les % d’écriture et de lecture doivent être le plus proche de 0%

image

Ici on remarque que le Disque est soumis à rude épreuve, même s’il tient le choc (Les pics de durée d’inactivité n’atteignent pas 0% sur une longue durée)

 

Quoi d’autres ?

Il existe d’autres éléments, qui sont plus ou moins en rapport avec les IO :

  • Amélioration des Index
  • Fragmentation des index
  • Pression mémoire
  • Amélioration des procédures stockées et requêtes
  • Nouveaux index etc …

Un véritable sacérdoce ces IO !! Smile

Le Framework de synchronisation MS, entièrement réalisé en .NET, et indépendant (enfin presque Smile with tongue out) de la base de données source ou cible, va bientôt passé en Release et … publié avec le code source !

C’est l’aboutissement d’un long effort, ou peut être, plus vraisemblablement d’un désintéressement de Microsoft vis à vis de ce produit (en terme de support et d’amélioration), qui va enfin nous donner la possibilité de voir les entrailles de la bête (enfin mieux qu’avec Reflector quoi Smile)

L’information était déjà officielle, mais la date de mise à disposition était assez vague (courant 2011 !)

C’est sur une petite mise à jour de son Post que Mr Mark Scurell, nous fait signe que le code source sera disponible d’ici fin Aout.

Ca ne devrait donc plus tarder !

C’est certes une bonne nouvelle, mais ça annonce clairement des évolutions et surtout le non support de MS vis à vis du Fx 4.0

Attention toutefois, le support est encore bien sûr effectif sur la version 2.1 !!

Dès que tout ça est disponible, je vous ferai un petit compte rendu de ce que l’inspection de tout ça peut nous apporter Smile

imageEt voilà la session que j’attendais : SQL CE dans WP7

Depuis la sortie de WP7, la seule façon de faire du management de données est Linq to Objects sur des entités qu’on va ensuite sérialiser dans l’Isolated Storage (si on veut les garder sur le téléphone)

Mais si vous voulez plus de contrôle, plus de possibilités de structuration de schéma (liaison N-1 N-N etc ….), de possibilités de tri, ordonnancement etc … c’est compliqué et couteux

Merci donc à WP7 Mango d’apporter SQL SERVER CE dans WP7 !

Et il vous faudra vous y habituer, pour faire du SQL dans WP7, on va faire du … Linq To SQL (le vieux truc qui resort du placard Sourire)

Bon, ça peut se comprendre, moins d’abstraction permettra de meilleurs performances (et oui, on a pas encore des WP7 avec un ICORE 7 dedans Sourire)

image

image

En plus d’apporter donc SQL CE dans WP7, la team WP7 nous donne aussi accès à des api permettant de faire des requêtes sur les contacts ou encore les évènements du calendrier

Linq To SQL and Code First baby !

et oui, SQL CE dans WP7 est basé entièrement sur du LinqToSql et une création à base de Code First (décoration d’entités qui va ensuite générer la base de données relationnelle)

Grosso modo, on va (re)créer le DataContext qui va se mapper sur nos données SQL CE. On va refaire ce que fait le designer VS en gros Sourire

Je sens bien les bon templates T4 qui vont sortir bientot pour générer les classes WP7 pour une base de données SDF Sourire

Requêter des données SQL CE pour WP7

imagePremière démo : Faire du linq to Sql pour accéder aux données stockées dans SQL CE

On retrouve ce qu’on connait déjà en LinqToSql :

  • Une requête Linq
  • Un provider qui va traduire l’instruction en SQL
  • ADO.NET qui va exécuter la requête

 

Créer un schéma SQL CE pour WP7

Comment Dire …. CODE FIRST !

imageCréer un schéma, c’est créer vos entités dans un objet héritant de DataContext et les décorer via des attributs *

On est dans un principe de base de données Objets, transformée en base de données relationnelle.

 

Je vous mets les screenshots de ce que j’ai pu prendre pendant la session :

On peut voir que tout les décorations par attributs nous rappelle LinqToSQL, jusqu’à la création du DataContext :

image

image

image

 

Création de la base de données : on donne une Data Source dans l’Isolated Storage et on appelle CreateDatabase

image

Cool; on a les Compiled Query

image

Update Delete Inserts

Bon ben c’est du Linq To SQL, donc on modifie, supprime nos objets et on appelle le SubmitChanges()

imageimage

 

Schema Upgrades

Il existe un objet DatabaseSchemaUpdater qui permet, transactionnellement, de faire une mise à jour de votre schéma.

image

Ok, ça je demande à voir ce que ça va donner Sourire J’ai quand meme un peu peur que ce soit un peu trop “léger” pour gérer les cas complexes de mise à jour!

Conclusion

Bon ben je suis enchanté par cette nouvelle, je suis moins fan du coté “Code First” même si j’estime que ici, cela prend plus de sens qu’ailleurs…

Sinon, pas mal de choses restent à éclaircir comme les perfs (Connection Pooling) l’intégration avec SQL SERVER 2008 + (Réplication ?) et surtout les perfs de l’engin sur mon WP7 ! Sourire

J’entends souvent dire que la mise à jour d’une ligne rend la table entière indisponible.

Bon soyons bien clair sur ce point ! La réponse est … ça dépend Smile

Allez, une petit démonstration de tout ça, sous la forme d’un exercice :

 

Problème

Soit le premier script suivant :

  1. Création d’une table
  2. Remplissage de la table avec quelques enregistrements
  3. Mise à jour d’une ligne dans une transaction non commit
   1: USE [master]

   2: GO

   3: IF  EXISTS (SELECT name FROM sys.databases WHERE name = N'Test')

   4:        DROP DATABASE [Test]

   5: GO

   6: CREATE DATABASE [Test] 

   7: Go

   8: Use Test;

   9: CREATE TABLE [dbo].[Tbl1](

  10:        [ID] [int] IDENTITY(1,1) NOT NULL,

  11:        [C1] [int] NULL,

  12:        [C2] [int] Null

  13: )

  14: Go

  15: Declare @i int = 0;

  16: while (@i < 5000)

  17: Begin

  18:        Insert into Tbl1 values (@i, @i + 100);

  19:        set @i = @i + 1;

  20: End

  21: Go

  22: Begin Transaction

  23:        Update Tbl1

  24:        Set C1 = 12 where ID = 1

  25:              

  26: -- Commit Transaction

Et le deuxième script suivant (A exécuter dans une nouveau fichier de requête SQL, donc sur une nouvelle connexion)

   1: Update Tbl1

   2: Set  C1 = 13 where ID = 2

La question est :

  1. Pourquoi le deuxième update ne marche pas (alors que j’attaque pas la même ligne en update)
  2. Comment faire pour que le deuxième update fonctionne même si j’ai pas terminé la première requête et sa transaction ?

Réponse

Le problème ici est l’absence d’index sur notre table (oui c’est le mal !).

Comme on précise pas d’index, il crée des pages de données les unes après les autres, au fur et à mesure des insert (la boucle while 10000)

L’ajout d’enregistrement se fait un peu n’importe comment, c’est pas ordonné, c’est un peu le bordel. SQL SERVER sait juste quelle est la première page et la dernière (et chaque page connait la suivante et la précédente)

Dans le premier script, pour récupérer la ligne à mettre à jour, vu qu’il n’a pas d’index ordonné et qu’il ne sait pas où est la ligne dans les pages de la table, il fait un bon vieux Gros Scan (parcours de toutes les pages) pour récupérer la ligne :

Il parcourt toute la table à la recherche de la ligne quoi ! (ou qu’elle là ma tite ligne, ou qu’elle est ….)

clip_image001

Du coup il locke toute la table pendant l’update.

Si on active le plan d’exécution du premier update, on peut voir voit ça :

clip_image002

Table Scan, rhooooooooo………

 

La Première Solution (la bonne)

Contrairement aux idées reçues, un Update ne locke pas toute la table, juste les pages qu’il a utilisées pour récupérer la ligne à mettre à jour.

Si on crée notre table avec un index, il va directement chercher la ligne au bon endroit, grâce au B TREE ordonné créé et ne va locker que la ligne trouvée  :

   1: ALTER TABLE dbo.Tbl ADD CONSTRAINT

   2: PK_Tbl1 PRIMARY KEY CLUSTERED  (ID ) 

clip_image003

Si on regarde le plan d’exécution du premier script, ça devient, du coup ça :

clip_image004

Il fait bien directement appel à l’index cluster, ne parcourant donc PAS toute la table pour récupérer l’enregistrement.

Du coup, le 2eme script passe TRES BIEN, même si le premier update n’est pas terminé ! J

clip_image006

Autre réponse possible

Il existe au moins une autre solution, mais qui cache un peu le problème sous jacent (pas d’index)

C’est d’activer le versionning sur la base :

   1: ALTER DATABASE Test1

   2:     SET ALLOW_SNAPSHOT_ISOLATION ON;

Là le 2eme script fonctionne:

   1: SET TRANSACTION ISOLATION LEVEL SNAPSHOT 

   2:  

   3: Update Tbl1

   4: Set  C1 = 13 where ID = 2

Par contre, vous allez saturer votre base tempdb avec les copies de chaque ligne lockée. C’est biensur bien moins performant, et ça cache un énorme problème de base Smile

Bon UPDATE !

Les Techdays sont terminés. Je recommence une vie normale où j’arrive à dormir sans stresser Smile

Cette année, j’ai eu la chance d’être webcasté pour la TechDays TV.

image

Vous pouvez me retrouver en session Technique le premier et deuxième jour :image

  1. http://viplive7-pad.brainsonic.com/customers/microsoft/techdays11-j1/  (Session à 09:09.00)
  2. http://viplive7-pad.brainsonic.com/customers/microsoft/techdays11-j2/ (Session à 04 :17.22)

Personnellement, je trouve que la 2ème session a été meilleure que la 1ere !

Sinon bonne expérience, à renouveler l’année prochaine j’espère !

Bon visionnage Smile

Grosse nouveauté de cette année, pour les malchanceux qui n’auraient pas l’occasion de venir à Paris assister au TechDays 2011 (C’est dommage, ça vaut le coup d’y aller au moins 1 fois, croyez moi Smile)

imageCette année, grosse nouveauté, vous aurez l’occasion de regarder en LIVE les plénières des 3 jours AINSI que certaines sessions. La Web Tv Techdays 2011 débarque sur vos petits moniteurs Smile !

Evidemment, il y’a plus de 300 sessions, il a donc fallu que Microsoft fasse un choix des sessions à retransmettre en direct. Il y’aura donc 15 sessions retransmises via la web tv. Ce qui donne, si on compte les plénière, 17 h de retransmission ! De quoi rassasier les plus gourmands !

La web tv des techdays retransmettra principalement les sessions accès sur Azure, Office 365, Sharepoint 2010, Windows 7 biensur, de la BI ou encore de la virtualisation avec Hyper V.

Si je vous parle de ça, c’est que j’ai l’immense plaisir d’animer non pas Une mais Deux de ces sessions en direct live. (Ok la pression est montée d’un cran, je vous assure Smile with tongue out)

Je vous retrouverai donc pour :

  1. La session OData : Partagez vos données de manière interopérable, Lundi de 17h30 à 18h30 avec Thomas Conte.
  2. La session SQL Azure : La base de données dans le Cloud , Mercredi de 13 à 14h avec Stéphane Crozatier.

image

Vous aurez donc la grand joie d’entendre ce cher accent toulousain qui, à en croire certains, est assez prononcé chez moi Smile

Et pour bien se mettre en jambe, pour ceux qui seront sur place, retrouvez moi d’abord lundi de 11h à 12h pour la session Sécurité avec SQL SERVER que je co-animerai avec Jean Pierre Rielh !

D’ailleurs pour rappel, Bewise arrivera en force cette année au Tech Days, puisque nous animerons pas moins de 10 sessions cette année. Retrouvez toutes ces informations sur notre page dédiée à l’évènement !

Et rassurez vous, ces sessions sont retransmises en direct live certes, mais vous retrouverez les autres sessions webcastés dans les jours qui suivront l’évènement !!

Allez, je vous laisse, faut que je prépare moi Open-mouthed smile

imageJ’ai la chance en 2011 d’animer encore quelques sessions aux TechDays 2011.

Pour cette année, je vais faire un focus sur ma techno de prédilection : SQL SERVER Smile

Vous pouvez me retrouver sur 3 sessions accès autour des technos de transfert et partages de données :

SQL Azure : La base de données dans le Cloud :

SQL Azure est une base de données relationnelle, disponible sous forme de service, hébergée et opérée par Microsoft. Quels sont les bénéfices d’une telle solution, pour quels scénarios et combien ça coute ? Quelles sont les similitudes et différences avec SQL Server, et les briques complémentaires comme le décisionnel ? Découvrez la base de données dans le Cloud: SQL Azure, SQL Azure Reporting et SQL Data Sync

Le lien direct : Sql Azure : La base de données dans le cloud

ODATA : Partagez vos données avec ODATA

OData (Open Data Protocol) est un protocole web prévu pour requêter et mettre à jour des données à travers HTTP. Aujourd’hui, on retrouve OData un peu partout chez Microsoft. Pour les développeurs, vous pouvez très facilement exposer vos données en utilisant le framework WCF Data Services de .NET 4.0 qui vous exposera vos entités sous HTTP REST avec support du CRUD (Create Read Update Delete).

SQL Azure peut exposer ses données directement sous un flux OData en cochant une simple case.

Windows Azure expose ses tables de données en OData, etc.

Venez découvrir OData à travers de nombreuses démos !

Le lien direct : Odata : Partagez vos données avec OData

SQL SERVER 2008 R2 : La Sécurité de votre Serveur SQL SERVER 2008

La sécurité avec SQL SERVER 2008 R2. Venez découvrir tous les enjeux de la sécurité avec SQL SERVER 2008 R2. Nous passerons en revue toutes les actions à mener pour sécuriser votre serveur : Encryptions, sécurité des utilisateurs, Parefeu, configuration des ports, surface d’exposition, sécurité des comptes de services, contexte d’exécution, administration des polices de sécurité, audit …

le lien direct : La Sécurité de votre Serveur SQL SERVER 2008

Ca fait un petit moment que je n’avais pas parlé de mon projet SQL MiM. Je n’ai pas fais de mise à jour du projet CodePlex depuis un bail, et pour cause, j’ai tout cassé, et j’ai tout refais !

Le fonctionnel reste le même mais je trouvais l’interface trop fade, j’ai donc décidé de refaire tout ça en m’inspirant du thème Metro et de l’interface Zune.

Alors certes, c’est pas encore fini (je ne sais pas si ça le sera un jour d’ailleurs Smile) mais voilà déjà quelques screens de la version Avant / Aprés :

 

Informations générales, Avant : Informations générale, Aprés :
SQL_MiM_ServerStats image

Index fragmentés, Avant :

Index fragmentés, Aprés :
SQL_MiM_FragIndexes image
Taille des fichiers, Avant : Taille des fichiers, Aprés :
SQL_MiM_FileSizes image

Bon je n’ai pas fais que des arrangements graphiques, j’ai aussi implémentés quelques nouveautés Smile

Comme par exemple, la monitoring des pools de connexion :

image

Et quelques autres surprises en cours de chantier Smile

Introduction

Les Sparses Columns sont une nouvelle fonctionnalité introduite par SQL SERVER 2008.

Si on se réfère à la traduction littérale de Sparse Column, on obtiendrait quelque chose comme « Colonne à faible densité ».
Pour être plus précis, les Sparse Columns représentent une solution de stockage efficace (en terme base de données !) des colonnes dites de faible densité (ou faible usage).

Faisons simple : Il s’agit ici d’économiser de l’espace disque en diminuant drastiquement le nombre de pages de données des tables sous SQL SERVER

Read the rest of this entry

J’ai récemment été confronté à un problème d’architecture SQL et de performances d’opérations CRUD sur SQL SERVER 2008

Pour tout vous dire, il s’agit de mon petit projet WP7 Starcraft 2 (oui je sais, je suis un Geek joueur Smile)

AppScreen01

Il ne devrait pas tarder à pointer le bout de son nez sur le Market Place, mais en attendant, voici un post résumant une des problématiques SQL que j’ai rencontrée !

Voici globalement les données du problème :

  • Je récupère des données provenant d’une source externe
  • Je dois fusionner ces données avec les données existantes de ma base de données. Il faut :
    1. Insérer les données non existantes
    2. Mettre à jour les données existantes
    3. Supprimer les données existantes de ma base de données ne faisant plus partie du scope
  • Je doit traiter pour une clé primaire de 0 à N données associées (N variant de 0 à environ 1000 enregistrements environ)
    • Pour imager le truc, pensez à quelque chose du genre (“Pour chaque client, je récupère de 0 à N Factures)
  • Je dois garder une consitence des données lors de ces opérations
  • Je dois être performance Smile (si si !)

Bien, à partir de là, on peut commencer à réfléchir à une solution.

Première solution : Brut-Force

Oui alors bon celle là, elle est loin d’être excellente, mais c’est la première qui vient à l’esprit:

Je crée une procédure stockée qui prend en paramètre un Record.

Celle ci vérifie si la ligne existe :

  • Si elle existe : Update
  • Si elle n’existe pas : Insert

On éxécute cette requête N fois le nb de records à mettre à jour. (Pour info je mets à jour environ 1 Million de record, faudra pas être pressé Smile with tongue out)

On encapsule ça dans une TransactionScope supportée par MSDTC et roulez !

Bon problème :

  1. On ne gère pas les enregistrements existants obsolètes à supprimer
  2. C’est ANTI-performant …
  3. C’est une solution qui va engendrer un nombre de problèmes incalculables…

Deuxième solution : La solution élégante

Alors, aprés avoir réfléchi à une solution pérenne, voici ce que je vous propose.

0 à N Enregistrements :

Là c’est plutôt simple, on va passer par une TVP : Table Value Parameter.

j’en parle ici pour ceux qui sont intéressés par la découverte de cette formidable avancée Smile

Voici la définition de ma TVP:

CREATE TYPE [dbo].[CharacterLeaguesTvp] AS TABLE(
    [LeagueId] [int] NOT NULL, [RegionId] [nvarchar](5) NOT NULL,
    [CharacterId] [int] NOT NULL, [int] NOT NULL, [Name] [nvarchar](150) NULL,
    [MostPlayedRace] [tinyint] NULL, [DivisionName] [nvarchar](250) NULL, [TeamType] [int] NULL,
    [LeagueType] [int] NULL, [Rank] [tinyint] NULL, [Points] [int] NULL, [VictoriesCount] [int] NULL,
    [LossesCount] [int] NULL, [LastModifiedDate] [datetime] NULL,
    PRIMARY KEY CLUSTERED 
([RegionId] ASC, [LeagueId] ASC, [ZoneId] ASC, [CharacterId] ASC)WITH (IGNORE_DUP_KEY = OFF)
)

Bon la défintion de la procédure stockée devient, du coup, BEAUCOUP plus simple Green with envy

Et elle a la bonne idée de prendre l’ensemble de mes records en un seul appel Open-mouthed smile

ALTER PROCEDURE [dbo].[spInsertOrUpdateCharacterLeague]
(
 @tmpCharacterLeagues  AS CharacterLeaguesTvp readonly
)
as
 
BEGIN
  ....
END

Ok, premier problème résolu, on a UN seul appel à une procédure stockée. Facile de gérer la consitence des données du coup !

Fusion : MERGE

Pour la partie fusion des données, la réponse est assez simple quand on parle juste d’insertion et mise à jour.

On va utiliser l’instruction MERGE de SQL SERVER 2008. De plus avec l’apport de la TVP, rien de plus simple !

Pour l’utilisation du Merge, je vous redirige ici.

Voici le corps de la méthode d’insertion / mise à jour :

  MERGE tCharacterLeague AS target
  USING @tmpCharacterLeagues AS source 
 
  ON (target.CharacterId = source.CharacterId and 
      target.LeagueId = source.LeagueId And 
      target.ZoneId = source.ZoneId and 
      target.RegionId = source.RegionId)    
  WHEN MATCHED THEN 
        UPDATE SET LastModifiedDate = source.LastModifiedDate,
                   Name = source.Name,
                   MostPlayedRace = source.MostPlayedRace,
                   [Rank] = source.[Rank],
                   Points = Source.Points,
                   VictoriesCount = Source.VictoriesCount,
                   LossesCount = source.LossesCount
  WHEN NOT MATCHED By TARGET THEN    
    INSERT (RegionId, CharacterId, ZoneId, LeagueId, Name, MostPlayedRace, [Rank], Points,
            VictoriesCount, LossesCount, LastModifiedDate)
    VALUES (Source.RegionId, Source.CharacterId, Source.ZoneId, Source.LeagueId, 
            Source.Name, Source.MostPlayedRace, Source.[Rank], Source.Points,
            Source.VictoriesCount, Source.LossesCount, Source.LastModifiedDate)

Ok là on est vraiment pas mal, en 1 appel, on fusionne TOUS les enregistrements existants et les nouveaux.

Reste à gérer les suppressions.

Là j’étais parti sur un truc assez simple de prime abord : On rajoute l’instruction de suppression sur le Merge

WHEN NOT MATCHED By Source THEN    
    
Delete;

Et ça marche … un peu trop

En effet, le Delete agit sur L’ENSEMBLE de la table, du coup TOUS les enregistrements de ma base (même ceux qui ne correspondent pas à ma clé primaire) sont impactées et donc supprimés !

Du coup je me retrouve avec une base .. vide! (enfin presque, il reste les éléments insérés / updatés)

Bon, il faut donc réduire le scope de mon merge “à la base”

Rien de plus simple avec une CTE !

Ce qui donne au final :

-- Utilisation d'une CTE pour réduire le scope, surtout pour le delete
  WITH TargetCharacterLeague AS 
 (
      SELECT CL.*
      FROM dbo.CharacterLeague CL
      Inner Join @tmpTableLeague T 
        on T.LeagueId = CL.LeagueId And T.ZoneId = CL.ZoneId and T.RegionId = CL.RegionId
  )
  MERGE TargetCharacterLeague AS target
  USING @tmpCharacterLeagues AS source 
 
  ON (target.CharacterId = source.CharacterId and 
      target.LeagueId = source.LeagueId And 
      target.ZoneId = source.ZoneId and 
      target.RegionId = source.RegionId)
    
  WHEN MATCHED THEN 
        UPDATE SET LastModifiedDate = source.LastModifiedDate,
                   Name = source.Name,
                   MostPlayedRace = source.MostPlayedRace,
                   [Rank] = source.[Rank],
                   Points = Source.Points,
                   VictoriesCount = Source.VictoriesCount,
                   LossesCount = source.LossesCount
  WHEN NOT MATCHED By TARGET THEN    
    INSERT (RegionId, CharacterId, ZoneId, LeagueId, Name, MostPlayedRace, [Rank], Points,
            VictoriesCount, LossesCount, LastModifiedDate)
    VALUES (Source.RegionId, Source.CharacterId, Source.ZoneId, Source.LeagueId, 
            Source.Name, Source.MostPlayedRace, Source.[Rank], Source.Points,
            Source.VictoriesCount, Source.LossesCount, Source.LastModifiedDate)
  WHEN NOT MATCHED By Source THEN    
    Delete;

 

Et voilà, une utilisation à la fois élégante et performante des outils mis à votre disposition avec SQL SERVER 2008 !

Bon Merge , bonne CTE, bonne TVP etc etc !