Je suis principalement un développeur .NET utilisant Entity Framework ORM. Cependant, parce que je ne veux pas échouer dans l'utilisation de l'ORM , j'essaie de comprendre ce qui se passe dans la couche de données (base de données). Fondamentalement, pendant le développement, je démarre le profileur et vérifie ce que certaines parties de code génèrent en termes de requêtes.
Si je repère quelque chose de très compliqué (ORM peut générer des requêtes affreuses même à partir d'instructions LINQ plutôt simples, si elles ne sont pas soigneusement écrites) et/ou lourdes (durée, CPU, lectures de page), je le prends dans SSMS et vérifie son plan d'exécution.
Cela fonctionne bien pour mon niveau de connaissance de la base de données. Cependant, BULK INSERT semble être une créature spéciale, car il ne semble pas produire de SHOWPLAN .
Je vais essayer d'illustrer un exemple très simple:
Définition de la table
CREATE TABLE dbo.ImportingSystemFileLoadInfo
(
ImportingSystemFileLoadInfoId INT NOT NULL IDENTITY(1, 1) CONSTRAINT PK_ImportingSystemFileLoadInfo PRIMARY KEY CLUSTERED,
EnvironmentId INT NOT NULL CONSTRAINT FK_ImportingSystemFileLoadInfo REFERENCES dbo.Environment,
ImportingSystemId INT NOT NULL CONSTRAINT FK_ImportingSystemFileLoadInfo_ImportingSystem REFERENCES dbo.ImportingSystem,
FileName NVARCHAR(64) NOT NULL,
FileImportTime DATETIME2 NOT NULL,
CONSTRAINT UQ_ImportingSystemImportInfo_EnvXIs_TableName UNIQUE (EnvironmentId, ImportingSystemId, FileName, FileImportTime)
)
Remarque: aucun autre index n'est défini sur la table
L'encart en vrac (ce que j'attrape dans le profileur, un seul lot)
insert bulk [dbo].[ImportingSystemFileLoadInfo] ([EnvironmentId] Int, [ImportingSystemId] Int, [FileName] NVarChar(64) COLLATE Latin1_General_CI_AS, [FileImportTime] DateTime2(7))
Mesures
Pour mon application, ce n'est pas grave, bien que les lectures semblent assez grandes (je connais très peu de choses sur SQL Server, donc je compare à la taille de page 8K et aux petites informations d'enregistrement que j'ai)
Question: comment puis-je vérifier si cet INSERT EN VRAC peut être optimisé? Ou cela n'a aucun sens, car c'est sans doute le moyen le plus rapide de pousser des données volumineuses d'une application client vers SQL Server?
Autant que je sache, vous pouvez optimiser un insert en vrac d'une manière très similaire à celle d'un insert régulier. En règle générale, un plan de requête pour une insertion simple n'est pas très informatif, alors ne vous inquiétez pas de ne pas avoir le plan. Je vais passer en revue quelques façons d'optimiser un insert, mais la plupart d'entre elles ne s'appliquent probablement pas à l'insert que vous avez spécifié dans la question. Cependant, ils pourraient être utiles si à l'avenir vous deviez charger de plus grandes quantités de données.
1. Insérer des données dans l'ordre des clés de clustering
SQL Server trie souvent les données avant de les insérer dans une table avec un index cluster. Pour certaines tables et applications, vous pouvez améliorer les performances en triant les données dans le fichier plat et en indiquant à SQL Server que les données sont triées via l'argument ORDER
de BULK INSERT
:
COMMANDE ({colonne [ASC | DESC]} [ ... n])
Spécifie comment les données du fichier de données sont triées. Les performances d'importation en masse sont améliorées si les données importées sont triées en fonction de l'index cluster sur la table, le cas échéant.
Puisque vous utilisez une colonne IDENTITY
comme clé en cluster, vous n'avez pas à vous en préoccuper.
2. Utilisez TABLOCK
si possible
Si vous êtes assuré d'avoir une seule session en insérant des données dans votre table, vous pouvez spécifier l'argument TABLOCK
pour BULK INSERT
. Cela peut réduire la contention des verrous et conduire à journalisation minimale dans certains scénarios. Cependant, vous insérez dans une table avec un index cluster qui contient déjà des données afin que vous n'obteniez pas une journalisation minimale sans l'indicateur de trace 610 qui est mentionné plus loin dans cette réponse.
Si TABLOCK
n'est pas possible, car vous ne pouvez pas changer le code , tout espoir n'est pas perdu. Pensez à utiliser sp_table_option
:
EXEC [sys].[sp_tableoption]
@TableNamePattern = N'dbo.BulkLoadTable' ,
@OptionName = 'table lock on bulk load' ,
@OptionValue = 'ON'
Une autre option consiste à activer indicateur de trace 715 .
3. Utilisez une taille de lot appropriée
Parfois, vous pourrez régler les insertions en modifiant la taille du lot.
ROWS_PER_BATCH = row_per_batch
Indique le nombre approximatif de lignes de données dans le fichier de données.
Par défaut, toutes les données du fichier de données sont envoyées au serveur en tant que transaction unique et le nombre de lignes du lot est inconnu de l'optimiseur de requêtes. Si vous spécifiez ROWS_PER_BATCH (avec une valeur> 0), le serveur utilise cette valeur pour optimiser l'opération d'importation en bloc. La valeur spécifiée pour ROWS_PER_BATCH doit être approximativement la même que le nombre réel de lignes. Pour plus d'informations sur les considérations de performances, voir "Remarques", plus loin dans cette rubrique.
Voici la citation de plus tard dans l'article:
Si le nombre de pages à vider dans un même lot dépasse un seuil interne, une analyse complète du pool de mémoire tampon peut se produire pour identifier les pages à vider lorsque le lot est validé. Cette analyse complète peut nuire aux performances d'importation en masse. Un cas probable de dépassement du seuil interne se produit lorsqu'un grand pool de mémoire tampon est combiné avec un sous-système d'E/S lent. Pour éviter les dépassements de mémoire tampon sur les grandes machines, n'utilisez pas l'indicateur TABLOCK (qui supprimera les optimisations en bloc) ou utilisez une taille de lot plus petite (qui préserve les optimisations en bloc).
Étant donné que les ordinateurs varient, nous vous recommandons de tester différentes tailles de lots avec votre chargement de données pour savoir ce qui vous convient le mieux.
Personnellement, je voudrais simplement insérer les 695 lignes en un seul lot. Cependant, le réglage de la taille du lot peut faire une grande différence lors de l'insertion de nombreuses données.
4. Assurez-vous que vous avez besoin de la colonne IDENTITY
Je ne sais rien de votre modèle de données ou de vos besoins, mais ne tombez pas dans le piège d'ajouter une colonne IDENTITY
à chaque table. Aaron Bertrand a un article à ce sujet appelé Mauvaises habitudes à mettre en place: mettre une colonne IDENTITY sur chaque table . Pour être clair, je ne dis pas que vous devez supprimer la colonne IDENTITY
de ce tableau. Cependant, si vous déterminez que la colonne IDENTITY
n'est pas nécessaire et que vous la supprimez, cela pourrait améliorer les performances d'insertion.
5. Désactiver les index ou les contraintes
Si vous chargez une grande quantité de données dans une table par rapport à ce que vous avez déjà, il peut être plus rapide de désactiver les index ou les contraintes avant le chargement et de les activer après le chargement. Pour de grandes quantités de données, il est généralement plus inefficace pour SQL Server de créer un index en une seule fois plutôt que lorsque les données sont chargées dans la table. Il semble que vous ayez inséré 695 lignes dans un tableau avec 11500 lignes, donc je ne recommanderais pas cette technique.
6. Considérez TF 610
L'indicateur de trace 610 permet une journalisation minimale dans certains scénarios supplémentaires. Pour votre table avec une clé en cluster IDENTITY
, vous obtiendrez une journalisation minimale pour toutes les nouvelles pages de données tant que votre modèle de récupération est simple ou enregistré en bloc. Je pense que cette fonctionnalité n'est pas activée par défaut car elle peut dégrader les performances sur certains systèmes. Vous devrez tester soigneusement avant d'activer cet indicateur de trace. La référence Microsoft recommandée semble toujours être The Data Loading Performance Guide
Impact d'E/S de la journalisation minimale sous l'indicateur de trace 610
Lorsque vous validez une transaction de chargement en bloc qui a été journalisée de manière minimale, toutes les pages chargées doivent être vidées sur le disque avant la fin de la validation. Toutes les pages vidées non capturées par une opération de point de contrôle antérieure peuvent créer beaucoup d'E/S aléatoires. Comparez cela à une opération entièrement journalisée, qui crée à la place des E/S séquentielles sur les écritures de journal et ne nécessite pas de vidage des pages chargées sur le disque au moment de la validation.
Si votre scénario de chargement consiste en de petites opérations d'insertion sur des btrees qui ne franchissent pas les limites des points de contrôle et que vous avez un système d'E/S lent, l'utilisation d'une journalisation minimale peut en fait ralentir les vitesses d'insertion.
Pour autant que je sache, cela n'a rien à voir avec l'indicateur de trace 610, mais plutôt avec une journalisation minimale elle-même. Je crois que la citation précédente sur ROWS_PER_BATCH
l'optimisation aboutissait à ce même concept.
En conclusion, vous ne pouvez probablement pas faire grand-chose pour régler votre BULK INSERT
. Je ne serais pas préoccupé par le nombre de lectures que vous avez observé avec votre encart. SQL Server signale les lectures chaque fois que vous insérez des données. Considérez le très simple INSERT
suivant:
DROP TABLE IF EXISTS X_TABLE;
CREATE TABLE X_TABLE (
VAL VARCHAR(1000) NOT NULL
);
SET STATISTICS IO, TIME ON;
INSERT INTO X_TABLE WITH (TABLOCK)
SELECT REPLICATE('Z', 1000)
FROM dbo.GetNums(10000); -- generate 10000 rows
Sortie de SET STATISTICS IO, TIME ON
:
Tableau 'X_TABLE'. Nombre de balayages 0, lectures logiques 11428
J'ai 11428 rapports lus mais ce ne sont pas des informations exploitables. Parfois, le nombre de lectures signalées peut être réduit par une journalisation minimale, mais bien sûr, la différence ne peut pas être directement traduite en un gain de performances.
Je vais commencer à répondre à cette question, avec l'intention de mettre à jour continuellement cette réponse pendant que je construis une base de connaissances de trucs. J'espère que d'autres personnes rencontrent cela et m'aident à améliorer mes propres connaissances au cours du processus.
Gut Check: votre pare-feu effectue-t-il une inspection approfondie et dynamique des paquets? Vous ne trouverez pas grand-chose sur Internet à ce sujet, mais si vos insertions en vrac sont environ 10 fois plus lentes que ce qu'elles devraient être, vous avez probablement un dispositif de sécurité effectuant une inspection approfondie des paquets de niveau 3-7 et vérifiant la "prévention d'injection SQL générique ".
Mesurez la taille des données que vous prévoyez d'insérer en bloc, en octets, par lot. Et vérifiez si vous stockez des données LOB, car il s'agit d'une opération de récupération et d'écriture de page distincte.
Plusieurs raisons pour lesquelles vous devriez procéder de cette façon:
une. Dans AWS, Elastic Block Storage IOPS est décomposé en octets, pas en lignes.
b. Alors que la plupart des bibliothèques ou des livres blancs testent en fonction du nombre de lignes, c'est vraiment le nombre de pages qui peuvent être écrites à ce sujet, et, pour le calculer, vous devez savoir combien d'octets par ligne et la taille de votre page (généralement 8 Ko) , mais vérifiez toujours si vous avez hérité du système de quelqu'un d'autre.)
SELECT *
FROM
sys.dm_db_index_physical_stats(DB_ID(),OBJECT_ID(N'YourTable'), NULL, NULL, 'DETAILED')
Faites attention à avg_record_size_in_bytes et page_count.
c. Comme l'explique Paul White dans https://sqlperformance.com/2019/05/sql-performance/minimal-logging-insert-select-heap , "Pour activer la journalisation minimale avec INSERT...SELECT
, SQL Server doit attendre plus de 250 lignes avec une taille totale d'au moins une extension (8 pages). "
Si vous avez des index avec des contraintes de vérification ou des contraintes uniques, utilisez SET STATISTICS IO ON
Et SET STATISTICS TIME ON
(Ou SQL Server Profiler ou SQL Server Extended Events) pour capturer des informations comme si votre insertion en bloc a des opérations de lecture . Les opérations de lecture sont dues au fait que le moteur de base de données SQL Server s'assure que les contraintes d'intégrité passent.
Essayez de créer une base de données de test où le PRIMARY FILEGROUP
est monté sur un lecteur RAM. Cela devrait être légèrement plus rapide que le SSD mais aussi éliminer les questions comme si votre contrôleur RAID peut ajouter des frais généraux. En 2018, cela ne devrait pas, mais en créant plusieurs lignes de base différentielles comme celle-ci, vous pouvez avoir une idée générale de la quantité de frais généraux que votre matériel ajoute.
Placez également le fichier source sur un lecteur RAM).
Placer le fichier source sur un lecteur RAM éliminera tout problème de contention si vous lisez le fichier source à partir du même lecteur que le FILEGROUP de votre serveur de base de données est activé.
Vérifiez que vous avez formaté votre disque dur en utilisant des extensions de 64 Ko.
Utilisez serBenchmark.com et comparez votre SSD. Cette volonté:
Si vous appelez "INSERT BULK" à partir de C # via Entity Framework Extensions, assurez-vous de "réchauffer" le JIT en premier et de "jeter" les premiers résultats.
Essayez de créer des compteurs de performances pour votre programme. Avec .NET, vous pouvez utiliser benchmark.NET et il profilera automatiquement un tas de mesures de base. Vous pouvez ensuite PARTAGER vos tentatives de profilage avec la communauté open source et voir si les personnes exécutant un matériel différent signalent les mêmes mesures (à savoir de mon point précédent sur l'utilisation de UserBenchmark.com pour comparer).
Essayez d'utiliser des canaux nommés et de l'exécuter en tant qu'hôte local.
Si vous ciblez SQL Server et utilisez .NET Core, envisagez de faire tourner un Linux avec SQL Server Std Edition - cela coûte moins d'un dollar par heure, même pour du matériel sérieux. Le principal avantage d'essayer le même code avec le même matériel avec un système d'exploitation différent est de voir si la pile TCP/IP du noyau du système d'exploitation cause des problèmes.
Utilisez les requêtes de diagnostic SQL Server de Glen Barry pour mesurer la latence du lecteur pour le lecteur stockant le FILEGROUP de votre table de base de données.
une. Assurez-vous de mesurer avant votre test et après votre test. Le "avant votre test" vous indique simplement si vous avez des caractéristiques horribles IO comme base de référence.
b. Pour mesurer "pendant votre test", vous devez vraiment utiliser les compteurs de performance PerfMon.
Pourquoi? Parce que la plupart des serveurs de bases de données utilisent une sorte de stockage en réseau (NAS). Dans le cloud, dans AWS, Elastic Block Storage est exactement cela. Vous pourriez être lié par les IOPS de votre solution de volume/NAS EBS.
Utilisez un outil pour mesurer les statistiques d'attente. Red Gate SQL Monitor , SolarWinds Database Performance Analyzer, ou même Glen Barry's SQL Server Diagnostic Queries, ou Paul Randal's Wait Statistics query .
une. Les types d'attente les plus courants seront probablement Memory/CPU, WRITELOG, PAGEIOLATCH_EX et ASYNC_NETWORK_IO .
b. Vous pouvez encourir des types d'attente supplémentaires si vous exécutez des groupes de disponibilité.
Mesurez les effets de plusieurs commandes INSERT BULK
Simultanées avec TABLOCK
désactivé (TABLOCK forcera probablement la sérialisation des commandes INSERT BULK). Votre goulot d'étranglement attend peut-être la fin d'un INSERT BULK
; vous devez essayer de mettre en file d'attente autant de tâches que le modèle de données physiques de votre serveur de base de données peut gérer.
Pensez à partitionner votre table. À titre d'exemple particulier: si votre table de base de données est uniquement en annexe, Andrew Novick a suggéré de créer un "AUJOURD'HUI" FILEGROUP
et de partitionner en au moins deux groupes de fichiers, AUJOURD'HUI et AVANT_AUJOURD'HUI. De cette façon, si vos données INSERT BULK
Ne sont que des données d'aujourd'hui, vous pouvez filtrer sur un champ CreatedOn pour forcer toutes les insertions à frapper un seul FILEGROUP
, et ainsi réduire le blocage lors de l'utilisation de TABLOCK
. Cette technique est décrite plus en détail dans un livre blanc Microsoft: Tableau de partitionnement et stratégies d'indexation utilisant SQL Server 2008
Si vous utilisez des index columnstore, désactivez TABLOCK
et chargez les données dans 102 400 lignes Taille du lot. Vous pouvez ensuite charger toutes vos données en parallèle directement dans des groupes de lignes columnstore. Cette suggestion (et rationnelle documentée) vient de Microsoft Columnstore indexes - Data loading guidance :
Le chargement en masse a ces optimisations de performances intégrées:
Charges parallèles: Vous pouvez avoir plusieurs chargements groupés simultanés (bcp ou insertion groupée) qui chargent chacun un fichier de données distinct. Contrairement aux chargements en bloc de Rowstore dans SQL Server, vous n'avez pas besoin de spécifierTABLOCK
car chaque thread d'importation en bloc chargera les données exclusivement dans des groupes de lignes séparés (groupes de lignes compressés ou delta) avec un verrouillage exclusif. L'utilisation deTABLOCK
forcera un verrouillage exclusif sur la table et vous ne pourrez pas importer de données en parallèle.
Journalisation minimale: Un chargement en bloc utilise une journalisation minimale sur les données qui vont directement aux groupes de lignes compressés. Toutes les données qui vont à un groupe de lignes delta sont entièrement enregistrées. Cela inclut toutes les tailles de lot inférieures à 102 400 lignes. Cependant, avec le chargement en masse, l'objectif est que la plupart des données contournent les groupes de lignes delta.
Optimisation du verrouillage: Lors du chargement dans un groupe de lignes compressé, le verrou X sur le groupe de lignes est acquis. Toutefois, lors du chargement en bloc dans un groupe de lignes delta, un verrou X est acquis au niveau du groupe de lignes, mais SQL Server verrouille toujours les verrous PAGE/EXTENT car le verrou de groupe de lignes X ne fait pas partie de la hiérarchie de verrouillage.
Depuis SQL Server 2016, il n'est plus nécessaire d'activer l'indicateur de trace 610 pour une connexion minimale à la table indexée . Citant l'ingénieur Microsoft Parikshit Savjani ( accentuation mine ):
L'un des objectifs de conception de SQL Server 2016 était d'améliorer les performances et l'évolutivité du moteur pour le faire fonctionner plus rapidement sans avoir besoin de boutons ou d'indicateurs de trace pour les clients. Dans le cadre de ces améliorations, l'une des améliorations apportées au code moteur SQL Server activait le contexte de chargement en masse (également appelé insertions rapides ou contexte de chargement rapide) et la journalisation minimale par défaut lorsque effectuer des opérations de chargement en masse sur la base de données avec un modèle de récupération journalisé simple ou en bloc. Si vous n'êtes pas familier avec la journalisation minimale, je vous recommande fortement de lire ce billet de blog de Sunil Agrawal où il explique comment fonctionne la journalisation minimale dans SQL Server. Pour que les insertions en vrac soient enregistrées de manière minimale, elles doivent toujours remplir les conditions préalables qui sont documentées ici.
Dans le cadre de ces améliorations de SQL Server 2016, , vous n'avez plus besoin d'activer l'indicateur de trace 610 pour une connexion minimale dans la table indexée et rejoint certains des autres drapeaux de trace (1118, 1117, 1236, 8048) pour devenir une partie de l'histoire. Dans SQL Server 2016, lorsque l'opération de chargement en bloc entraîne l'allocation d'une nouvelle page, toutes les lignes remplissant séquentiellement cette nouvelle page sont journalisées de façon minimale si toutes les autres conditions préalables à la journalisation minimale discutées précédemment sont remplies. Les lignes insérées dans les pages existantes (pas de nouvelle allocation de pages) pour maintenir l'ordre des index sont toujours entièrement enregistrées, tout comme les lignes qui sont déplacées à la suite de fractionnements de pages pendant le chargement. Il est également important d'avoir ALLOW_PAGE_LOCKS activé pour les index (qui est activé par défaut) pour que l'opération de journalisation minimale fonctionne car les verrous de page sont acquis pendant l'allocation et, par conséquent, seules les allocations de page ou d'étendue sont enregistrées.
Si vous utilisez SqlBulkCopy en C # ou EntityFramework.Extensions (qui utilise SqlBulkCopy sous le capot), vérifiez la configuration de votre build. Exécutez-vous vos tests en mode Release? L'architecture cible est-elle définie sur Any CPU/x64/x86?
Pensez à utiliser sp_who2 pour voir si la transaction INSERT BULK est SUSPENDUE. Il pourrait être SUSPENDU car il est bloqué par un autre spid. Pensez à lire Comment minimiser le blocage de SQL Server . Vous pouvez également utiliser sp_WhoIsActive d'Adam Machanic, mais sp_who2 vous fournira les informations de base dont vous avez besoin.
Vous avez peut-être simplement des E/S de disque défectueuses. Si vous effectuez une insertion en bloc et que votre utilisation du disque n'atteint pas 100% et est bloquée à environ 2%, vous avez probablement soit un mauvais micrologiciel, soit un périphérique d'E/S défectueux. (Cela est arrivé à un de mes collègues.) Utilisez [SSD UserBenchmark] pour comparer avec les autres pour les performances matérielles, surtout si vous pouvez reproduire la lenteur sur votre machine de développement locale. (J'ai mis ce dernier dans la liste car la plupart des entreprises n'autorisent pas les développeurs à exécuter des bases de données sur leur machine locale en raison du risque IP.)
Si votre table utilise la compression, vous pouvez essayer d'exécuter plusieurs sessions et dans chaque session, commencez par en utilisant une transaction existante et exécutez-la avant la commande SqlBulkCopy:
ALTER SERVER CONFIGURATION SET PROCESS AFFINITY CPU = AUTO;
Pour le chargement continu, un flux d'idées, d'abord décrit dans un livre blanc de Microsoft, Tableaux partitionnés et stratégies d'indexation utilisant SQL Server 2008 :
Chargement continu
Dans un scénario OLTP, de nouvelles données peuvent arriver en continu. Si les utilisateurs interrogent également la partition la plus récente, l'insertion continue de données peut entraîner un blocage: les requêtes des utilisateurs peuvent bloquer les insertions, et de la même manière, les insertions peuvent bloquer les requêtes des utilisateurs.
Les conflits sur la table ou la partition de chargement peuvent être réduits en utilisant l'isolement de capture instantanée, en particulier le niveau d'isolement
READ COMMITTED SNAPSHOT
. Sous l'isolementREAD COMMITTED SNAPSHOT
, Les insertions dans une table n'entraînent pas d'activité dans le magasin de versions tempdb , donc le la surcharge de tempdb est minimale pour les insertions, mais aucun verrou partagé ne sera pris par les requêtes des utilisateurs sur la même partition.Dans d'autres cas, lorsque des données sont insérées dans une table partitionnée en continu à un taux élevé, vous pouvez toujours être en mesure de mettre en scène les données pendant de courtes périodes dans les tables de transfert, puis d'insérer ces données dans la partition la plus récente jusqu'à la fenêtre de la partition actuelle passe et les données sont ensuite insérées dans la partition suivante. Par exemple, supposons que vous ayez deux tables de transfert qui reçoivent chacune 30 secondes de données, sur une base alternative: une table pour la première moitié d'une minute, la deuxième table pour la seconde moitié d'une minute. Une procédure stockée d'insertion détermine dans quelle moitié de la minute l'insertion actuelle se trouve, puis elle s'insère dans la première table de transfert. Lorsque 30 secondes sont écoulées, la procédure d'insertion détermine qu'elle doit s'insérer dans la deuxième table de transfert. Une autre procédure stockée charge ensuite les données de la première table intermédiaire dans la dernière partition de la table, puis tronque la première table intermédiaire. Après 30 secondes supplémentaires, la même procédure stockée insère les données de la deuxième procédure stockée et les place dans la partition actuelle, puis tronque la deuxième table de transfert.
Microsoft CAT Team's The Data Loading Performance Guide
Assurez-vous que vos statistiques sont à jour. Utilisez FULLSCAN si vous le pouvez après chaque génération d'index.
SAN Performance Tuning avec SQLIO et assurez-vous également si vous utilisez des disques mécaniques que vos partitions de disque sont alignées. Voir Microsoft's Disk Partition Alignment Best Practices .
Les lectures sont susceptibles d'être les contraintes uniques et FK vérifiées pendant l'insertion - vous pouvez obtenir une amélioration de la vitesse si vous pouvez les désactiver/supprimer pendant l'insertion et les activer/recréer ensuite. Vous devrez tester si cela le rend globalement plus lent que de les garder actifs. Cela peut également ne pas être une bonne idée si d'autres processus écrivent simultanément sur la même table. - Gareth Lyons
Selon le Q & A les clés étrangères deviennent non fiables après l'insertion en bloc , les contraintes FK deviennent non fiables après un BULK INSERT
sans CHECK_CONSTRAINTS
option (mon cas comme je l'ai terminé avec des contraintes non fiables). Ce n'est pas clair, mais cela n'aurait pas de sens de les vérifier et de les rendre non fiables. Cependant, PK et UNIQUE seront toujours vérifiés (voir BULK INSERT (Transact-SQL) ). - Alexei