¿Cómo se investiga el desempeño de una declaración BULK INSERT?

12

Soy principalmente un desarrollador .NET que usa Entity Framework ORM. Sin embargo, debido a que no quiero fallar en el uso del ORM , estoy tratando de entender qué sucede dentro de la capa de datos (base de datos). Básicamente, durante el desarrollo, inicio el generador de perfiles y verifico lo que generan algunas partes del código en términos de consultas.

Si veo algo completamente complicado (ORM puede generar consultas terribles incluso a partir de declaraciones LINQ bastante simples, si no se escriben cuidadosamente) y / o pesado (duración, CPU, lecturas de página), lo tomo en SSMS y verifico su plan de ejecución.

Funciona bien para mi nivel de conocimiento de la base de datos. Sin embargo, BULK INSERT parece ser una criatura especial, ya que no parece producir un SHOWPLAN .

Trataré de ilustrar un ejemplo muy simple:

Definición de tabla

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)
)

Nota: no hay otros índices definidos en la tabla

La inserción masiva (lo que capturo en el generador de perfiles, solo un lote)

insert bulk [dbo].[ImportingSystemFileLoadInfo] ([EnvironmentId] Int, [ImportingSystemId] Int, [FileName] NVarChar(64) COLLATE Latin1_General_CI_AS, [FileImportTime] DateTime2(7))

Métrica

  • 695 artículos insertados
  • CPU = 31
  • Lecturas = 4271
  • Escribe = 24
  • Duración = 154
  • Recuento total de tablas = 11500

Para mi aplicación, está bien, aunque las lecturas parecen bastante grandes (sé muy poco acerca de las partes internas de SQL Server, así que comparo el tamaño de página de 8K y la pequeña información de registro que tengo)

Pregunta: ¿cómo puedo investigar si este BULK INSERT se puede optimizar? ¿O no tiene ningún sentido, ya que podría decirse que es la forma más rápida de enviar grandes datos desde una aplicación cliente a SQL Server?

Alexei
fuente

Respuestas:

14

Por lo que puedo decir, puede optimizar una inserción masiva de una manera muy similar a la que optimizaría una inserción regular. Por lo general, un plan de consulta para una inserción simple no es muy informativo, así que no se preocupe por no tener el plan. Revisaré algunas formas de optimizar un inserto, pero la mayoría de ellas probablemente no soliciten el inserto que especificó en la pregunta. Sin embargo, podrían ser útiles si en el futuro necesita cargar grandes cantidades de datos.

1. Insertar datos en orden de clave de agrupación

SQL Server suele ordenar los datos antes de insertarlos en una tabla con un índice agrupado. Para algunas tablas y aplicaciones, puede mejorar el rendimiento al ordenar los datos en el archivo plano y dejar que SQL Server sepa que los datos se ordenan mediante el ORDERargumento de BULK INSERT:

ORDEN ({columna [ASC | DESC]} [, ... n])

Especifica cómo se ordenan los datos en el archivo de datos. El rendimiento de la importación masiva mejora si los datos que se importan se ordenan según el índice agrupado en la tabla, si corresponde.

Como está utilizando una IDENTITYcolumna como clave agrupada, no necesita preocuparse por esto.

2. Usar TABLOCKsi es posible

Si se garantiza que solo tiene una sesión para insertar datos en su tabla, puede especificar el TABLOCKargumento para BULK INSERT. Esto puede reducir la contención de bloqueo y puede conducir a un registro mínimo en algunos escenarios. Sin embargo, está insertando en una tabla con un índice agrupado que ya contiene datos, por lo que no obtendrá un registro mínimo sin la marca de seguimiento 610 que se menciona más adelante en esta respuesta.

Si TABLOCKno es posible, porque no puede cambiar el código , no se pierde toda esperanza. Considere usar sp_table_option:

EXEC [sys].[sp_tableoption]
    @TableNamePattern = N'dbo.BulkLoadTable' ,
    @OptionName = 'table lock on bulk load' , 
    @OptionValue = 'ON'

Otra opción es habilitar el indicador de rastreo 715 .

3. Use un tamaño de lote apropiado

A veces podrá ajustar los insertos cambiando el tamaño del lote.

ROWS_PER_BATCH = rows_per_batch

