Agregado 7/11 El problema es que los puntos muertos ocurren debido a la exploración del índice durante MERGE JOIN. En este caso, una transacción que intenta obtener el bloqueo S en todo el índice en la tabla primaria FK, pero anteriormente otra transacción coloca el bloqueo X en un valor clave del índice.
Permítanme comenzar con un pequeño ejemplo (se usa TSQL2012 DB de 70-461 cource):
CREATE TABLE [Sales].[Orders](
[orderid] [int] IDENTITY(1,1) NOT NULL,
[custid] [int] NULL,
[empid] [int] NOT NULL,
[shipperid] [int] NOT NULL,
... )
Las columnas [custid], [empid], [shipperid]
son parámetros correlacionados para ello [Sales].[Customers], [HR].[Employees], [Sales].[Shippers]
. En cada caso tenemos un índice agrupado en una columna referida en una tabla parental.
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([custid]) REFERENCES [Sales].[Customers] ([custid])
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Employees] FOREIGN KEY([empid]) REFERENCES [HR].[Employees] ([empid])
ALTER TABLE [Sales].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Shippers] FOREIGN KEY([shipperid])REFERENCES [Sales].[Shippers] ([shipperid])
Estoy intentando con INSERT [Sales].[Orders] SELECT ... FROM
otra tabla llamada [Sales].[OrdersCache]
que tiene la misma estructura que las [Sales].[Orders]
claves externas excepto. Otra cosa que podría ser importante mencionar es que la tabla [Sales].[OrdersCache]
es un índice agrupado.
CREATE CLUSTERED INDEX idx_c_OrdersCache ON Sales.OrdersCache ( custid, empid )
Como era de esperar cuando estoy tratando de insertar un volumen bajo de datos, LOOP JOIN funciona bien haciendo que la búsqueda de índice en las claves foráneas.
Con grandes volúmenes de datos, el optimizador de consultas utiliza MERGE JOIN como la forma más eficiente de mantener la clave foregn en la consulta.
Y no hay nada que ver con él, excepto usar OPTION (LOOP JOIN) en nuestro caso con claves foráneas o INNER LOOP JOIN en el caso explícito de JOIN.
A continuación se muestra la consulta que estoy tratando de ejecutar en mi entorno:
INSERT Sales.Orders (
custid, empid, shipperid, ... )
SELECT custid, empid, 2, ...
FROM Sales.OrdersCache
Mirando el plan podemos ver que las 3 claves extranjeras validadas con MERGE JOIN. No es una forma apropiada para mí, ya que utiliza INDEX SCAN con bloqueo de índice completo.
Usar OPTION (LOOP JOIN) no es adecuado ya que cuesta casi un 15% más que MERGE JOIN (creo que la regresión será mayor con el crecimiento de los volúmenes de datos).
En la instrucción SELECT puede ver un valor único para el shipperid
atributo para todo el conjunto insertado. En mi opinión, debe haber una manera de hacer que la fase de validación para el conjunto insertado sea más rápida al menos para el atributo inmutable. Algo como:
- hacer LOOP JOIN, MERGE JOIN, HASH JOIN si tenemos un subconjunto indefinido para la validación de JOIN
- si solo hay un valor explícito de la columna validada, hacemos la validación solo una vez (INDEX SEEK).
¿Hay algún patrón común para superar la situación anterior utilizando estructuras de código, objetos DDL adicionales, etc.?
Añadido 20/07. Solución. Query Optimizer ya realiza una optimización de validación de 'clave única: clave externa' mediante MERGE JOIN. Y se dirige solo a la tabla Sales.Shippers, dejando LOOP JOIN para otras uniones en la consulta al mismo tiempo. Como tengo algunas filas en la tabla principal, el Optimizador de consultas utiliza el algoritmo de combinación Ordenar-fusionar y comparar cada fila de la tabla interna con la tabla principal solo una vez. Entonces esa es la respuesta a mi pregunta si hay algún mecanismo particular para procesar efectivamente valores individuales en un conjunto durante la validación de una sola clave. Esa no es una decisión tan perfecta, pero esa es la forma en que SQL Server optimiza el caso.
La investigación sobre el rendimiento del rendimiento reveló que en mi caso la instrucción de inserción MERGE JOIN y LOOP JOIN se volvió aproximadamente igual a 750 filas insertadas simultáneamente con la siguiente superioridad de MERGE JOIN (en el recurso de tiempo de CPU). Por lo tanto, usar OPTION (LOOP JOIN) es una solución adecuada para mi proceso comercial.