Kankuru regsrvr importer

Hello,

Oui je travaille avec Kankuru tous les jours, ca m’aide beaucoup au quotidien, mais je n’écris pas ceci parce que je suis le développeur ! Je pense que c’est un bon complément de SSMS. Par contre, s’il y a bien une chose qui m’ennuie vraiment dans Kankuru, c’est d’ajouter de nouveaux serveurs ! A l’époque, je voulais pouvoir gérer d’autres serveurs que des serveurs SQL Server, j’avais donc séparé l’ajout des serveurs et des instances SQL.

Il suffit de le refaire !

Oui, effectivement, je pourrais redévelopper ces interfaces pour les simplifier. Ca viendra peut être mais ce n’est pas ma priorité et surtout, je fais déjà le travail d’enregistrer les serveurs dans SSMS alors pourquoi faire le travail 2 fois.

Il y a quelques jours, sur twitter, David m’a demandé si on ne pouvait pas importer les serveurs enregistrés dans SSMS et je me suis dit que c’était une bonne idée.

Par contre, j’ai déjà testé les scans réseaux avec notamment la méthode SMO mais sans résultat probant. Je préfère ne pas passer de temps dans cette voie pour le moment.

Kankuru regsrvr Importer

kankuru regsrvr importer preview

Screenshot du script généré par Kankuru importer

J’ai réalisé ce projet Windows Forms très rapidement avec l’aide de Martial, un collègue. Le code est loin d’être beau mais il devrait vous permettre d’importer vos fichier regsrvr.

NB: Lorsque vous exportez les serveurs enregistrés, pensez à décocher cette case :

Export Regsrvr file from ssms

Exporter un fichier regsrvr depuis SSMS

Les mots de passes en authentification SQL sont encryptés donc il ne vous restera plus qu’à le remplacer par le bon.

GIthub

Je ne suis pas vraiment fier du code que j’ai écrit mais je le partage tout de même sur github.

Et pour les plus pressés, voici le fichier exe déjà compilé KankuruImporter

Il suffit d’avoir le framework 4.0 installé comme pour Kankuru.

Meet the DBA – rencontrez les DBA de Criteo

Bonjour tout le monde,

Mercredi 13 janvier, à partir de 18h30, nous organisons avec le GUSS une session chez Criteo (la société qui m’emploie).

Au programme Scale SQL for the web

  • L’organisation de l’équipe DBA
  • La gestion de l’infrastructure
  • Notre implémentation de la réplication SQL
  • Le déploiement du code SQL en production (Intégration continue)

Et ensuite, nous pourrons discuter autour d’un verre et d’une pizza.

Ca se passe à Paris dans le 9ème arrondissement et c’est gratuit 🙂

Vous n’avez qu’à vous inscrire sur le site du GUSS.

J’espère vous y rencontrer nombreux.

BULK INSERT Format File

Dernièrement, je suis tombé sur une application qui alimentait des milions de lignes de données dans une table de façon unitaire. Pour améliorer les performances d’insertion, j’ai du mettre en place une procédure qui BULK INSERT un fichier dans une table SQL Server. J’ai décidé d’utiliser un fichier de format (BCP Format File).
Ce fichier de format contient la structure de la table et du fichier.

Génération du fichier de format (Format File)

Pour générer un fichier de format, j’ai utilisé l’utilitaire BCP. Cet utilitaire bien connu des DBA permet d’exporter des données d’une table vers un fichier ou de faire le chemin inverse. Il permet également de générer des fichiers de format qui seront utilisés lors d’un BULK INSERT.

Pour les besoins de cet article, j’ai créé la table testimport dans ma base TEST. Elle contiendra les données insérées.

CREATE TABLE [dbo].[testimport](
id int NOT NULL
, name nvarchar(50)
, category smallint
, comments nvarchar(500)
, width decimal (18,2)
, height decimal (18,2)
CONSTRAINT [PK_testimport] PRIMARY KEY CLUSTERED
(
id ASC
) ON [PRIMARY]
) ON [PRIMARY]

Ensuite, en ligne de commande, je peux générer un fichier de format :

bcp « TEST.dbo.testimport » format -S »localhost » -T -f « C:\temp\testimport_format.xml » -c -x -t »; » -r »\n »

fichier_format_code

Explication :
bcp : commande d’appel de l’utilitaire. S’il n’est pas reconnu, vérifiez qu’il soit installé et votre PATH.
« TEST.dbo.testimport » : la table utilisée pour générer le formatfile
format : c’est cet argument qui permet de générer un fichier de format
-S « localhost » : le nom de mon instance locale
-T : authentification Windows. On peut également utiliser l’authentification SQL
-f « C:\temp\testimport_format.xml » : le nom de mon fichier de format qui sera créé.
-c : type de données char. -w pour de l’unicode
-x : format XML pour le format file
-t « ; » : le séparateur de colonne choisi
-r « \n » : le séparateur de ligne choisi

Vous pouvez retrouver toutes les options sur le site MSDN

Et voici le résultat :

Format file généré

Format file généré

BULK INSERT

J’ai donc ce fichier de 10 000 lignes à insérer dans ma table. Et grâce à BULK INSERT, je peux importer par lot mes données en T-SQL.

Le fichier à insérer

Le fichier à insérer

Et voici la requête qui permet de BULK INSERT

BULK INSERT TEST.dbo.testimport FROM 'C:\temp\testimport.dat'
WITH (
   BATCHSIZE =1000, MAXERRORS = 1
   ,FORMATFILE='C:\temp\testimport_format.xml');

Table Calendrier

Je n’y coupe jamais. A chaque fois, je me pose les mêmes questions. Dans beaucoup de projets, j’ai besoin d’un table calendrier et je ne me rappelle jamais des fonctions de format de date ! J’ai toujours besoin de retourner sur le site MSDN pour vérifier la syntaxe de telle ou telle fonction de date. Cette fois ci, j’ai décidé de publier un exemple de table calendrier. Cette table n’est pas forcément la plus complète mais elle a le mérite de recenser tout de même pas mal d’informations intéressantes et d’être facile à implémenter dans un ETL ou un job d’alimentation.

Création de la table Calendrier

Voici le DDL de la table Calendrier.

