Descubro que, en una vista indizada con los índices adecuados, MAX (fecha) realiza un escaneo de índice completo seguido de un agregado de flujo, mientras que la fecha TOP (1) usa el índice de manera óptima y solo escanea una fila. Para grandes cantidades de filas, esto genera problemas graves de rendimiento. He incluido un código para demostrar el problema a continuación, pero estaría interesado en saber si otros pueden explicar por qué ocurre este comportamiento (no ocurre en una tabla con índice similar) y si se trata de un error en el optimizador de SQL Server (I He probado en 2008 SP2 y en R2, y ambos muestran los mismos problemas).¿Por qué MAX funciona mucho peor que TOP en una vista indexada?
CREATE TABLE dbo.TableWithDate
(
id INT IDENTITY(1,1) PRIMARY KEY,
theDate DATE NOT NULL
);
CREATE NONCLUSTERED INDEX [ix_date] ON dbo.TableWithDate([theDate] DESC);
INSERT INTO dbo.TableWithDate(theDate) VALUES('1 MAR 2010'),('1 MAR 2010'), ('3 JUN 2008');
-- Test 1: max vs top(1) on the table. They give same optimal plan (scan one row from the index, since index is in order)
SELECT TOP(1) theDate FROM dbo.TableWithDate ORDER BY theDate DESC;
SELECT MAX(theDate) FROM dbo.TableWithDate;
CREATE TABLE dbo.TheJoinTable
(
identId INT IDENTITY(1,1) PRIMARY KEY,
foreignId INT NOT NULL,
someValue INT NOT NULL
);
CREATE NONCLUSTERED INDEX [ix_foreignValue] ON dbo.TheJoinTable([foreignId] ASC);
INSERT INTO dbo.TheJoinTable(foreignId,someValue) VALUES (1,10),(1,20),(1,30),(2,5),(3,6),(3,10);
GO
CREATE VIEW dbo.TheTablesJoined
WITH SCHEMABINDING
AS
SELECT T2.identId, T1.id, T1.theDate, T2.someValue
FROM dbo.TableWithDate AS T1
INNER JOIN dbo.TheJoinTable AS T2 ON T2.foreignId=T1.id
GO
-- Notice the different plans: the TOP one does a scan of 1 row from each and joins
-- The max one does a scan of the entire index and then does seek operations for each item (less efficient)
SELECT TOP(1) theDate FROM dbo.TheTablesJoined ORDER BY theDate DESC;
SELECT MAX(theDate) FROM dbo.TheTablesJoined;
-- But what about if we put an index on the view? Does that make a difference?
CREATE UNIQUE CLUSTERED INDEX [ix_clust1] ON dbo.TheTablesJoined([identId] ASC);
CREATE NONCLUSTERED INDEX [ix_dateDesc] ON dbo.TheTablesJoined ([theDate] DESC);
-- No!!!! We are still scanning the entire index (look at the actual number of rows) in the MAX case.
SELECT TOP(1) theDate FROM dbo.TheTablesJoined ORDER BY theDate DESC;
SELECT MAX(theDate) FROM dbo.TheTablesJoined;
Esto suena más como una característica que no se ha implementado aún en el optimizador en lugar de un error. ¿Qué sucede si usa la sugerencia de la tabla 'NOEXPAND'? – Gabe
Solo por curiosidad, ¿qué sucede si declaras una clave foránea entre las tablas? – Ronnis
Hace la diferencia si agrego una restricción de clave externa (debería haber mencionado que lo había intentado). Sin embargo, al agregar WITH (NOEXPAND) a la consulta de la vista indizada, se fuerza a elegir un plan diferente (óptimo). Gracias por recomendar que intente eso. –