Database
 sql >> Base de données >  >> RDS >> Database

Journalisation minimale avec INSERT…SELECT et contexte de chargement rapide

Ce message fournit de nouvelles informations sur les conditions préalables au chargement groupé minimalement enregistré lors de l'utilisation de INSERT...SELECT dans les tables indexées .

L'installation interne qui permet ces cas s'appelle FastLoadContext . Il peut être activé de SQL Server 2008 à 2014 inclus à l'aide de l'indicateur de trace documenté 610. À partir de SQL Server 2016, FastLoadContext est activé par défaut ; l'indicateur de suivi n'est pas requis.

Sans FastLoadContext , les seules insertions d'index pouvant être enregistrées de manière minimale sont ceux dans un vide index clusterisé sans index secondaires, comme indiqué dans la deuxième partie de cette série. La journalisation minimale les conditions pour les tables de tas non indexées ont été couvertes dans la première partie.

Pour plus d'informations, consultez le guide de chargement des performances des données et l'équipe Tiger notes sur les changements de comportement pour SQL Server 2016.

Contexte de chargement rapide

Pour rappel, le RowsetBulk installation (couverte dans les parties 1 et 2) permet une journalisation minimale chargement groupé pour :

  • Tas vide et non vide tableaux avec :
    • Verrouillage des tables ; et
    • Aucun index secondaire.
  • Tables groupées vides , avec :
    • Verrouillage des tables ; et
    • Aucun index secondaire ; et
    • DMLRequestSort=true sur l'insertion d'index cluster opérateur.

Le FastLoadContext le chemin du code ajoute la prise en charge de minimally-logged et concurrent chargement groupé sur :

  • Vides et non vides regroupés index b-tree.
  • Vides et non vides non clusterisés index b-tree maintenus par un dédié Insertion d'index opérateur de plan.

Le FastLoadContext nécessite également DMLRequestSort=true sur l'opérateur du plan correspondant dans tous les cas.

Vous avez peut-être remarqué un chevauchement entre RowsetBulk et FastLoadContext pour les tables en cluster vides sans index secondaires. Un TABLOCK l'indice n'est pas nécessaire avec FastLoadContext , mais il n'est pas obligatoire d'être absent Soit. En conséquence, un insert approprié avec TABLOCK peut toujours être éligible à la journalisation minimale via FastLoadContext en cas d'échec, le RowsetBulk détaillé tests.

FastLoadContext peut être désactivé sur SQL Server 2016 à l'aide de l'indicateur de trace documenté 692. L'événement étendu du canal de débogage fastloadcontext_enabled peut être utilisé pour surveiller FastLoadContext utilisation par partition d'index (ensemble de lignes). Cet événement ne se déclenche pas pour RowsetBulk charges.

Journalisation mixte

Un seul INSERT...SELECT instruction utilisant FastLoadContext peut se connecter entièrement certaines lignes pendant la journalisation minimale autres.

Les lignes sont insérées une à la fois par l'insertion d'index opérateur et entièrement connecté dans les cas suivants :

  • Toutes les lignes ajoutées à la première page d'index, si l'index était vide au début de l'opération.
  • Lignes ajoutées à existantes pages d'index.
  • Lignes déplacées entre les pages par un fractionnement de page.

Sinon, les lignes du flux d'insertion commandé sont ajoutées à une toute nouvelle page à l'aide d'un outil optimisé et minimalement enregistré chemin de code. Une fois qu'autant de lignes que possible sont écrites dans la nouvelle page, celle-ci est directement liée à la structure d'index cible existante.

La page nouvellement ajoutée ne sera pas nécessairement être complet (bien que ce soit évidemment le cas idéal) car SQL Server doit faire attention à ne pas ajouter de lignes à la nouvelle page qui appartiennent logiquement à un existant sommaire. La nouvelle page sera "cousue" dans l'index en tant qu'unité, de sorte que nous ne pouvons pas avoir de lignes sur la nouvelle page qui appartiennent ailleurs. Il s'agit principalement d'un problème lors de l'ajout de lignes dans la plage de clés existante de l'index, plutôt qu'avant le début ou après la fin de la plage de clés d'index existante.