CREATE TABLE [dbo].[Calendrier]
(
   [DateJour] [date] NOT NULL,
   [Jour] [int] NOT NULL,
   [Mois] [int] NOT NULL,
   [Annee] [int] NOT NULL,
   [Trimestre] [int] NOT NULL,
   [Semestre] [int] NOT NULL,
   [JourAnnee] [int] NOT NULL,
   [JourSemaine] [int] NOT NULL,
   [Semaine] [int] NOT NULL,
   [SemaineIso] [int] NOT NULL,
   [JourTexteLong] [varchar](50) NOT NULL,
   [JourTexteCourt] [varchar](50) NOT NULL,
   [MoisTexteLong] [varchar](50) NOT NULL,
   [MoisTexteCourt] [varchar](50) NOT NULL,
   [DateTexteLong] [varchar](50) NOT NULL,
   [MoisTexteAnnee] [varchar](50) NOT  NULL,
   [TrimestreTexte] [varchar](14) NOT NULL,
   [SemestreTexte] [varchar](13) NOT NULL
) ON [PRIMARY]

ALTER TABLE dbo.Calendrier ADD CONSTRAINT PK_Calendrier
PRIMARY KEY CLUSTERED (DateJour ASC)

Insérer les données

Avant d’insérer les données, il faut tout d’abord définir une date de début et une date de fin.
Le script utilise la récursivité d’une CTE afin de sélectionner toutes les dates entre l’intervalle de début et de fin. (N’oubliez pas de fixer l’indicateur MAXRECURSION 0 à la fin de la requête)

DECLARE @DATEDEBUT DATE
DECLARE @DATEFIN DATE
SET @DATEDEBUT = '20100101'
SET @DATEFIN = '20141231'

;WITH mycte AS
(
   SELECT @DATEDEBUT DateJour
   UNION ALL
   SELECT DATEADD(DAY, 1, DateJour)
   FROM mycte
   WHERE DATEADD(DAY, 1, DateJour) < @DATEFIN
)
INSERT INTO [dbo].[Calendrier]
(    [DateJour]
   , [Jour]
   , [Mois]
   , [Annee]
   , [Trimestre]
   , [Semestre]
   , [JourAnnee]
   , [JourSemaine]
   , [Semaine]
   , [SemaineIso]
   , [JourTexteLong]
   , [JourTexteCourt]
   , [MoisTexteLong]
   , [MoisTexteCourt]
   , [DateTexteLong]
   , [MoisTexteAnnee]
   , [TrimestreTexte]
   , [SemestreTexte])
SELECT DateJour
   , DATEPART(DAY, DateJour) as Jour
   , DATEPART(MONTH, DateJour) as Mois
   , DATEPART(YEAR, DateJour) as Annee
   , DATEPART(QUARTER, DateJour) as Trimestre
   , CASE WHEN DATEPART(MONTH, DateJour) < 7 THEN 1 ELSE 2 END as Semestre
   , DATEPART(DAYOFYEAR, DateJour) as JourAnnee
   , DATEPART(WEEKDAY, DateJour) as JourSemaine
   , DATEPART(WEEK, DateJour) as Semaine
   , DATEPART(ISO_WEEK, DateJour) as SemaineIso
   , FORMAT(DateJour, 'dddd', 'fr-fr') as JourTexteLong
   , FORMAT(DateJour, 'ddd', 'fr-fr') as JourTexteCourt
   , FORMAT(DateJour, 'MMMM', 'fr-fr') as MoisTexteLong
   , FORMAT(DateJour, 'MMM', 'fr-fr') as MoisTexteCourt
   , FORMAT(DateJour, 'dd MMMM yyyy', 'fr-fr') as DateTexteLong
   , FORMAT(DateJour, 'MMMM yyyy', 'fr-fr') as MoisTexteAnnee
   , CASE WHEN DATEPART(QUARTER, DateJour) = 1 THEN '1er trimestre' ELSE CAST(DATEPART(QUARTER, DateJour) AS CHAR(1)) + 'ème trimestre' END as TrimestreTexte
   , CASE WHEN DATEPART(MONTH, DateJour) < 7 THEN '1er semestre' ELSE '2ème semestre' END as SemestreTexte
FROM mycte
OPTION (MAXRECURSION 0)

1er jour de la semaine

En fonction de l’environnement, le premier jour de la semaine n’est pas forcément le lundi. Mais pas de panique, il suffit de jouer avec cette fonction de session pour configurer le premier jour de la semaine. Plus d’information sur le site MSDN.

SET DATEFIRST 1

Insérer les nouvelles données


SET DATEFIRST 1

DECLARE @DATEDEBUT DATE
DECLARE @DATEFIN DATE
SET @DATEDEBUT = '20100101'
SET @DATEFIN = GETDATE()

;WITH mycte AS
(
   SELECT @DATEDEBUT DateJour
   UNION ALL
   SELECT DATEADD(DAY, 1, DateJour)
   FROM mycte
   WHERE DATEADD(DAY, 1, DateJour) < @DATEFIN
)
INSERT INTO [dbo].[Calendrier]
(    [DateJour]
   , [Jour]
   , [Mois]
   , [Annee]
   , [Trimestre]
   , [Semestre]
   , [JourAnnee]
   , [JourSemaine]
   , [Semaine]
   , [SemaineIso]
   , [JourTexteLong]
   , [JourTexteCourt]
   , [MoisTexteLong]
   , [MoisTexteCourt]
   , [DateTexteLong]
   , [MoisTexteAnnee]
   , [TrimestreTexte]
   , [SemestreTexte])
SELECT DateJour
   , DATEPART(DAY, DateJour) as Jour
   , DATEPART(MONTH, DateJour) as Mois
   , DATEPART(YEAR, DateJour) as Annee
   , DATEPART(QUARTER, DateJour) as Trimestre
   , CASE WHEN DATEPART(MONTH, DateJour) < 7 THEN 1 ELSE 2 END as Semestre
   , DATEPART(DAYOFYEAR, DateJour) as JourAnnee
   , DATEPART(WEEKDAY, DateJour) as JourSemaine
   , DATEPART(WEEK, DateJour) as Semaine
   , DATEPART(ISO_WEEK, DateJour) as SemaineIso
   , FORMAT(DateJour, 'dddd', 'fr-fr') as JourTexteLong
   , FORMAT(DateJour, 'ddd', 'fr-fr') as JourTexteCourt
   , FORMAT(DateJour, 'MMMM', 'fr-fr') as MoisTexteLong
   , FORMAT(DateJour, 'MMM', 'fr-fr') as MoisTexteCourt
   , FORMAT(DateJour, 'dd MMMM yyyy', 'fr-fr') as DateTexteLong
   , FORMAT(DateJour, 'MMMM yyyy', 'fr-fr') as MoisTexteAnnee
   , CASE WHEN DATEPART(QUARTER, DateJour) = 1 THEN '1er trimestre' ELSE CAST(DATEPART(QUARTER, DateJour) AS CHAR(1)) + 'ème trimestre' END as TrimestreTexte
   , CASE WHEN DATEPART(MONTH, DateJour) < 7 THEN '1er semestre' ELSE '2ème semestre' END as SemestreTexte
