Tengo una estructura de base de datos similar a esta,
CREATE TABLE [dbo].[Dispatch](
[DispatchId] [int] NOT NULL,
[ContractId] [int] NOT NULL,
[DispatchDescription] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Dispatch] PRIMARY KEY CLUSTERED
(
[DispatchId] ASC,
[ContractId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[DispatchLink](
[ContractLink1] [int] NOT NULL,
[DispatchLink1] [int] NOT NULL,
[ContractLink2] [int] NOT NULL,
[DispatchLink2] [int] NOT NULL
) ON [PRIMARY]
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (1, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (2, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (3, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (4, 1, N'Test')
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 2)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 3)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 3, 1, 2)
GO
El objetivo de la tabla DispatchLink es vincular dos registros de envío juntos. Por cierto, estoy usando una clave primaria compuesta en mi tabla de despacho debido al legado, por lo que no puedo cambiar eso sin mucho dolor. Además, la tabla de enlaces puede no ser la forma correcta de hacerlo Pero de nuevo legado.
Entonces mi pregunta, si ejecuto esta consulta
select * from Dispatch d
inner join DispatchLink dl on d.DispatchId = dl.DispatchLink1 and d.ContractId = dl.ContractLink1
or d.DispatchId = dl.DispatchLink2 and d.ContractId = dl.ContractLink2
Nunca puedo conseguir que haga una búsqueda de índice en la tabla DispatchLink. Siempre hace un escaneo de índice completo. Eso está bien con algunos registros, pero cuando tiene 50000 en esa tabla, escanea 50000 registros en el índice de acuerdo con el plan de consulta. Es porque hay 'ands' y 'ors' en la cláusula de unión, pero no puedo entender por qué SQL no puede hacer un par de búsquedas de índice, una para el lado izquierdo de 'o', y uno para el lado derecho de 'o'.
Me gustaría una explicación para esto, no una sugerencia para hacer que la consulta sea más rápida a menos que se pueda hacer sin ajustar la consulta. La razón es que estoy usando la consulta anterior como un filtro de combinación de replicación de combinación, por lo que desafortunadamente no puedo agregar otro tipo de consulta.
ACTUALIZACIÓN: Por ejemplo, estos son los tipos de índices que he estado agregando,
CREATE NONCLUSTERED INDEX IDX1 ON DispatchLink (ContractLink1, DispatchLink1)
CREATE NONCLUSTERED INDEX IDX2 ON DispatchLink (ContractLink2, DispatchLink2)
CREATE NONCLUSTERED INDEX IDX3 ON DispatchLink (ContractLink1, DispatchLink1, ContractLink2, DispatchLink2)
Por lo tanto, utiliza los índices, pero realiza un escaneo del índice en todo el índice, por lo que 50000 registros escanea 50000 registros en el índice.
DispatchLink
mesa?Respuestas:
El optimizador puede considerar muchas alternativas de plan (incluidas las que tienen múltiples búsquedas), pero para disyunciones (
OR
predicados) no considera planes que impliquen intersecciones de índice de forma predeterminada. Dados los índices:Podemos forzar búsquedas de índice (suponiendo SQL Server 2008 o posterior):
Usando sus datos de muestra, el plan de búsqueda cuesta a 0.0332551 unidades en comparación con 0.0068057 para el plan de escaneo:
Hay todo tipo de posibles reescrituras de consultas y sugerencias que podemos probar. Un ejemplo de una reescritura para promover una opción que el optimizador no considera para el plan original es:
Este plan de ejecución no busca el segundo índice si encuentra una coincidencia en el primero:
Esto puede funcionar muy ligeramente mejor que el
FORCESEEK
plan predeterminado .Sin agregar ningún índice nuevo, también podemos forzar una búsqueda en la tabla de Despacho:
Esto puede ser mejor o peor que el primer ejemplo dependiendo de cosas como cuántas filas hay en cada una de las tablas. La
APPLY + TOP
mejora aún es posible:fuente