C'est encore possible pour ajouter de nouvelles pages dans la plage de clés d'index existante, mais les nouvelles lignes doivent être triées plus haut que la clé la plus élevée sur le précédent page d'index existante et trier plus bas que la clé la plus basse sur le suivant page d'index existante. Pour avoir les meilleures chances d'obtenir une journalisation minimale dans ces circonstances, assurez-vous que les lignes insérées ne chevauchent pas autant que possible les lignes existantes.

Conditions DMLRequestSort

N'oubliez pas que FastLoadContext ne peut être activé que si DMLRequestSort est défini sur true pour l'insertion d'index correspondante opérateur dans le plan d'exécution.

Il existe deux chemins de code principaux qui peuvent définir DMLRequestSort à vrai pour les insertions d'index. L'un ou l'autre chemin retour vrai est suffisant.

1. FOptimiserInsérer

Le sqllang!CUpdUtil::FOptimizeInsert le code nécessite :

  • Plus de 250 lignes estimé à insérer ; et
  • Plus de 2 pages estimé insérer la taille des données ; et
  • L'indice cible doit avoir moins de 3 pages feuilles .

Ces conditions sont les mêmes que pour RowsetBulk sur un index cluster vide, avec une exigence supplémentaire pour pas plus de deux pages d'index de niveau feuille. Notez bien que cela fait référence à la taille de l'index existant avant l'insertion, pas la taille estimée des données à ajouter.

Le script ci-dessous est une modification de la démo utilisée dans les parties précédentes de cette série. Il montre une journalisation minimale lorsque moins de trois pages d'index sont remplies avant le test INSERT...SELECT court. Le schéma de table de test est tel que 130 lignes peuvent tenir sur une seule page de 8 Ko lorsque la gestion des versions de ligne est désactivée pour la base de données. Le multiplicateur dans le premier TOP la clause peut être modifiée pour déterminer le nombre de pages d'index existantes avant le test INSERT...SELECT est exécuté :

IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL
BEGIN
    DROP TABLE dbo.Test;
END;
GO
CREATE TABLE dbo.Test 
(
    id integer NOT NULL IDENTITY
        CONSTRAINT [PK dbo.Test (id)]
        PRIMARY KEY,
    c1 integer NOT NULL,
    padding char(45) NOT NULL
        DEFAULT ''
);
GO
-- 130 rows per page for this table 
-- structure with row versioning off
INSERT dbo.Test
    (c1)
SELECT TOP (3 * 130)    -- Change the 3 here
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV;
GO
-- Show physical index statistics
-- to confirm the number of pages
SELECT
    DDIPS.index_type_desc,
    DDIPS.alloc_unit_type_desc,
    DDIPS.page_count,
    DDIPS.record_count,
    DDIPS.avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats
(
    DB_ID(), 
    OBJECT_ID(N'dbo.Test', N'U'), 
    1,      -- Index ID
    NULL,   -- Partition ID
    'DETAILED'
) AS DDIPS
WHERE
    DDIPS.index_level = 0;  -- leaf level only
GO
-- Clear the plan cache
DBCC FREEPROCCACHE;
GO
-- Clear the log
CHECKPOINT;
GO
-- Main test
INSERT dbo.Test
    (c1)
SELECT TOP (269)
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV;
GO
-- Show log entries
SELECT
    FD.Operation,
    FD.Context,
    FD.[Log Record Length],
    FD.[Log Reserve],
    FD.AllocUnitName,
    FD.[Transaction Name],
    FD.[Lock Information],
    FD.[Description]
FROM sys.fn_dblog(NULL, NULL) AS FD;
GO
-- Count the number of  fully-logged rows
SELECT 
    [Fully Logged Rows] = COUNT_BIG(*) 
FROM sys.fn_dblog(NULL, NULL) AS FD
WHERE 
    FD.Operation = N'LOP_INSERT_ROWS'
    AND FD.Context = N'LCX_CLUSTERED'
    AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)';
GO

Lorsque l'index clusterisé est préchargé avec 3 pages , l'insert de test est entièrement enregistré (enregistrements détaillés du journal des transactions omis par souci de brièveté) :

Lorsque le tableau est préchargé avec seulement 1 ou 2 pages , l'insert de test est enregistré au minimum :