FROM mycte
WHERE NOT EXISTS (SELECT * FROM dbo.Calendrier c WHERE mycte.DateJour = c.DateJour)
OPTION (MAXRECURSION 0)

Conclusion

Table calendrier

SELECT sur la table calendrier

Bien évidemment cette table n’est pas exhaustive, n’hésitez donc pas à me proposer vos idées d’amélioration dans les commentaires.

Les journées SQL Server 2014

Les journées SQL Server 2014

Il y a un an, j’assistais à mes premières journées SQL Server. Étant un néo-parisien, je n’avais jamais pu m’y rendre. Pour cette première, j’avais même eu la chance d’y animer une session sur le monitoring avec Kankuru avec l’aide de Philippe Geiger ! Cette année encore, les journées SQL Server reviennent pour une 4ème édition. Et le moins que l’on puisse dire, c’est que le casting est excellent.

Les Journées SQL Server ?

Les journées SQL Server, ce sont 2 jours de conférences gratuites sur des sujets autour de la technologie SQL Server. Cet évènement, organisé par le GUSS et Microsoft, est le plus grand en France dédié à notre chère base de données. Il s’adresse à tous les acteurs du monde informatique puisqu’il y aura des sessions sur l’administration, la BI, la Big Data, les outils, les nouveautés et tous les sujets connexes à SQL Server.

Mon planning

Il est toujours compliqué de faire des choix lors de la construction de son planning. Est ce que je dois assister à des sessions sur le moteur SQL afin d’en apprendre encore plus ? Ou dois-je m’ouvrir vers d’autres tracks pour améliorer ma culture informatique ? J’hésite encore, mais je pense tout de même voir les sessions qui ont un rapport avec le moteur SQL, les performances et l’architecture.

Agenda JSS2014

Agenda des JSS 2014, faites votre choix !

Je recommande tout de même la session de David Barbarin, le premier jour, pour tous ceux qui souhaiteraient en savoir plus sur l’AlwaysOn et la haute disponibilité. C’est d’ailleurs un sujet sur lequel je travaille actuellement dans Kankuru…

La session de Frederic Pichaut est pour moi un incontournable ! Ceux qui ont déjà eu la chance de voir une de ses sessions savent pourquoi je la recommande. Même quand on pense être un expert sur un sujet, on apprend toujours avec Fred.

Les sessions de Jean-Pierre Riehl sont toujours excellentes. Un bon moyen de voir ce qu’il se fait dans la BI de Microsoft.

Pour le deuxième jour, tout dba devrait être présent dans la track « Core SQL ». Les meilleurs DBA français proposent des sessions techniques très intéressantes.

Et moi dans tout ça ?

Cette année encore, j’ai été sélectionné pour présenter une session. Mais pour changer, il ne sera pas question de Kankuru. Je vais vous présenter les bibliothèques SMO avec du code C#. Grâce à cette session, vous pourrez débuter un projet SMO afin d’automatiser toutes les taches quotidiennes d’administration. Cette fois encore, Philippe Geiger m’a proposé son aide 🙂

Et en retour, je l’assisterai pour sa session sur StreamInsight ! En tout, 2 heures de session en compagnie de Philippe 🙂

Inscriptions

Ces 2 jours de conférences sont gratuits. Ils se déroulent au centre de conférence de Microsoft à Issy-les-moulineaux.

Pour vous inscrire, c’est très simple ! Il suffit de se rendre sur le site du GUSS à cette adresse, de créer un compte sur le site (si vous n’en avez pas encore) et de vous inscrire. Ça ne prend pas plus de 2 minutes.

En pleine session, lors des JSS 2013 avec Philippe Geiger

Ma présentation lors des JSS 2013, avec mon énorme micro !

Ma présentation lors des JSS 2013, avec mon énorme micro !

J’espère y revoir d’anciens collègues et rencontrer de nouvelles têtes ! N’hésitez pas à venir discuter avec moi si vous me croisez dans les couloirs 🙂

SMO Créer une base de données en C#

Beaucoup d’applications ont besoin de leur base de données pour fonctionner. Après l’installation d’un logiciel, l’application doit donc créer sa propre base. C’est le cas de Kankuru par exemple. Après son installation, j’utilise SMO pour créer la base de données vide (mais ensuite, contrairement à cet article, j’utilise des scripts SQL pour créer les objets). Dans ce tutoriel, je vais vous montrer comment on peut se passer du moindre script T-SQL pour créer une base de données et ses objets (une table) en utilisant SMO.

Prérequis

Pour les débutants, je vous propose de consulter cet article pour apprendre à créer un projet C# + SMO.

Connexion à l’instance

Oui pour créer une base de données, il faut avant tout être connecté à une instance SQL Server ! Pour la démo, je me connecte à mon instance locale.

string _instance = "localhost";
smoCommon.ServerConnection sc = new smoCommon.ServerConnection(_instance);
sc.Connect();
smo.Server myServer = new smo.Server(sc);

Création de la base de données

Une fois connecté à l’instance, je vais créer une base de données. A ce stade, ce ne sera qu’une enveloppe vide.

smo.Database myDb = new smo.Database(myServer, "TestSMO"); // TestSMO est le nom de la base que je vais créer
myDb.Collation = "French_CI_AS"; // Un exemple de collation
myDb.RecoveryModel = smo.RecoveryModel.Simple; // Il y a aussi BulkLogged et FULL
myDb.CompatibilityLevel = smo.CompatibilityLevel.Version110; // Pour SQL Server 2012

// FileGroup et Fichiers
smo.FileGroup myFileGroup = new smo.FileGroup(myDb, "PRIMARY", false); // True pour un FileStream
myDb.FileGroups.Add(myFileGroup);