Indica el número aproximado de filas de datos en el archivo de datos.

De manera predeterminada, todos los datos en el archivo de datos se envían al servidor como una transacción única, y el optimizador de consultas desconoce el número de filas en el lote. Si especifica ROWS_PER_BATCH (con un valor> 0), el servidor usa este valor para optimizar la operación de importación masiva. El valor especificado para ROWS_PER_BATCH debe ser aproximadamente el mismo que el número real de filas. Para obtener información sobre consideraciones de rendimiento, consulte "Comentarios", más adelante en este tema.

Aquí está la cita de más adelante en el artículo:

Si el número de páginas que se vaciará en un solo lote excede un umbral interno, se puede realizar un escaneo completo de la agrupación de almacenamiento intermedio para identificar qué páginas vaciar cuando se confirma el lote. Este análisis completo puede dañar el rendimiento de importación masiva. Un caso probable de exceder el umbral interno ocurre cuando una gran agrupación de almacenamiento intermedio se combina con un subsistema de E / S lento. Para evitar desbordamientos del búfer en máquinas grandes, no use la sugerencia TABLOCK (que eliminará las optimizaciones masivas) o utilice un tamaño de lote más pequeño (que conserva las optimizaciones masivas).

Debido a que las computadoras varían, le recomendamos que pruebe varios tamaños de lote con su carga de datos para averiguar qué funciona mejor para usted.

Personalmente, simplemente insertaría las 695 filas en un solo lote. Sin embargo, ajustar el tamaño del lote puede marcar una gran diferencia al insertar muchos datos.

4. Asegúrese de que necesita la IDENTITYcolumna

No sé nada acerca de su modelo de datos o requisitos, pero no caiga en la trampa de agregar una IDENTITYcolumna a cada tabla. Aaron Bertrand tiene un artículo sobre esto llamado Malos hábitos: poner una columna IDENTIDAD en cada mesa . Para ser claros, no estoy diciendo que debas eliminar la IDENTITYcolumna de esta tabla. Sin embargo, si determina que la IDENTITYcolumna no es necesaria y la elimina, eso podría mejorar el rendimiento de la inserción.

5. Deshabilitar índices o restricciones

Si está cargando una gran cantidad de datos en una tabla en comparación con lo que ya tiene, puede ser más rápido deshabilitar índices o restricciones antes de la carga y habilitarlos después de la carga. Para grandes cantidades de datos, generalmente es más ineficiente para SQL Server construir un índice de una vez en lugar de cuando los datos se cargan en la tabla. Parece que insertó 695 filas en una tabla con 11500 filas, por lo que no recomendaría esta técnica.

6. Considere TF 610

Trace Flag 610 permite un registro mínimo en algunos escenarios adicionales. Para su tabla con una IDENTITYclave agrupada, obtendría un registro mínimo para cualquier página de datos nueva siempre que su modelo de recuperación sea simple o de registro masivo. Creo que esta función no está activada de forma predeterminada porque puede degradar el rendimiento en algunos sistemas. Debería realizar una prueba cuidadosa antes de habilitar esta marca de seguimiento. La referencia recomendada de Microsoft todavía parece ser la Guía de rendimiento de carga de datos

Impacto de E / S del registro mínimo bajo el indicador de traza 610

Cuando confirma una transacción de carga masiva que se registró mínimamente, todas las páginas cargadas se deben vaciar al disco antes de que se complete la confirmación. Cualquier página enjuagada que no haya sido captada por una operación de punto de control anterior puede crear una gran cantidad de E / S aleatorias. Compare esto con una operación completamente registrada, que crea E / S secuenciales en las escrituras de registro y no requiere que las páginas cargadas se vacíen en el disco en el momento de la confirmación.

Si su escenario de carga es pequeñas operaciones de inserción en btrees que no cruzan los límites del punto de control, y tiene un sistema de E / S lento, el uso de un registro mínimo en realidad puede ralentizar las velocidades de inserción.

Por lo que puedo decir, esto no tiene nada que ver con el indicador de traza 610, sino con un registro mínimo en sí mismo. Creo que la cita anterior sobre el ROWS_PER_BATCHajuste estaba llegando a este mismo concepto.

