我遇到了SQL Server的复杂问题。
我管理40个具有相同结构但数据不同的数据库。这些数据库大小从2 MB到10 GB不等。这些数据库的主要表是:
CREATE TABLE [dbo].[Eventos](
[ID_Evento] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[FechaGPS] [datetime] NOT NULL,
[FechaRecepcion] [datetime] NOT NULL,
[CodigoUnico] [varchar](30) COLLATE Modern_Spanish_CI_AS NULL,
[ID_Movil] [int] NULL,
[CodigoEvento] [char](5) COLLATE Modern_Spanish_CI_AS NULL,
[EventoData] [varchar](150) COLLATE Modern_Spanish_CI_AS NULL,
[EventoAlarma] [bit] NOT NULL CONSTRAINT [DF_Table_1_Alarma] DEFAULT ((0)),
[Ack] [bit] NOT NULL CONSTRAINT [DF_Eventos_Ack] DEFAULT ((0)),
[Procesado] [bit] NOT NULL CONSTRAINT [DF_Eventos_Procesado] DEFAULT ((0)),
[Latitud] [float] NULL,
[Longitud] [float] NULL,
[Velocidad] [float] NULL,
[Rumbo] [smallint] NULL,
[Satelites] [tinyint] NULL,
[EventoCerca] [bit] NOT NULL CONSTRAINT [DF_Eventos_FueraCerca] DEFAULT ((0)),
[ID_CercaElectronica] [int] NULL,
[Direccion] [varchar](250) COLLATE Modern_Spanish_CI_AS NULL,
[Localidad] [varchar](150) COLLATE Modern_Spanish_CI_AS NULL,
[Provincia] [varchar](100) COLLATE Modern_Spanish_CI_AS NULL,
[Pais] [varchar](50) COLLATE Modern_Spanish_CI_AS NULL,
[EstadoEntradas] [char](16) COLLATE Modern_Spanish_CI_AS NULL,
[DentroFuera] [char](1) COLLATE Modern_Spanish_CI_AS NULL,
[Enviado] [bit] NOT NULL CONSTRAINT [DF_Eventos_Enviado] DEFAULT ((0)),
[SeñalGSM] [int] NOT NULL DEFAULT ((0)),
[GeoCode] [bit] NOT NULL CONSTRAINT [DF_Eventos_GeoCode] DEFAULT ((0)),
[Contacto] [bit] NOT NULL CONSTRAINT [DF_Eventos_Contacto] DEFAULT ((0)),
CONSTRAINT [PK_Eventos] PRIMARY KEY CLUSTERED
(
[ID_Evento] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
USE [ABS]
GO
ALTER TABLE [dbo].[Eventos] WITH CHECK ADD CONSTRAINT [FK_Eventos_Eventos] FOREIGN KEY([ID_Evento])
REFERENCES [dbo].[Eventos] ([ID_Evento])
我还有一个每n秒运行一次的循环来处理这些记录(只有新记录并将它们标记为已处理)。此过程使用此查询:
SELECT
Tbl.ID_Cliente, Ev.ID_Evento, Tbl.ID_Movil, Ev.EventoData, Tbl.Evento,
Tbl.ID_CercaElectronica, Ev.Latitud, Ev.Longitud, Tbl.EsAlarma, Ev.FechaGPS,
Tbl.AlarmaVelocidad, Ev.Velocidad, Ev.CodigoEvento
FROM
dbo.Eventos AS Ev
INNER JOIN
(SELECT
Det.CodigoEvento, Mov.CodigoUnico, Mov.ID_Cliente, Mov.ID_Movil, Det.Evento,
Mov.ID_CercaElectronica, Det.EsAlarma, Mov.AlarmaVelocidad
FROM
dbo.Moviles Mov
INNER JOIN
dbo.GruposEventos AS GE
INNER JOIN
dbo.GruposEventosDet AS Det ON Det.ID_GrupoEventos = GE.ID_GrupoEventos
ON GE.ID_GrupoEventos = Mov.ID_GrupoEventos) as Tbl ON EV.CodigoUnico = Tbl.CodigoUnico AND Ev.CodigoEvento = Tbl.CodigoEvento
WHERE
(Ev.Procesado = 0)
该表可以在某些数据库上拥有超过1.000.000条记录。因此,为了优化流程,我使用SQL助手为此查询创建了特定的索引以进行优化:
CREATE NONCLUSTERED INDEX [OptimizadorProcesarEventos] ON [dbo].[Eventos]
(
[Procesado] ASC,
[CodigoEvento] ASC,
[CodigoUnico] ASC,
[FechaGPS] ASC
)
INCLUDE ( [ID_Evento],
[EventoData],
[Latitud],
[Longitud],
[Velocidad]) WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]
这曾经很完美。但现在偶尔也只在某些数据库中,查询需要永远,并给我超时。所以我运行一个“show execution plan”并意识到在某些情况下,取决于表中的数据,SQL Server决定不使用我的索引并改为使用PK索引。我验证这在其他数据库上运行相同的执行计划,工作正常并且正在使用索引。
所以我的问题:为什么SQL Server 在某些情况下决定不使用我的索引?
感谢您的关注!
更新 我已经尝试更新STATICS并没有帮助。我现在暂时避免使用HINT,所以问题仍然存在:为什么SQL Server会选择一种更低效的方式来执行我的查询,如果它有索引呢?
更新II 经过多次测试后,我可以最终解决问题,尽管我并不完全理解为什么会这样。我将索引更改为:
CREATE NONCLUSTERED INDEX [OptimizadorProcesarEventos] ON [dbo].[Eventos]
(
[CodigoUnico] ASC,
[CodigoEvento] ASC,
[Procesado] ASC,
[FechaGPS] ASC
)
INCLUDE ( [ID_Evento],
[EventoData],
[Latitud],
[Longitud],
[Velocidad]) WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]
Basicaly我改变了索引中字段的顺序,并且查询开始使用索引作为预期。我仍然觉得SQL Server如何选择使用或不使用特定查询的索引。谢谢大家。
答案 0 :(得分:1)
你必须找到很多关于Query优化器如何选择正确索引的文章。如果不在谷歌搜索的东西。 我可以指出一个开始。
Index Selection and the Query Optimizer
简单的答案如下:
"基于索引使用历史,统计信息,插入/更新/删除的行数等等。查询优化器发现使用PK索引比使用其他非群集索引的成本更低。 "
现在,您会对Query Optimizer如何找到它有很多疑问?这将需要一些家庭作业。
虽然在你的具体情况下,我不同意" Femi"如上所述,试图运行"更新统计数据"因为还有其他一些情况,Update Statistics也无济于事。 听起来您已经在此查询上测试了此索引,如果您确定只希望该查询在100%的时间内使用该索引,请使用查询提示并指定需要使用此索引。通过这种方式,您可以始终确保使用此索引。
注意:您必须对各种数据加载进行足够的测试,以确保在任何情况下都不会使用此索引或不接受。一旦您使用Query提示,每次执行都将仅使用该提示,并且Optimizer将始终使用该索引提出执行计划。
答案 1 :(得分:0)
在这种特定情况下很难说清楚,但查询计划程序通常会查看它对特定表的统计信息,并决定使用错误的索引(对于某些错误的定义;可能只是你认为它应该使用的索引)。尝试在表格上运行UPDATE STATISTICS,看看查询计划程序是否达到了一组不同的决策。
答案 2 :(得分:0)
确定优化器选择或不选择给定索引的原因可能有点暗淡。但是,我注意到,您可能正在使用更好的索引。具体做法是:
CREATE NONCLUSTERED INDEX [OptimizadorProcesarEventos] ON [dbo].[Eventos]
(
[Procesado] ASC,
[CodigoEvento] ASC,
[CodigoUnico] ASC,
[FechaGPS] ASC
)
INCLUDE ( [ID_Evento],
[EventoData],
[Latitud],
[Longitud],
[Velocidad])
WHERE Procesado = 0 -- this makes it a filtered index
WITH (SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF,
IGNORE_DUP_KEY = OFF,
ONLINE = OFF)
ON [PRIMARY]
我假设在任何给定时间,表中的大多数行都被处理(即Procesado = 1),因此上述索引将远小于未过滤的版本。