Si los TVP son "notablemente más lentos" que las otras opciones, lo más probable es que no los estés implementando correctamente.
- No debe usar un DataTable, a menos que su aplicación tenga un uso para él fuera de enviar los valores al TVP. Usando el
IEnumerable<SqlDataRecord>
la interfaz es más rápida y usa menos memoria ya que no está duplicando la colección en la memoria solo para enviarla a la base de datos. Tengo esto documentado en los siguientes lugares:- ¿Cómo puedo insertar 10 millones de registros en el menor tiempo posible? (mucha información adicional y enlaces aquí también)
- Pasar diccionario al procedimiento almacenado T-SQL
- Transmisión de datos En SQL Server 2008 desde una aplicación (en SQLServerCentral.com; se requiere registro gratuito)
-
No debe usar
AddWithValue
para SqlParameter, aunque no es probable que se trate de un problema de rendimiento. Pero aún así, debería ser:SqlParameter tvp = com.Parameters.Add("data", SqlDbType.Structured); tvp.Value = MethodThatReturnsIEnumerable<SqlDataRecord>(MyCollection);
- Los TVP son variables de tabla y, como tales, no mantienen estadísticas. Es decir, informan que solo tienen 1 fila en el Optimizador de consultas. Entonces, en su proceso, ya sea:
- Utilice la recompilación a nivel de instrucción en cualquier consulta que utilice el TVP para cualquier cosa que no sea una simple SELECCIÓN:
OPTION (RECOMPILE)
- Cree una tabla temporal local (es decir, un solo
#
) y copie el contenido del TVP en la tabla temporal - Podría intentar agregar una clave principal agrupada al tipo de tabla definida por el usuario
- Si usa SQL Server 2014 o posterior, puede intentar usar tablas optimizadas para memoria/OLTP en memoria. Consulte:Tabla temporal y variable de tabla más rápidas mediante optimización de memoria
- Utilice la recompilación a nivel de instrucción en cualquier consulta que utilice el TVP para cualquier cosa que no sea una simple SELECCIÓN:
Con respecto a por qué está viendo:
insert into @data ( ... fields ... ) values ( ... values ... )
-- for each row
insert into @data ( ... fields ... ) values ( ... values ... )
en lugar de:
insert into @data ( ... fields ... )
values ( ... values ... ),
( ... values ... ),
SI eso es realmente lo que está sucediendo, entonces:
- Si las inserciones se realizan dentro de una transacción, entonces no hay una diferencia de rendimiento real
- La nueva sintaxis de la lista de valores (es decir,
VALUES (row1), (row2), (row3)
) está limitado a unas 1000 filas y, por lo tanto, no es una opción viable para los TVP que no tienen ese límite. SIN EMBARGO, esta no es probablemente la razón por la que se utilizan inserciones individuales, dado que no hay límite al hacerINSERT INTO @data (fields) SELECT tab.[col] FROM (VALUES (), (), ...) tab([col])
, que documenté aquí:Número máximo de filas para el constructor de valores de tabla . En cambio... - La razón más probable es que hacer inserciones individuales permite transmitir los valores del código de la aplicación a SQL Server:
- utilizando un iterador (es decir, el
IEnumerable<SqlDataRecord>
mencionado en el n.° 1 anterior), el código de la aplicación envía cada fila a medida que la devuelve el método y - construyendo los
VALUES (), (), ...
incluso si haceINSERT INTO ... SELECT FROM (VALUES ...)
enfoque (que no está limitado a 1000 filas), que todavía requeriría construir el todoVALUES
lista antes de enviar cualquiera de los datos en SQL Server. Si hay una gran cantidad de datos, se tardaría más en construir la cadena súper larga y se necesitaría mucha más memoria mientras se hace.
- utilizando un iterador (es decir, el
Consulte también este documento técnico del equipo de asesoramiento al cliente de SQL Server:Maximizar el rendimiento con TVP