En conclusión, probablemente no hay mucho que puedas hacer para ajustar tu BULK INSERT. No me preocuparía el recuento de lecturas que observó con su inserto. SQL Server informará las lecturas cada vez que inserte datos. Considere lo siguiente muy simple INSERT:

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

Salida de SET STATISTICS IO, TIME ON:

Tabla 'X_TABLE'. Escaneo recuento 0, lecturas lógicas 11428

Tengo 11428 lecturas reportadas pero esa no es información procesable. A veces, el número de lecturas informadas puede reducirse mediante un registro mínimo, pero, por supuesto, la diferencia no puede traducirse directamente en una ganancia de rendimiento.

Joe Obbish
fuente
12

Voy a comenzar a responder esta pregunta, con la intención de actualizarla continuamente a medida que construya una base de conocimiento de trucos. Espero que otros encuentren esto y me ayuden a mejorar mi propio conocimiento en el proceso.

  1. Comprobación de la tripa: ¿su firewall está realizando una inspección de paquetes profunda y con estado? No encontrará mucho en Internet sobre esto, pero si sus inserciones masivas son aproximadamente 10 veces más lentas de lo que deberían ser, es probable que tenga un dispositivo de seguridad que realice una inspección profunda de paquetes de Nivel 3-7 y verifique "Prevención de inyección SQL genérica" ".

  2. Mida el tamaño de los datos que planea insertar en masa, en bytes, por lote. Y compruebe si está almacenando datos LOB, ya que es una operación de recuperación y escritura de página separada.

    Varias razones por las que debes hacerlo de esta manera:

    a. En AWS, los IOPS de Elastic Block Storage se dividen en bytes, no en filas.

    1. Consulte Rendimiento del volumen de Amazon EBS en instancias de Linux »Características y monitoreo de E / S para obtener una explicación de lo que es una unidad EBS IOPS
    2. Específicamente, los volúmenes SSD de propósito general (gp2) tienen el concepto de "Créditos de E / S y rendimiento de ráfaga" y es común que el procesamiento pesado de ETL agote los créditos de saldo de ráfaga. La duración de la ráfaga se mide en bytes, no en filas de SQL Server :)

    si. Si bien la mayoría de las bibliotecas o documentos técnicos prueban en función del número de filas, en realidad es el número de páginas que se pueden escribir en ese asunto y, para calcularlo, necesita saber cuántos bytes por fila y el tamaño de su página (generalmente 8 KB , pero siempre verifica si has heredado el sistema de otra persona).

    SELECT *
    FROM 
    sys.dm_db_index_physical_stats(DB_ID(),OBJECT_ID(N'YourTable'), NULL, NULL, 'DETAILED')
    

    Presta atención a avg_record_size_in_bytes y page_count.

    C. Como Paul White explica en https://sqlperformance.com/2019/05/sql-performance/minimal-logging-insert-select-heap , "Para habilitar un registro mínimo con INSERT...SELECT, SQL Server debe esperar más de 250 filas con un tamaño total de al menos una extensión (8 páginas) ".

  3. Si tiene algún índice con restricciones de verificación o restricciones únicas, use SET STATISTICS IO ONy SET STATISTICS TIME ON(o SQL Server Profiler o SQL Server Extended Events) para capturar información como si su inserción masiva tiene alguna operación de lectura. Las operaciones de lectura se deben a que el motor de base de datos de SQL Server se asegura de que pasan las restricciones de integridad.

  4. Intente crear una base de datos de prueba donde el PRIMARIOFILEGROUP esté montado en una unidad RAM. Esto debería ser un poco más rápido que el SSD, pero también eliminaría cualquier pregunta sobre si su Controlador RAID podría estar agregando gastos generales. En 2018, no debería, pero al crear múltiples líneas de base diferenciales como esta, puede tener una idea general de la cantidad de sobrecarga que está agregando su hardware.

  5. También coloque el archivo fuente en una unidad RAM también.

    Poner el archivo fuente en una unidad RAM descartará cualquier problema de contención si está leyendo el archivo fuente de la misma unidad en la que está el FILEGROUP de su servidor de base de datos.

  6. Verifique que haya formateado su disco duro con extensiones de 64 KB.

  7. Use UserBenchmark.com y compare su SSD. Esta voluntad:

    1. Agregue más conocimiento a otros aficionados al rendimiento sobre qué rendimiento esperar de un dispositivo
    2. Ayudarle a determinar si el rendimiento de su unidad es inferior al de sus pares con la misma unidad exacta
    3. Ayudarle a determinar si el rendimiento de su unidad tiene un rendimiento inferior al de otras unidades en la misma categoría (SSD, HDD, etc.)
  8. Si llama a "INSERT BULK" desde C # a través de las extensiones de Entity Framework, asegúrese de "calentar" primero el JIT y "tirar" los primeros resultados.

  9. Intente crear contadores de rendimiento para su programa. Con .NET, puede usar benchmark.NET y automáticamente perfilará un conjunto de métricas básicas. Luego puede COMPARTIR sus intentos de creación de perfiles con la comunidad de código abierto y ver si las personas que ejecutan hardware diferente informan las mismas métricas (a saber, desde mi punto anterior sobre el uso de UserBenchmark.com para comparar).

  10. Intente usar canalizaciones con nombre y ejecutarlo como localhost.

  11. Si está apuntando a SQL Server y está utilizando .NET Core, considere hacer girar Linux con SQL Server Std Edition; esto cuesta menos de un dólar por hora, incluso para hardware serio. La principal ventaja de probar el mismo código con el mismo hardware con un sistema operativo diferente es ver si la pila TCP / IP del núcleo del sistema operativo está causando problemas.

  12. Use las consultas de diagnóstico de SQL Server de Glen Barry para medir la latencia de la unidad para la unidad que almacena el FILEGROUP de su tabla de base de datos.

    a. Asegúrese de medir antes de su prueba y después de su prueba. El "antes de su prueba" simplemente le dice si tiene características IO horribles como referencia.

    si. Para medir "durante su prueba", realmente necesita usar PerfMon Performance Counters.

    ¿Por qué? Porque la mayoría de los servidores de bases de datos utilizan algún tipo de almacenamiento conectado a la red (NAS). En la nube, en AWS, Elastic Block Storage es solo eso. Podría estar sujeto a los IOPS de su solución EBS de volumen / NAS.

  13. Use alguna herramienta para medir las estadísticas de espera. Red Gate SQL Monitor , SolarWinds Database Performance Analyzer, o incluso las consultas de diagnóstico del servidor SQL de Glen Barry, o la consulta de estadísticas de espera de Paul Randal .

    a. Los tipos de espera más comunes probablemente serán Memoria / CPU, WRITELOG, PAGEIOLATCH_EX y ASYNC_NETWORK_IO .

    si. Puede incurrir en tipos de espera adicionales si está ejecutando Grupos de disponibilidad.

  14. Mida los efectos de múltiples INSERT BULKcomandos simultáneos con TABLOCKdeshabilitado (TABLOCK probablemente forzará la serialización de los comandos INSERT BULK). Su cuello de botella podría estar esperando a INSERT BULKque se complete; debe intentar poner en cola tantas de estas tareas como pueda manejar el modelo de datos físicos de su servidor de base de datos.

  15. Considere dividir su mesa. Como un ejemplo particular: si su tabla de la base de datos es solo para agregar, Andrew Novick sugirió crear un "TODAY" FILEGROUPy particionar en al menos dos grupos de archivos, TODAY y BEFORE_TODAY. De esta manera, si sus INSERT BULKdatos son solo datos de hoy, puede filtrar en un campo CreatedOn para forzar que todas las inserciones golpeen una sola FILEGROUPy, por lo tanto, reducir el bloqueo al usarlas TABLOCK. Esta técnica se describe con más detalle en un documento técnico de Microsoft: tabla dividida y estrategias de índice con SQL Server 2008

  16. Si está utilizando índices de almacén de columnas, apague TABLOCKy cargue datos en 102.400 filas Tamaño de lote. Luego puede cargar todos sus datos en paralelo directamente en los grupos de filas del almacén de columnas. Esta sugerencia (y racional documentada) proviene de los índices de Columnstore de Microsoft - Guía de carga de datos :

    La carga masiva tiene estas optimizaciones de rendimiento integradas:

    Cargas paralelas: puede tener varias cargas masivas concurrentes (bcp o inserción masiva) que cargan cada una un archivo de datos por separado. A diferencia de las cargas masivas del almacén de filas en SQL Server, no necesita especificar TABLOCKporque cada subproceso de importación masiva cargará datos exclusivamente en grupos de filas separadas (grupos de filas comprimidos o delta) con bloqueo exclusivo. El uso TABLOCKforzará un bloqueo exclusivo en la tabla y no podrá importar datos en paralelo.

    Registro mínimo:Una carga masiva utiliza un registro mínimo de datos que va directamente a grupos de filas comprimidos. Todos los datos que van a un grupo de filas delta se registran por completo. Esto incluye cualquier tamaño de lote que sea inferior a 102,400 filas. Sin embargo, con la carga masiva, el objetivo es que la mayoría de los datos omitan los grupos de filas delta.

    Optimización de bloqueo: cuando se carga en un grupo de filas comprimido, se adquiere el bloqueo X en el grupo de filas. Sin embargo, cuando se carga en bloque en el grupo de filas delta, se adquiere un bloqueo X en el grupo de filas, pero SQL Server todavía bloquea los bloqueos PÁGINA / EXTENSIÓN porque el bloqueo de grupos de filas X no forma parte de la jerarquía de bloqueo.

  17. A partir de SQL Server 2016, ya no es necesario habilitar el indicador de traza 610 para un registro mínimo en la tabla indexada . Citando al ingeniero de Microsoft Parikshit Savjani (el énfasis es mío ):

    Uno de los objetivos de diseño de SQL Server 2016 era mejorar el rendimiento y la escalabilidad del motor fuera de la caja para que funcione más rápido sin la necesidad de perillas o marcas de seguimiento para los clientes. Como parte de estas mejoras, una de las mejoras realizadas en el código del motor de SQL Server fue activar el contexto de carga masiva (también conocido como inserciones rápidas o contexto de carga rápida) y un registro mínimo de forma predeterminada al realizar operaciones de carga masiva en la base de datos con simple o modelo de recuperación registrada a granel. Si no está familiarizado con el registro mínimo, le recomiendo leer esta publicación de blog de Sunil Agrawal donde explica cómo funciona el registro mínimo en SQL Server. Para que las inserciones masivas se registren mínimamente, aún debe cumplir con las condiciones previas que se documentan aquí.

    Como parte de estas mejoras en SQL Server 2016, ya no necesita habilitar la marca de seguimiento 610 para un inicio de sesión mínimo en la tabla indexaday se une a algunas de las otras banderas de rastreo (1118, 1117, 1236, 8048) para formar parte de la historia. En SQL Server 2016, cuando la operación de carga masiva hace que se asigne una nueva página, todas las filas que llenan secuencialmente esa nueva página se registran mínimamente si se cumplen todos los otros requisitos previos para un registro mínimo discutido anteriormente. Las filas insertadas en páginas existentes (sin asignación de página nueva) para mantener el orden del índice todavía se registran completamente, al igual que las filas que se mueven como resultado de divisiones de página durante la carga. También es importante tener ALLOW_PAGE_LOCKS activado para los índices (que está activado de forma predeterminada) para que la operación de registro mínima funcione a medida que se adquieren bloqueos de página durante la asignación y, por lo tanto, solo se registran las asignaciones de página o extensión.

  18. Si está utilizando SqlBulkCopy en C # o EntityFramework.Extensions (que utiliza SqlBulkCopy debajo del capó), compruebe su configuración de compilación. ¿Estás ejecutando tus pruebas en modo Release? ¿Está la arquitectura de destino establecida en cualquier CPU / x64 / x86?

  19. Considere usar sp_who2 para ver si la transacción INSERT BULK está SUSPENDIDA. Podría suspenderse porque está bloqueado por otro spid. Considere leer Cómo minimizar el bloqueo de SQL Server . También puede usar sp_WhoIsActive de Adam Machanic, pero sp_who2 le dará la información básica que necesita.

  20. Es posible que solo tenga E / S de disco defectuoso. Si está haciendo una inserción masiva y la utilización de su disco no alcanza el 100%, y está atascado en alrededor del 2%, entonces probablemente tenga un firmware defectuoso o un dispositivo de E / S defectuoso. (Esto le sucedió a un compañero de trabajo mío). Utilice [SSD UserBenchmark] para comparar con otros para el rendimiento del hardware, especialmente si puede replicar la lentitud en su máquina de desarrollo local. (Puse esto último en la lista porque la mayoría de las empresas no permiten a los desarrolladores ejecutar bases de datos en su máquina local debido al riesgo de IP).

  21. Si su tabla usa compresión, puede intentar ejecutar varias sesiones y, en cada sesión, comenzar con el uso de una transacción existente y ejecutar esto antes del comando SqlBulkCopy:

    ALTERAR LA CONFIGURACIÓN DEL SERVIDOR CONFIGURAR PROCESO AFFINITY CPU = AUTO;

  22. Para la carga continua, una secuencia de ideas, descrita por primera vez en un documento técnico de Microsoft, Tabla dividida y estrategias de índice con SQL Server 2008 :

    Carga continua

    En un escenario OLTP, es posible que ingresen nuevos datos continuamente. Si los usuarios también consultan la partición más nueva, la inserción continua de datos puede provocar el bloqueo: las consultas de los usuarios pueden bloquear las inserciones y, de manera similar, las inserciones pueden bloquear las consultas de los usuarios.

    La contención en la tabla o partición de carga se puede reducir mediante el uso del aislamiento de instantáneas, en particular, el READ COMMITTED SNAPSHOTnivel de aislamiento. Bajo READ COMMITTED SNAPSHOTaislamiento, las inserciones en una tabla no causan actividad en el almacén de versiones tempdb , por lo que la sobrecarga de tempdb es mínima para las inserciones, pero las consultas de los usuarios no tomarán bloqueos compartidos en la misma partición.

    En otros casos, cuando los datos se insertan en una tabla particionada de forma continua a una velocidad alta, es posible que pueda organizar los datos durante cortos períodos de tiempo en tablas de ensayo y luego insertar esos datos en la partición más nueva repetidamente hasta que aparezca la ventana para la partición actual pasa y los datos se insertan en la siguiente partición. Por ejemplo, suponga que tiene dos tablas de preparación que reciben 30 segundos de datos cada una, de forma alternativa: una tabla para la primera mitad de un minuto, la segunda tabla para la segunda mitad de un minuto. Un procedimiento almacenado de inserción determina en qué mitad del minuto está la inserción actual y luego se inserta en la primera tabla de etapas. Cuando transcurren 30 segundos, el procedimiento de inserción determina que debe insertarse en la segunda tabla de etapas. Otro procedimiento almacenado carga los datos de la primera tabla de etapas en la partición más nueva de la tabla y luego trunca la primera tabla de etapas. Después de otros 30 segundos, el mismo procedimiento almacenado inserta los datos del segundo procedimiento almacenado y los coloca en la partición actual, y luego trunca la segunda tabla de etapas.

  23. Guía de rendimiento de carga de datos de Microsoft CAT Team

  24. Asegúrese de que sus estadísticas estén actualizadas. Use FULLSCAN si puede después de cada compilación de índice.

  25. Ajuste de rendimiento de SAN con SQLIO y también asegúrese de que si está utilizando discos mecánicos, sus particiones de disco estén alineadas. Consulte las mejores prácticas de alineación de particiones de disco de Microsoft .

  26. COLUMNSTORE INSERT/ UPDATErendimiento

John Zabroski
fuente
2

Es probable que las lecturas sean las restricciones únicas y FK que se verifican durante la inserción: puede obtener una mejora de velocidad si puede deshabilitarlas / soltarlas durante la inserción y habilitarlas / recrearlas después. Tendrá que probar si esto lo hace más lento en general en comparación con mantenerlos activos. Esto también puede no ser una buena idea si otros procesos están escribiendo en la misma tabla al mismo tiempo. - Gareth Lyons

De acuerdo con las preguntas y respuestas Las claves externas se vuelven no confiables después de la inserción masiva , las restricciones FK se vuelven no confiables después de una BULK INSERTsin CHECK_CONSTRAINTSopción (mi caso, ya que terminé con restricciones no confiables). No está claro, pero no tendría sentido revisarlos y aun así no confiar en ellos. Sin embargo, PK y UNIQUE se seguirán verificando (vea BULK INSERT (Transact-SQL) ). - Alexei

usuario126897
fuente