sql >> Base de Datos >  >> RDS >> Sqlserver

Múltiples instrucciones INSERT frente a INSERT único con múltiples VALORES

Adición: SQL Server 2012 muestra un rendimiento mejorado en esta área, pero no parece abordar los problemas específicos que se indican a continuación. Aparentemente, esto debería solucionarse en la próxima versión principal después ¡Servidor SQL 2012!

Su plan muestra que las inserciones individuales utilizan procedimientos parametrizados (posiblemente parametrizados automáticamente), por lo que el tiempo de análisis/compilación para estos debe ser mínimo.

Sin embargo, pensé en investigar esto un poco más, así que configuré un bucle (secuencia de comandos) e intenté ajustar la cantidad de VALUES cláusulas y registrando el tiempo de compilación.

Luego dividí el tiempo de compilación por el número de filas para obtener el tiempo de compilación promedio por cláusula. Los resultados están abajo

Hasta 250 VALUES cláusulas presentes el tiempo de compilación/número de cláusulas tiene una ligera tendencia al alza pero nada demasiado dramático.

Pero luego hay un cambio repentino.

Esa sección de los datos se muestra a continuación.

+------+----------------+-------------+---------------+---------------+
| Rows | CachedPlanSize | CompileTime | CompileMemory | Duration/Rows |
+------+----------------+-------------+---------------+---------------+
|  245 |            528 |          41 |          2400 | 0.167346939   |
|  246 |            528 |          40 |          2416 | 0.162601626   |
|  247 |            528 |          38 |          2416 | 0.153846154   |
|  248 |            528 |          39 |          2432 | 0.157258065   |
|  249 |            528 |          39 |          2432 | 0.156626506   |
|  250 |            528 |          40 |          2448 | 0.16          |
|  251 |            400 |         273 |          3488 | 1.087649402   |
|  252 |            400 |         274 |          3496 | 1.087301587   |
|  253 |            400 |         282 |          3520 | 1.114624506   |
|  254 |            408 |         279 |          3544 | 1.098425197   |
|  255 |            408 |         290 |          3552 | 1.137254902   |
+------+----------------+-------------+---------------+---------------+

El tamaño del plan en caché que había estado creciendo linealmente de repente cae, pero CompileTime aumenta 7 veces y CompileMemory se dispara. Este es el punto de corte entre que el plan sea uno autoparametrizado (con 1.000 parámetros) a uno no parametrizado. A partir de entonces, parece volverse linealmente menos eficiente (en términos de número de cláusulas de valor procesadas en un momento dado).

No estoy seguro de por qué esto debería ser. Presumiblemente, cuando compila un plan para valores literales específicos, debe realizar alguna actividad que no se escala linealmente (como la clasificación).

No parece afectar el tamaño del plan de consulta en caché cuando probé una consulta que constaba completamente de filas duplicadas y tampoco afecta el orden de la salida de la tabla de constantes (y como está insertando en un montón de tiempo dedicado a ordenar sería inútil de todos modos incluso si lo hiciera).

Además, si se agrega un índice agrupado a la tabla, el plan aún muestra un paso de ordenación explícito, por lo que no parece estar ordenando en tiempo de compilación para evitar una ordenación en tiempo de ejecución.

Traté de ver esto en un depurador, pero los símbolos públicos para mi versión de SQL Server 2008 no parecen estar disponibles, así que tuve que buscar el equivalente UNION ALL construcción en SQL Server 2005.

A continuación se muestra un seguimiento de pila típico

sqlservr.exe!FastDBCSToUnicode()  + 0xac bytes  
sqlservr.exe!nls_sqlhilo()  + 0x35 bytes    
sqlservr.exe!CXVariant::CmpCompareStr()  + 0x2b bytes   
sqlservr.exe!CXVariantPerformCompare<167,167>::Compare()  + 0x18 bytes  
sqlservr.exe!CXVariant::CmpCompare()  + 0x11f67d bytes  
sqlservr.exe!CConstraintItvl::PcnstrItvlUnion()  + 0xe2 bytes   
sqlservr.exe!CConstraintProp::PcnstrUnion()  + 0x35e bytes  
sqlservr.exe!CLogOp_BaseSetOp::PcnstrDerive()  + 0x11a bytes    
sqlservr.exe!CLogOpArg::PcnstrDeriveHandler()  + 0x18f bytes    
sqlservr.exe!CLogOpArg::DeriveGroupProperties()  + 0xa9 bytes   
sqlservr.exe!COpArg::DeriveNormalizedGroupProperties()  + 0x40 bytes    
sqlservr.exe!COptExpr::DeriveGroupProperties()  + 0x18a bytes   
sqlservr.exe!COptExpr::DeriveGroupProperties()  + 0x146 bytes   
sqlservr.exe!COptExpr::DeriveGroupProperties()  + 0x146 bytes   
sqlservr.exe!COptExpr::DeriveGroupProperties()  + 0x146 bytes   
sqlservr.exe!CQuery::PqoBuild()  + 0x3cb bytes  
sqlservr.exe!CStmtQuery::InitQuery()  + 0x167 bytes 
sqlservr.exe!CStmtDML::InitNormal()  + 0xf0 bytes   
sqlservr.exe!CStmtDML::Init()  + 0x1b bytes 
sqlservr.exe!CCompPlan::FCompileStep()  + 0x176 bytes   
sqlservr.exe!CSQLSource::FCompile()  + 0x741 bytes  
sqlservr.exe!CSQLSource::FCompWrapper()  + 0x922be bytes    
sqlservr.exe!CSQLSource::Transform()  + 0x120431 bytes  
sqlservr.exe!CSQLSource::Compile()  + 0x2ff bytes   

Por lo tanto, al salir de los nombres en el seguimiento de la pila, parece pasar mucho tiempo comparando cadenas.

Este artículo de KB indica que DeriveNormalizedGroupProperties está asociado con lo que solía llamarse la etapa de normalización del procesamiento de consultas

Esta etapa ahora se denomina vinculación o algebrización y toma la salida del árbol de análisis de expresión de la etapa de análisis anterior y genera un árbol de expresión algebrizado (árbol del procesador de consultas) para pasar a la optimización (optimización del plan trivial en este caso) [ref].

Intenté un experimento más (Script) que consistía en volver a ejecutar la prueba original pero analizando tres casos diferentes.

  1. Cadenas de nombre y apellido de 10 caracteres de longitud sin duplicados.
  2. Cadenas de nombre y apellido de 50 caracteres de longitud sin duplicados.
  3. Cadenas de nombre y apellido de 10 caracteres de longitud con todos los duplicados.

Se puede ver claramente que cuanto más largas son las cadenas, peor se ponen las cosas y, a la inversa, cuanto más se duplica, mejor se ponen las cosas. Como se mencionó anteriormente, los duplicados no afectan el tamaño del plan en caché, por lo que supongo que debe haber un proceso de identificación de duplicados al construir el propio árbol de expresión algebrizado.

Editar

@Lieven muestra un lugar donde se aprovecha esta información

SELECT * 
FROM (VALUES ('Lieven1', 1),
             ('Lieven2', 2),
             ('Lieven3', 3))Test (name, ID)
ORDER BY name, 1/ (ID - ID) 

Porque en tiempo de compilación puede determinar que el Name la columna no tiene duplicados, omite ordenar por el 1/ (ID - ID) secundario expresión en tiempo de ejecución (la clasificación en el plan solo tiene un ORDER BY columna) y no se genera ningún error de división por cero. Si se agregan duplicados a la tabla, el operador de clasificación muestra dos órdenes por columnas y se genera el error esperado.