Lorsque le tableau n'est pas préchargé avec n'importe quelle page, le test équivaut à exécuter la démonstration de table en cluster vide de la deuxième partie, mais sans le TABLOCK indice :

Les 130 premières lignes sont entièrement enregistrées . C'est parce que l'index était vide avant de commencer et que 130 lignes tiennent sur la première page. N'oubliez pas que la première page est toujours entièrement enregistrée lorsque FastLoadContext est utilisé et l'index était vide auparavant. Les 139 lignes restantes sont insérées avec une journalisation minimale .

Si un TABLOCK un indice est ajouté à l'encart, toutes les pages sont minimalement enregistrés (y compris le premier) car le chargement de l'index cluster vide se qualifie désormais pour le RowsetBulk mécanisme (au prix de la prise d'un Sch-M verrou).

2. FDemandRowsSortedForPerformance

Si le FOptimizeInsert les tests échouent, DMLRequestSort peut toujours être défini sur true par un deuxième ensemble de tests dans le sqllang!CUpdUtil::FDemandRowsSortedForPerformance code. Ces conditions sont un peu plus complexes, il sera donc utile de définir quelques paramètres :

  • P – nombre de pages de niveau feuille existantes dans l'index cible .
  • Iestimé nombre de lignes à insérer.
  • R =P / I (pages cibles par ligne insérée).
  • T – nombre de partitions cibles (1 pour non partitionné).

La logique pour déterminer la valeur de DMLRequestSort est alors :

  • Si P <= 16 renvoie faux , sinon :
    • Si R < 8 :
      • Si P > 524 renvoie vrai , sinon faux .
    • Si R >= 8 :
      • Si T > 1 et I > 250 renvoie vrai , sinon faux .

Les tests ci-dessus sont évalués par le processeur de requêtes lors de la compilation du plan. Il y a une condition finale évalué par le code du moteur de stockage (IndexDataSetSession::WakeUpInternal ) au moment de l'exécution :

  • DMLRequestSort est actuellement vrai; et
  • I >= 100 .

Nous décomposerons ensuite toute cette logique en éléments gérables.

Plus de 16 pages cibles existantes

Le premier essai P <= 16 signifie que les index avec moins de 17 pages feuille existantes ne seront pas éligibles pour FastLoadContext via ce chemin de code. Pour être tout à fait clair sur ce point, P est le nombre de pages de niveau feuille dans l'index cible avant le INSERT...SELECT est exécuté.

Pour illustrer cette partie de la logique, nous allons précharger le tableau groupé de test avec 16 pages de données. Cela a deux effets importants (rappelez-vous que les deux chemins de code doivent renvoyer false se retrouver avec un faux valeur pour DMLRequestSort ):

  1. Il garantit que le précédent FOptimizeInsert le test échoue , car la troisième condition n'est pas remplie (P < 3 ).
  2. Le P <= 16 condition dans FDemandRowsSortedForPerformance ne sera également pas être rencontré.

Nous attendons donc FastLoadContext ne pas être activé. Le script de démonstration modifié est :

IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL
BEGIN
    DROP TABLE dbo.Test;
END;
GO
CREATE TABLE dbo.Test 
(
    id integer NOT NULL IDENTITY
        CONSTRAINT [PK dbo.Test (id)]
        PRIMARY KEY,
    c1 integer NOT NULL,
    padding char(45) NOT NULL
        DEFAULT ''
);
GO
-- 130 rows per page for this table 
-- structure with row versioning off
INSERT dbo.Test
    (c1)
SELECT TOP (16 * 130) -- 16 pages
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV;
GO
-- Show physical index statistics
-- to confirm the number of pages
SELECT
    DDIPS.index_type_desc,
    DDIPS.alloc_unit_type_desc,
    DDIPS.page_count,
    DDIPS.record_count,
    DDIPS.avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats
(
    DB_ID(), 
    OBJECT_ID(N'dbo.Test', N'U'), 
    1,      -- Index ID
    NULL,   -- Partition ID
    'DETAILED'
) AS DDIPS
WHERE
    DDIPS.index_level = 0;  -- leaf level only
GO
-- Clear the plan cache
DBCC FREEPROCCACHE;
GO
-- Clear the log
CHECKPOINT;
GO
-- Main test
INSERT dbo.Test
    (c1)
SELECT TOP (269)
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV1
CROSS JOIN master.dbo.spt_values AS SV2;
GO
-- Show log entries
SELECT
    FD.Operation,
    FD.Context,
    FD.[Log Record Length],
    FD.[Log Reserve],
    FD.AllocUnitName,
    FD.[Transaction Name],
    FD.[Lock Information],
    FD.[Description]
FROM sys.fn_dblog(NULL, NULL) AS FD;
GO
-- Count the number of  fully-logged rows
SELECT 
    [Fully Logged Rows] = COUNT_BIG(*) 
FROM sys.fn_dblog(NULL, NULL) AS FD
WHERE 
    FD.Operation = N'LOP_INSERT_ROWS'
    AND FD.Context = N'LCX_CLUSTERED'
    AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)';