// Fichier de données
smo.DataFile myDataFile = new smo.DataFile(myFileGroup, "TestSMO_data");
myDataFile.FileName = myServer.MasterDBPath + "\TestSMO_data.mdf"; // je crée le fichier dans le répertoire par défaut de l'instance mais vous pouvez mettre un chemin complet
myDataFile.Size = 500 * 1024.0; // 500Mo
myDataFile.GrowthType = smo.FileGrowthType.Percent; // None pour pas de croissance et KB si accroissement en taille fixe
myDataFile.Growth = 12; // 12%
myDataFile.IsPrimaryFile = true; // Uniquement sur le fichier primaire hein !
myFileGroup.Files.Add(myDataFile); // j'ajoute le fichier au bon filegroup

//Fichier de transactions
smo.LogFile myLogFile = new smo.LogFile(myDb, "TestSMO_log");
myLogFile.FileName = myServer.MasterDBLogPath + "\TestSMO_log.ldf"; // je crée le fichier dans le répertoire par défaut de l'instance mais vous pouvez mettre un chemin complet
myLogFile.Size = 50 * 1024.0; // 50Mo
myLogFile.GrowthType = smo.FileGrowthType.KB; // None pour pas de croissance et Percent si accroissement en pourcentage
myLogFile.Growth = 20 * 1024.0; // 20 Mo
myDb.LogFiles.Add(myLogFile); // j'ajoute le fichier à ma base (et non pas au filegroup comme le fichier de données)

myDb.Create(); // création de la base, jusqu'ici rien n'avait été créé sur l'instance.

smo_create_database

Création de la table

Une fois ma base créée, je peux y insérer des objets. Par exemple, je vais créer une table avec 2 colonnes dont une clef primaire auto-incrémentée.

// Déclaration de la table
smo.Table myTable = new smo.Table(myDb, "TableTest1", "dbo");
// Première colonne de la table. C'est une PK auto incrémentée
smo.Column myCol1 = new smo.Column(myTable, "id", smo.DataType.Int);
myCol1.Nullable = false;
myTable.Columns.Add(myCol1);

// Auto Incrément
myCol1.Identity = true;
myCol1.IdentityIncrement = 1;

// Définition de la PK
smo.Index myPk = new smo.Index(myTable, "IXPk");
myPk.IsClustered = true;
myPk.IsUnique = true;
myPk.IndexKeyType = smo.IndexKeyType.DriPrimaryKey;
myPk.IndexedColumns.Add(new smo.IndexedColumn(myPk, myCol1.Name));
myTable.Indexes.Add(myPk);

// Deuxième colonne de la table. C'est une chaine de caractère
smo.Column myCol2 = new smo.Column(myTable, "nom", smo.DataType.VarChar(200));
myCol2.Nullable = true;
myTable.Columns.Add(myCol2);

// Création de la table
myTable.Create();

smo Create Table

Script Complet

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using smoCommon = Microsoft.SqlServer.Management.Common;
using smo = Microsoft.SqlServer.Management.Smo;

namespace DemoCreateDatabase
{
class Program
{
static void Main(string[] args)
{
string _instance = "localhost";
smoCommon.ServerConnection sc = new smoCommon.ServerConnection(_instance);
sc.Connect();
smo.Server myServer = new smo.Server(sc);

smo.Database myDb = new smo.Database(myServer, "TestSMO"); // TestSMO est le nom de la base que je vais créer
myDb.Collation = "French_CI_AS"; // Un exemple de collation
myDb.RecoveryModel = smo.RecoveryModel.Simple; // Il y a aussi BulkLogged et FULL
myDb.CompatibilityLevel = smo.CompatibilityLevel.Version110; // Pour SQL Server 2012

// FileGroup et Fichiers
smo.FileGroup myFileGroup = new smo.FileGroup(myDb, "PRIMARY", false); // True pour un FileStream
myDb.FileGroups.Add(myFileGroup);

// Fichier de données
smo.DataFile myDataFile = new smo.DataFile(myFileGroup, "TestSMO_data");
myDataFile.FileName = myServer.MasterDBPath + "\TestSMO_data.mdf"; // je crée le fichier dans le répertoire par défaut de l'instance mais vous pouvez mettre un chemin complet
myDataFile.Size = 500 * 1024.0; // 50Mo
myDataFile.GrowthType = smo.FileGrowthType.Percent; // None pour pas de croissance et KB si accroissement en taille fixe
myDataFile.Growth = 12; // 12%
myDataFile.IsPrimaryFile = true; // Uniquement sur le fichier primaire hein !
myFileGroup.Files.Add(myDataFile); // j'ajoute le fichier au bon filegroup

//Fichier de transactions
smo.LogFile myLogFile = new smo.LogFile(myDb, "TestSMO_log");
myLogFile.FileName = myServer.MasterDBLogPath + "\TestSMO_log.ldf"; // je crée le fichier dans le répertoire par défaut de l'instance mais vous pouvez mettre un chemin complet
myLogFile.Size = 50 * 1024.0; // 50Mo
myLogFile.GrowthType = smo.FileGrowthType.KB; // None pour pas de croissance et Percent si accroissement en pourcentage
myLogFile.Growth = 20 * 1024.0; // 20 Mo
myDb.LogFiles.Add(myLogFile); // j'ajoute le fichier à ma base (et non pas au filegroup comme le fichier de données)

myDb.Create(); // création de la base, jusqu'ici rien n'avait été créé sur l'instance.

// Déclaration de la table
smo.Table myTable = new smo.Table(myDb, "TableTest1", "dbo");
// Première colonne de la table. C'est une PK auto incrémentée
smo.Column myCol1 = new smo.Column(myTable, "id", smo.DataType.Int);
myCol1.Nullable = false;
myTable.Columns.Add(myCol1);

// Auto Incrément
myCol1.Identity = true;
myCol1.IdentityIncrement = 1;

// Définition de la PK
smo.Index myPk = new smo.Index(myTable, "IXPk");
myPk.IsClustered = true;
myPk.IsUnique = true;
myPk.IndexKeyType = smo.IndexKeyType.DriPrimaryKey;
myPk.IndexedColumns.Add(new smo.IndexedColumn(myPk, myCol1.Name));
myTable.Indexes.Add(myPk);

// Deuxième colonne de la table. C'est une chaine de caractère
smo.Column myCol2 = new smo.Column(myTable, "nom", smo.DataType.VarChar(200));
myCol2.Nullable = true;
myTable.Columns.Add(myCol2);

// Création de la table
myTable.Create();

sc.Disconnect();
}
}
}

Conclusion