Les 269 lignes sont entièrement enregistrées comme prévu :

Notez que quel que soit le nombre de nouvelles lignes à insérer, le script ci-dessus ne sera jamais produire une journalisation minimale à cause du P <= 16 test (et P < 3 tester dans FOptimizeInsert ).

Si vous choisissez d'exécuter la démo vous-même avec un plus grand nombre de lignes, commentez la section qui affiche les enregistrements individuels du journal des transactions, sinon vous attendrez très longtemps et SSMS risque de se bloquer. (Pour être juste, cela pourrait le faire de toute façon, mais pourquoi ajouter au risque.)

Taux de pages par ligne insérée

S'il y en a 17 ou plus pages feuilles dans l'index existant, le précédent P <= 16 le test n'échouera pas. La prochaine section de logique traite du ratio de pages existantes aux lignes nouvellement insérées . Cela doit également réussir pour obtenir une journalisation minimale . Pour rappel, les conditions applicables sont :

  • Rapport R =P / I .
  • Si R < 8 :
    • Si P > 524 renvoie vrai , sinon faux .

Il faut aussi retenir le test final du moteur de stockage pour au moins 100 lignes :

  • I >= 100 .

Réorganiser un peu ces conditions, tous des éléments suivants doit être vrai :

  1. P > 524 (pages d'index existantes)
  2. I >= 100 (lignes insérées estimées)
  3. P / I < 8 (rapport R )

Il existe plusieurs façons de remplir ces trois conditions simultanément. Choisissons les valeurs minimales possibles pour P (525) et I (100) donnant un R valeur de (525 / 100) =5,25. Cela satisfait le (R < 8 test), nous nous attendons donc à ce que cette combinaison entraîne une journalisation minimale :

IF OBJECT_ID(N'dbo.Test', N'U') IS NOT NULL
BEGIN
    DROP TABLE dbo.Test;
END;
GO
CREATE TABLE dbo.Test 
(
    id integer NOT NULL IDENTITY
        CONSTRAINT [PK dbo.Test (id)]
        PRIMARY KEY,
    c1 integer NOT NULL,
    padding char(45) NOT NULL
        DEFAULT ''
);
GO
-- 130 rows per page for this table 
-- structure with row versioning off
INSERT dbo.Test
    (c1)
SELECT TOP (525 * 130) -- 525 pages
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV1
CROSS JOIN master.dbo.spt_values AS SV2;
GO
-- Show physical index statistics
-- to confirm the number of pages
SELECT
    DDIPS.index_type_desc,
    DDIPS.alloc_unit_type_desc,
    DDIPS.page_count,
    DDIPS.record_count,
    DDIPS.avg_record_size_in_bytes
FROM sys.dm_db_index_physical_stats
(
    DB_ID(), 
    OBJECT_ID(N'dbo.Test', N'U'), 
    1,      -- Index ID
    NULL,   -- Partition ID
    'DETAILED'
) AS DDIPS
WHERE
    DDIPS.index_level = 0;  -- leaf level only
GO
-- Clear the plan cache
DBCC FREEPROCCACHE;
GO
-- Clear the log
CHECKPOINT;
GO
-- Main test
INSERT dbo.Test
    (c1)
SELECT TOP (100)
    CHECKSUM(NEWID())
FROM master.dbo.spt_values AS SV1
CROSS JOIN master.dbo.spt_values AS SV2;
GO
-- Show log entries
SELECT
    FD.Operation,
    FD.Context,
    FD.[Log Record Length],
    FD.[Log Reserve],
    FD.AllocUnitName,
    FD.[Transaction Name],
    FD.[Lock Information],
    FD.[Description]
FROM sys.fn_dblog(NULL, NULL) AS FD;
GO
-- Count the number of  fully-logged rows
SELECT 
    [Fully Logged Rows] = COUNT_BIG(*) 
FROM sys.fn_dblog(NULL, NULL) AS FD
WHERE 
    FD.Operation = N'LOP_INSERT_ROWS'
    AND FD.Context = N'LCX_CLUSTERED'
    AND FD.AllocUnitName = N'dbo.Test.PK dbo.Test (id)';

Le INSERT...SELECT de 100 lignes est en effet enregistré au minimum :

Réduire l'estimation lignes insérées à 99 (casse I >= 100 ), et/ou réduire le nombre de pages d'index existantes à 524 (casser P > 524 ) entraîne une journalisation complète . Nous pourrions également apporter des modifications telles que R n'est plus inférieur à 8 pour produire une journalisation complète . Par exemple, en définissant P = 1000 et I = 125 donne R = 8 , avec les résultats suivants :

Les 125 lignes insérées ont été entièrement consignées comme prévu. (Cela n'est pas dû à la journalisation complète de la première page, puisque l'index n'était pas vide auparavant.)

Taux de pages pour les index partitionnés

Si tous les tests précédents échouent, le test restant nécessite R >= 8 et ne peut seulement être satisfait lorsque le nombre de partitions (T ) est supérieur à 1 et il y en a plus de 250 estimés lignes insérées (I ). Rappel :

  • Si R >= 8 :
    • Si T > 1 et I > 250 renvoie vrai , sinon faux .

Une subtilité :pour partitionné index, la règle qui dit que toutes les lignes de la première page sont entièrement enregistrées (pour un index initialement vide) s'applique par partition . Pour un objet avec 15 000 partitions, cela signifie 15 000 "premières" pages entièrement enregistrées.

Résumé et réflexions finales

Les formules et l'ordre d'évaluation décrits dans le corps sont basés sur l'inspection du code à l'aide d'un débogueur. Ils ont été présentés sous une forme qui représente fidèlement la synchronisation et l'ordre utilisés dans le code réel.

Il est possible de réorganiser et de simplifier un peu ces conditions, pour produire un résumé plus concis des exigences pratiques pour la journalisation minimale lors de l'insertion dans un b-tree en utilisant INSERT...SELECT . Les expressions raffinées ci-dessous utilisent les trois paramètres suivants :

  • P =nombre de existants indexer les pages de niveau feuille.
  • I =estimé nombre de lignes à insérer.
  • S =estimé insérer la taille des données dans les pages de 8 Ko.

Chargement groupé d'ensembles de lignes

  • Utilise sqlmin!RowsetBulk .
  • Nécessite un vide cible d'index clusterisé avec TABLOCK (ou équivalent).
  • Nécessite DMLRequestSort = true sur l'insertion d'index cluster opérateur.
  • DMLRequestSort est défini true si I > 250 et S > 2 .
  • Toutes les lignes insérées sont minimalement enregistrées .
  • Un Sch-M le verrou empêche l'accès simultané à la table.

Contexte de chargement rapide

  • Utilise sqlmin!FastLoadContext .
  • Active la consignation minimale insère dans les index b-tree :
    • En cluster ou non en cluster.
    • Avec ou sans verrou de table.
    • Index cible vide ou non.
  • Nécessite DMLRequestSort = true sur l'encart d'index associé opérateur de plan.
  • Seules les lignes écrites sur les nouvelles pages sont chargés en masse et enregistrés de manière minimale .
  • La première page d'un index précédemment vide la partition est toujours entièrement enregistrée .
  • Minimum absolu de I >= 100 .
  • Nécessite l'indicateur de trace 610 avant SQL Server 2016.
  • Disponible par défaut à partir de SQL Server 2016 (l'indicateur de trace 692 est désactivé).

DMLRequestSort est défini true pour :

  • N'importe quel index (partitionné ou non) si :
    • I > 250 et P < 3 et S > 2; ou
    • I >= 100 et P > 524 et P < I * 8

Pour les index partitionnés uniquement (avec> 1 partition), DMLRequestSort est également défini true si :

  • I > 250 et P > 16 et P >= I * 8

Il y a quelques cas intéressants découlant de ces FastLoadContext condition :

  • Tous insère dans un fichier non partitionné index avec entre 3 et 524 (inclus) les pages feuille existantes seront entièrement enregistrées quel que soit le nombre et la taille totale des lignes ajoutées. Cela affectera plus particulièrement les grandes insertions dans les petits tableaux (mais pas vides).
  • Tous insère dans un partitionné index entre 3 et 16 les pages existantes seront entièrement enregistrées .
  • Grands inserts à grand non partitionné les index peuvent ne pas être minimalement enregistrés en raison de l'inégalité P < I * 8 . Quand P est grand, un estimé proportionnellement grand nombre de lignes insérées (I ) est requis. Par exemple, un index de 8 millions de pages ne peut pas prendre en charge la journalisation minimale lors de l'insertion de 1 million de lignes ou moins.

Index non cluster

Les mêmes considérations et calculs appliqués aux index clusterisés dans les démos s'appliquent aux index non clusterisés index b-tree également, tant que l'index est maintenu par un opérateur de plan dédié (un large , ou par index plan). Index non clusterisés gérés par un opérateur de table de base (par exemple, Clustered Index Insert ) ne sont pas éligibles pour FastLoadContext .

Notez que les paramètres de la formule doivent être évalués à nouveau pour chaque élément non clusterisé opérateur d'index :taille de ligne calculée, nombre de pages d'index existantes et estimation de la cardinalité.

Remarques générales

Faites attention aux estimations de faible cardinalité à l'insertion d'index opérateur, car ceux-ci affecteront le I et S paramètres. Si un seuil n'est pas atteint en raison d'une erreur d'estimation de cardinalité, l'insertion sera entièrement enregistrée .

N'oubliez pas que DMLRequestSort est mis en cache avec le plan — il n'est pas évalué à chaque exécution d'un plan réutilisé. Cela peut introduire une forme du problème de sensibilité aux paramètres bien connu (également connu sous le nom de "reniflage de paramètres").

La valeur de P (pages feuille d'index) n'est pas actualisé au début de chaque instruction. L'implémentation actuelle met en cache la valeur pour l'ensemble du lot . Cela peut avoir des effets secondaires inattendus. Par exemple, un TRUNCATE TABLE dans le même lot en tant que INSERT...SELECT ne réinitialisera pas P à zéro pour les calculs décrits dans cet article - ils continueront à utiliser la valeur pré-tronquée et une recompilation n'aidera pas. Une solution de contournement consiste à soumettre des modifications importantes dans des lots séparés.

Drapeaux de suivi

Il est possible de forcer FDemandRowsSortedForPerformance pour retourner vrai en définissant non documenté et non pris en charge trace flag 2332, comme je l'ai écrit dans Optimisation des requêtes T-SQL qui modifient les données. Lorsque TF 2332 est actif, le nombre de lignes estimées à insérer doit encore être au moins 100 . TF 2332 affecte la journalisation minimale décision pour FastLoadContext uniquement (il est efficace pour les tas partitionnés dans la mesure où DMLRequestSort est concerné, mais n'a aucun effet sur le tas lui-même, puisque FastLoadContext ne s'applique qu'aux index).

Un large/par index la forme du plan pour la maintenance des index non clusterisés peut être forcée pour les tables rowstore à l'aide de l'indicateur de trace 8790 (non officiellement documenté, mais mentionné dans un article de la base de connaissances ainsi que dans mon article lié pour TF2332 juste au-dessus).

Le tout par Sunil Agarwal de l'équipe SQL Server :

  • Quelles sont les optimisations d'importation groupée ?
  • Optimisations d'importation en masse (journalisation minimale)
  • Modifications minimales de la journalisation dans SQL Server 2008
  • Modifications minimales de la journalisation dans SQL Server 2008 (partie 2)
  • Modifications minimales de la journalisation dans SQL Server 2008 (partie 3)