Je n’ai abordé qu’une toute petite partie des fonctionnalités de SMO mais je pense qu’en terme de création d’objets, il n’y a pas de limite.
Bien évidemment, si vous utilisez SMO pour créer une base de données, pensez à tester les exceptions !

SQLSaturday

SQLSaturday 2014 – dans les coulisses

De longues semaines de préparations, des centaines de mails, des dizaines d’heures de réunions téléphoniques et en 2 jours tout est fini. Mais quel plaisir d’avoir participé à cette aventure avec Jean-Pierre, Philippe, David et Charly. Le SQLSaturday 2014 était sans aucun doute le meilleur SQLSaturday auquel j’ai assisté ! Bon OK, ce n’était que mon 2ème SQLSat et je n’ai pas vu grand chose car trop occupé par l’organisation, je n’ai assisté à aucune session le samedi ! Mais aucun regret, je n’ai peut être pas pu améliorer mes compétences techniques, mais j’ai tout de même appris beaucoup de choses.

Pré-conférences du vendredi

C’était une première pour le GUSS et un gros pari. J’avoue avoir été sceptique quand nous avons débattu de l’idée d’organiser une journée de pré-conférence payante en France dont une en anglais. Et finalement, les chiffres sont là, il y avait bien 35 personnes le jour J ! Pour une première, c’est plutôt un succès. Je pense que cette réussite est pour beaucoup liée à la qualité des speakers et du faible tarif : si on s’y prenait en avance, la journée de formation revenait à moins de 100€. Bref, un rapport qualité/prix exceptionnel !

Pour ma part, j’avais choisi d’assister à la conférence de David Barbarin et Christophe Laporte (2 maitres DBA que l’on ne présente plus) sur le stockage des données et les restaurations. La matinée avec David était très intéressante et enrichissante mais peu accessible car assez compliquée. Je me suis fait la réflexion suivante à la pause déjeuner : « On ne fait pas le même métier, j’ai encore pas mal de chose à apprendre avant d’arriver à un tel niveau ! ».

Après le repas, Christophe nous a mis en situation de base corrompue en production avec pleins de scénario de restauration. Je pensais bien maitriser les restaurations, j’ai donc révisé pas mal de choses connues mais j’ai tout de même réussi à apprendre de nouvelles choses. A mon avis, cette session est un incontournable pour n’importe quel DBA !

J’ai passé toute la journée avec 2 puits de science et je ne regrette pas d’avoir posé une journée de congés et d’avoir payé cette formation.

Repas Speakers/Sponsors

Certainement une des choses les plus agréables de ce week end : un repas avec des speakers et sponsors du monde entier ! Alors oui, mon anglais est approximatif, mais c’est tout de même agréable de se retrouver en compagnie de passionnés autour d’un plat et de quelque(s) verre(s) (hic) La seule chose à retenir, c’est que la nuit fut courte 🙂

SQLSaturday Paris : le jour J

Le SQLSaturday Paris cette année pour les visiteurs, c’était 20 sessions techniques dont 5 en anglais, 4 Chalk-Talk (dont 1 sur les femmes dans l’IT qui fut très animé) et au moins 150 participants. Pour nous coté organisation, c’était l’accueil des visiteurs, l’organisation des repas, la gestion des sponsors, l’aide aux speakers, les différents concours, bref beaucoup de tâches réalisées dans l’ombre.

Je n’ai pas eu le temps d’assister aux sessions mais j’ai tout de même rencontré énormément de personnes intéressantes ce samedi :

– Des constructeurs sympas avec qui j’ai pu discuter technique et stockage sur leur baie de disques ultra performante (Nimble Storage et Violin). J’espère trouver un arrangement avec eux pour tester leur baie et écrire un article ici !

– Des éditeurs de logiciel pour SQL Server (MyAms Idera et Apex SQL). Alors, ca ne vaut pas Kankuru hein, mais ils se débrouillent pas mal 😉 Je me rends compte que je n’ai pas rencontré XLCubed mais comme je ne fais pas trop de BI…

– Des acteurs importants du monde SQL Server comme les sociétés de services (Neos Sdi, Azeo, D-Cube, Masao, Umanis).

Les photos

L’accueil des participants et la distribution des badges

accueil_badge

Le Chalk-Talk Women in IT

chalk_talk_women_in_it

Le concours de Selfie avec Jean-Pierre

concours_selfie

Et l’afterwork le samedi soir

after1

after2

Remerciements et conclusions

Je tiens à remercier :

– l’école SupInfo, qui nous a prêté ses locaux pour les 2 journées. Ce n’est pas une mince contribution car les locaux sont superbes et la vue sur Paris depuis le 40ème étage est juste magnifique !

– les étudiants de l’école Supinfo, qui ont réalisé un travail énorme sur les 2 jours et ont permis le bon déroulement de ce SQLSaturday. En plus, c’est toujours un plaisir de voir des étudiants s’investir dans la communauté.

– les sponsors, qui ont financé l’évènement. Pour rappel, le GUSS est une association à but non lucratif et notre seule source de revenu pour financer ces évènements vient des sponsors.

– les membres du board qui ont organisé le SQLSaturday (je n’aurais pas pu le faire seul malgré tout mon talent 😉 ) mais aussi Galla et Ratana qui m’ont relayé une bonne partie de la journée pour accueillir les participants.

J’ai passé 2 journées sympas en compagnie du GUSS, un peu de stress au début, beaucoup de technique le vendredi et pas mal de discussions sympas le samedi. Et maintenant ? Maintenant, les JSS arrivent à grand pas (1 et 2 décembre) et je pense cette fois-ci assister à un maximum de sessions et pourquoi pas en animer une…

Purger la base SSISDB

Purger la base SSISDB

Lorsque nous installons un catalogue SSIS sur nos instances, SSIS crée sa propre base de données SSISDB. Elle contient notamment les logs d’exécution de tous les packages. Après plusieurs mois d’activité, on peut constater que cette base peut grossir énormément.

Configurer la rétention

Dans SSMS, on peut configurer une période de rétention dans les propriétés d’un catalogue SSIS.

retention01

Il faut préciser le nombre de jours dans « Retention Period (days) » et s’assurer que « Clean Logs Periodically ». Par défaut, le nombre de jours est fixé à 365.

retention02

Le Job « SSIS Server Maintenance Job » utilise cette configuration pour purger la base SSISDB régulièrement. C’est la procédure stockée internal.cleanup_server_retention_window qui s’occupe de faire le ménage.

retention03

Script Maison

Voici un script qui permet de purger la base SSISDB en précisant un nombre de jours de rétention. Oui c’est toujours plus marrant de le faire soit même mais ce n’est surement pas supporté par Microsoft 🙂

USE SSISDB;
SET NOCOUNT ON;

DECLARE @NbJourRetention int = 90;

IF object_id('tempdb..#OperationASupprimer') IS NOT NULL
BEGIN
DROP TABLE #OperationASupprimer;
END;

CREATE TABLE #OperationASupprimer
(
operation_id bigint NOT NULL PRIMARY KEY
);

INSERT INTO
#OperationASupprimer
(
operation_id
)
SELECT
IO.operation_id
FROM
internal.operations AS IO
WHERE
IO.start_time < DATEADD(day, -@NbJourRetention, GETDATE());

DELETE T
FROM internal.event_message_context AS T
WHERE EXISTS( SELECT * FROM #OperationASupprimer oas WHERE oas.operation_id = T.operation_id);

DELETE T
FROM internal.event_messages AS T
WHERE EXISTS( SELECT * FROM #OperationASupprimer oas WHERE oas.operation_id = T.operation_id);

DELETE T
FROM internal.operation_messages AS T
WHERE EXISTS( SELECT * FROM #OperationASupprimer oas WHERE oas.operation_id = T.operation_id);

DELETE T
FROM internal.operations AS T
WHERE EXISTS( SELECT * FROM #OperationASupprimer oas WHERE oas.operation_id = T.operation_id);

Dans le script ci-dessus, la base ne contiendra plus que les 90 derniers jours de log. Il suffit de changer la valeur de la variable @NbJourRetention pour une période de rétention plus ou moins importante.

Si vous n’avez jamais purgé la base SSISDB, exécutez cette requête en diminuant progressivement le nombre de jours de rétention et en sauvegardant le journal de log de la base. Sinon celui ci pourrait grossir d’un seul coup !

Il ne vous reste plus qu’à planifier ce job régulièrement. 1 fois par semaine dans une période creuse par exemple.

Pagination

Régulièrement, je rencontre des projets, essentiellement web, dans lesquels il y a des besoins de pagination de liste. Par exemple, un site e-commerce a besoin d’afficher une liste de produits d’une catégorie. Bien souvent, les développeurs râlent après SQL Server car il n’y aurait pas d’options comme le LIMIT de MySQL. Je vais donc vous montrer comment réaliser une pagination avec SQL Server.

Le besoin

Dans cet article, je vais partir du principe que je travaille pour un site e-commerce qui me demande d’afficher la liste des produits en fonction de la catégorie choisie. Il faudra que cette liste n’affiche que 20 articles par page et bien sur que les pages soient affichées très rapidement.

Mise en place

Pour réaliser ces tests, j’ai créé la table produits :

CREATE TABLE [dbo].[produits](
    [id] [int] NOT NULL,
    [libelle] [varchar](250) NULL,
    [prix] [decimal](9, 2) NULL,
    [categoryId] [int] NULL,
    [ordreAffichage] [int] NULL,
    [description] [varchar](500) NULL,
CONSTRAINT [PK_produits] PRIMARY KEY CLUSTERED([id] ASC))

J’ai ensuite chargé 110 000 lignes de données dans cette table répartie en 3 catégories. J’ai généré les données dans Excel en incrémentant des id et des libellés.

Données de la pagination

ALL in the DOM

Une des techniques de pagination rencontrée fréquemment sur de petits sites est la technique que j’appelle « ALL in the DOM ». Elle consiste à charger tous les produits de la catégorie dans la page et de gérer la pagination avec JavaScript. La rapidité d’intégration de cette solution est sans doute un avantage mais les performances peuvent très vite se montrer désastreuses ! En effet, si on doit surcharger la page de plusieurs centaines d’articles qui ne seront sans doute jamais consultés, l’affichage pourrait être extrêmement long. Et c’est sans compter le requêtage de la table qui devra lire beaucoup de pages (et d’IO) ainsi que la bande passante du server SQL qui devra transférer les données à l’application. Bref, cette solution est à réserver exclusivement pour des projets amateurs à faible volumétrie.

La requête exécutée sur le serveur ressemblerait à ceci :

DECLARE @CATEGORY int = 3

select id
, libelle
, prix
, categoryId
, description
from produits
where categoryId = @CATEGORY
order by ordreAffichage

Table temporaire

Une autre solution que j’ai rencontré, mais que je ne recommande pas, est d’utiliser des TOP avec une table temporaire. Une première étape consiste à créer un table temporaire qui contient toutes les lignes jusqu’à la fin de la pagination : par exemple, avec 20 produits par page pour afficher la 5ème page, la table temporaire contiendra les 100 premiers produits. Dans la 2ème étape, il suffit ensuite de sélectionner avec un top 20 les produits de la page.

DECLARE @DEBUT int = 500
DECLARE @LONGUEUR int = 20
DECLARE @CATEGORY int = 3

select top (@DEBUT + @LONGUEUR) id
, libelle
, prix
, categoryId
, description
, ordreAffichage
into #t
from produits
where categoryId = @CATEGORY
order by ordreAffichage

select top (@LONGUEUR) *
from #t
order by ordreAffichage desc

drop table #t

Avec ce système, plus l’utilisateur paginera et plus les performances se dégraderont. Avec de nombreux utilisateurs, la TEMPDB pourrait grossir très vite.

CTE et Fenêtrage

Depuis la version 2005, on peut utiliser les CTE pour les requêtes récursives et avec la fonction de fenêtrage ROWNUMBER qui permet d’attribuer un rang à une ligne, nous sommes capable de ne récupérer que les lignes qui nous intéresse.

DECLARE @DEBUT int = 500
DECLARE @LONGUEUR int = 20
DECLARE @CATEGORY int = 3

;WITH cte AS
(
select id
, libelle
, prix
, categoryId
, description
, ROW_NUMBER() OVER (ORDER BY ordreAffichage) as rang
from produits
where categoryId = @CATEGORY
)
select id
, libelle
, prix
, categoryId
, description
FROM cte
WHERE rang > @DEBUT AND rang <= @DEBUT + @LONGUEUR

Je pense que cette solution (ou une adaptation de celle-ci) est bonne pour les SQL Server < 2012.

OFFSET et FETCH

Avec SQL Server 2012 et 2014, OFFSET et FETCH ont fait leur apparition. Cette méthode se rapproche le plus du LIMIT connu sur MySQL.

DECLARE @DEBUT int = 80000
DECLARE @LONGUEUR int = 20
DECLARE @CATEGORY int = 3

select id
, libelle
, prix
, categoryId
, description
from produits
where categoryId = @CATEGORY
order by ordreAffichage
offset @DEBUT rows
fetch next @LONGUEUR rows only

Au niveau performance, elle est quasiment identique aux CTE + ROWNUMBER mais elle est bien plus lisible.

TVP et Cache Client

Cette méthode est plus complexe à mettre en oeuvre. En effet, il est nécessaire d’utiliser les TVP dans SQL Server et gérer un cache au niveau de l’application.
Pour utiliser une TVP, il est nécessaire de créer un type TABLE au niveau de la base de données comme ceci :

CREATE TYPE dbo.TypePagination AS TABLE
(
id int
)

Lors du premier affichage d’une catégorie, il convient de le récupérer les ID de tous les produits de la catégorie et de les stocker dans le cache de l’application.

DECLARE @CATEGORY int = 3

select id
from produits
where categoryId = @CATEGORY
order by ordreAffichage

Lors de la pagination par l’utilisateur, il convient ensuite de récupérer les 20 ID suivant :

select id
, libelle
, prix
, categoryId
, description
from produits p
where exists (select * from @TypePagination t where t.id = p.id)
order by ordreAffichage

Cette méthode est de loin la plus performante si l’indexation de la table produits est réalisée correctement. L’inconvénient est la difficulté (relative) à sa mise en place, la consommation de ressources supérieure au niveau du serveur applicatif et la lenteur du premier affichage de la liste de produit (mais on peut compenser ceci par une procédure de chargement de cache lors du reboot du serveur applicatif).

Comparatif

Afin de tester les performances, j’ai exécuté 10 fois chacune des requêtes et j’ai calculé des moyennes sur le CPU Time, l’Elapsed Time et le Logical Reads. J’ai fait ce test pour la 1ère et la 50ème page de la liste de produit.

Comparatif Performance Pagination

La méthode avec la table temporaire est de moins en moins efficace en avançant dans la pagination. De plus elle a beaucoup d’inconvénients à cause de la création de tables temporaires.

La méthode All in the DOM est catastrophique ! Surtout que dans cet exercice, je ne tiens compte que de la consommation au niveau de la base de données. Il reste ensuite à la page Web de gérer la pagination.

La technique Offset + Fetch semble être un bon compromis entre performance et facilité de développement et reste stable au fur et à mesure de l’avancée dans la pagination. Cependant la méthode utilisant CTE + fonction de fenêtrage est très proche.

La dernière méthode utilisant la TVP est la plus performante au niveau base de données. Mais comme la performance doit se mesurer dans son ensemble avec la chaine complète et qu’une partie du travail est réalisé coté applicatif, il faut tester en fonction de vos besoins.

Plan de Maintenance

Plan de maintenance – a quoi ça sert ?

Le plan de maintenance d’une instance SQL Server est l’ensemble des jobs qui permettent de garantir le bon fonctionnement des bases de données dans le temps.

Est-ce indispensable ?

Imaginez une instance de bases de données non maintenue. Sans action sur vos indexs, les performances deviendraient catastrophiques. Sans sauvegarde, vous risqueriez de perdre toutes vos données, les journaux de logs satureraient l’espace de vos disques. Et ce n’est qu’un avant-gout des problèmes qui pourraient se multiplier. Je suis donc obligé de dire que la mise en place d’un plan de maintenance est obligatoire. C’est même une des premières choses à définir lors d’une installation.

Les différentes étapes

La sauvegarde

C’est surement à la sauvegarde de nos bases de données que l’on pense en premier quand on parle de plan de maintenance. Vous devez adapter votre stratégie de sauvegarde en fonction de vos besoins. Par exemple, pour un petit site web relativement statique avec un recovery model simple, une sauvegarde FULL chaque nuit lors des heures creuses d’activité pourrait suffire. Par contre, pour un gros site e-commerce, une sauvegarde FULL quotidienne + des sauvegardes incrémentales régulières + des sauvegardes des logs toutes les 5 minutes pourrait être envisageable.

Bref, cet article n’a pas vocation à vous aider à choisir une stratégie de sauvegarde mais plutôt de ne pas oublier des étapes importantes dans votre plan de maintenance.

N’oubliez pas que le but de la sauvegarde des bases de données est de pouvoir les restaurer ! Il faut donc penser à vérifier que vos sauvegardes sont opérationnelles.

Echec restore

Je sauvegarde toujours dans un nouveau fichier, je ne suis pas vraiment adepte de plusieurs sauvegardes dans un seul fichier (bien qu’il y ait quelques avantages). Ce nouveau fichier est horodaté dans son nom. Avec ce système, je suis donc obligé de créer une étape de suppression des anciennes sauvegardes pour ne pas remplir mes disques.

J’ai souvent la question sur la sauvegarde des bases systèmes. Oui il est préférable de sauvegarder au minimum MASTER (pour le mot de passe des logins par exemple), MSDB (pour toutes les données de l’agent) et éventuellement MODEL.

En principe, je sauvegarde également régulièrement dans des fichiers SQL le script des logins, les server objects et les objets de l’agent. Ce n’est pas obligatoire, mais ça permet souvent de gagner du temps.

La vérification d’intégrité

La vérification d’intégrité des bases de données est également incontournable. Si une de vos bases est corrompue, il faut être alerté le plus rapidement possible pour réduire la perte de données.

En général, j’exécute un DBCC CHECKDB avant de faire les sauvegardes complètes. Pour réduire le coût de cette opération, on peut alterner avec un DBCC CHECKDB et l’option WITH PHYSICAL_ONLY.

Integrité de vos bases

Je recommande de conserver la dernière sauvegarde FULL effectuée juste après le dernier DBCC CHECKDB (FULL). En cas de corruption, on peut repartir d’une sauvegarde saine.

Les indexs et statistiques

A chaque insertion / suppression / mise à jour de données dans une table, ses indexs se fragmentent.

Si une maintenance des indexs et des statistiques n’est pas effectuée, les performances globales de la base de données vont se dégrader. En effet, un index fragmenté va obliger à lire plus de pages de données donc plus d’IO et plus de RAM seront nécessaires. Si les statistiques ne sont pas à jour, l’optimiseur pourrait décider d’utiliser un plan d’exécution non adapté ce qui aurait pour effet de consommer trop de ressources et de ralentir les temps d’exécution des requêtes.

Il convient donc de défragmenter régulièrement les indexs et de recalculer les statistiques. Cette tache est une partie du plan de maintenance indispensable à une base de données.

On pourrait être tenté de reconstruire chaque index mais pour une base de données utilisée 24/24h, ça pourrait ne pas coller avec les besoins métier car cette opération peut être très couteuse. L’idéal est donc d’utiliser des scripts et en général j’applique cette règle :
– Si l’index est fragmenté à plus de 30% alors je reconstruis l’index (à la reconstruction de l’index, les statistiques sont recrées donc pas besoin de refaire une mise à jour).
– si l’index est fragmenté entre 10 et 30% alors je réorganise l’index et je recalcule les statistiques.
– sinon je ne fais rien.

defrag

N’oubliez pas qu’en version Enterprise, les indexs peuvent être mis à jour ONLINE. C’est plus lent mais la base est toujours accessible.

Si un index reste toujours fragmenté et possède moins de 8 pages, inutile de s’acharner à le défragmenter…

Le nettoyage

La dernière tache indispensable dans un plan de maintenance est la suppression des historiques de sauvegardes et des jobs de l’agent. En effet, si vous ne le faites pas, la base MSDB deviendra énorme.

Nettoyer les historiquesEssayez de restaurer rapidement une base à partir d’une sauvegarde FULL + quelques sauvegardes de LOGS avec un historique de quelques mois et vous comprendrez ce dont je veux parler 😉

Conclusion

Le plan de maintenance est une des taches de base d’un DBA et devrait être mis en place sur chaque instance. Il n’existe pas un plan de maintenance type car c’est à vous d’adapter votre plan au besoin de l’entreprise.

N’oubliez pas que grâce à Kankuru, vous pouvez surveiller que vos plans fonctionnent correctement sur tous vos serveurs !

Il est possible que vos plans de maintenance contiennent des étapes supplémentaires. N’hésitez pas à les décrire dans vos commentaires.

Sql Server 2014

SQL Server 2014 : les nouveautés

Hier, Microsoft a officiellement lancé SQL Server 2014. Une version d’évaluation gratuite de 180 jours est disponible sur le site Microsoft.

Je pensais écrire un article complet sur toutes les améliorations qu’apporte cette nouvelle release SQL Server 2014 mais le GUSS l’avait déjà fait avant moi lors des Journées SQL Server. Je vous propose donc de visionner la vidéo de la session :

Pour moi, la fonctionnalité qui m’intéresse le plus est bien sur Hekaton. J’y vois beaucoup de possibilités de gain de performance. C’est pourquoi, je vous propose également une seconde vidéo, enregistrée en décembre lors des journées SQL Server, entièrement dédiée au In-Memory. En plus, elle est présentée par un des meilleurs speakers : Christophe Laporte :

Je pense que dans le futur, je publierai des exemples concrets d’utilisations du In-Memory…

Débuter avec SMO et C# : performances SetDefaultInitFields

Bonjour,

Dans l’article précédent, je vous ai montré comment ouvrir une connexion à une instance SQL Server avec SMO et C# puis à lister le nom des bases de données. Avant d’aller plus loin dans l’apprentissage de SMO, il y a une notion très importante à retenir pour avoir de bonnes performances : SetDefaultInitFields.

Si je lance une trace SQL lors de l’exécution du programme qui liste le nom des bases de données, je vais voir que 5 requêtes ont été exécutées. Les 4 premières sont propre à l’utilisation de SMO, il a besoin d’obtenir des informations sur l’instance et la base avant de générer sa requête. Celle qui nous intéresse est la dernière qui sert à lister le nom des bases de données :


SELECT
dtb.name AS [Name]
FROM
master.sys.databases AS dtb
ORDER BY
[Name] ASC

SQL Profiler et SMO

Jusqu’ici, la requête générée est plutôt propre.

Lister la collation de chaque base de données

Je vais un peu modifier le code afin d’ajouter la collation de chaque base de données


string _instanceName = "localhost";
smoCommon.ServerConnection sc = new smoCommon.ServerConnection(_instanceName);
sc.Connect();
smo.Server monServeur = new smo.Server(sc);
foreach (smo.Database maBase in monServeur.Databases)
{
    Console.WriteLine(maBase.Name + " : " + maBase.Collation);
}
sc.Disconnect();

SMO Affichage des bases et des collations

Analyse de la trace SQL Profiler

Voici le résultat de la trace SQL Profiler. Comme vous pouvez le constater, nous sommes loin d’un traitement ensembliste et efficace ! Il y a une seule requête pour récupérer le nom de toutes les bases de données mais ensuite une requête par base de données pour récupérer la collation !

SQL Profiler SMO sans SetDefaultInitFields

Comment rendre le traitement ensembliste ?

Il existe une méthode pour améliorer la génération de l’écriture de la requête. Il s’agit de SetDefaultInitFields. Cette méthode permet de préciser à SMO qu’il doit récupérer certaines ou toutes les propriétés en une seule étape.

Je vais maintenant modifier mon code afin d’utiliser cette méthode pour récupérer également la collation :


string _instanceName = "localhost";
smoCommon.ServerConnection sc = new smoCommon.ServerConnection(_instanceName);
sc.Connect();
smo.Server monServeur = new smo.Server(sc);
monServeur.SetDefaultInitFields(typeof(smo.Database), "Collation");
foreach (smo.Database maBase in monServeur.Databases)
{
 Console.WriteLine(maBase.Name + " : " + maBase.Collation);
}
sc.Disconnect();

Et voici donc le résultat de la trace SQL Profiler.
SQL Profiler SMO avec SetDefaultInitFields

Il n’y a plus qu’une seule requête pour récupérer le nom et la collation :


SELECT
dtb.name AS [Name],
dtb.collation_name AS [Collation],
dtb.name AS [DatabaseName2]
FROM
master.sys.databases AS dtb
ORDER BY
[Name] ASC

Conclusions

Je vous ai montré cette méthode pour les bases de données, mais elle est également valable pour tous les autres objets SMO (les tables, les procédures stockées, les vues, etc…). Imaginez les gains de performance sur une base de données à plusieurs milliers de procédures stockées !

Attention tout de même : si vous écrivez mal la ou les propriétés dans la méthode SetDefaultInitFields, vous n’aurez pas d’erreur à la compilation puisque c’est une stringCollection. L’erreur surviendrait à l’exécution.