2010-12-20 4 views
6

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; 
+2

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

+0

Solo por curiosidad, ¿qué sucede si declaras una clave foránea entre las tablas? – Ronnis

+0

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. –

Respuesta

1

para evaluar el valor de cualquier función de agregado, como máximo, todas las filas de una tabla debe ser leído y porque uno de sus valores se utiliza en la evaluación. Top 1 solo tiene que leer una fila, esto se puede hacer muy rápido cuando no está forzado por orden y sin un índice de tablas para escanear toda la tabla. En esos casos, puede crear un índice adecuado para aumentar el rendimiento.

+0

"Top 1 solo tiene que leer una fila" - ¿está seguro de eso? Si tenemos un TOP con ORDER BY (99% de los casos), estoy bastante seguro de que el motor debe leer todas las filas para ordenarlas, y luego realizar el TOP. O estoy equivocado ... – RPM1984

+4

RPM1984: El optimizador sabe que la cláusula 'ORDER BY' coincide con el orden del índice, por lo que sabe que no tiene que ordenar, lo que significa que no tiene que leer todas las filas. – Gabe

+0

@RPM 99% eso es muy bueno. A menudo veo Top usado sin orden por (una falla). Pero en su caso solo agregue los índices requeridos. –

2

John Sansom covered las características de rendimiento de MAX contra TOP, sin embargo, sus resultados no respondieron específicamente a su pregunta.

Creo que la respuesta radica en el hecho de que MAX es una función de agregación de propósito general orientada al procesamiento de páginas y páginas de datos, donde TOP es un operador orientado a restringir únicamente el número de filas que se obtienen.

En este caso de uso estrecho, ambas consultas de ejemplo son capaces de ir tras lo mismo y pueden devolver el mismo resultado. La consulta que usa TOP se beneficia de las optimizaciones específicas que ofrece el uso de ese método para este caso de uso.

que vierten a cabo los planes de XML para ambas consultas, y la declaración utilizando MAX contenía:

<DefinedValues> 
    <DefinedValue> 
    <ColumnReference Column="Expr1004" /> 
    <ScalarOperator ScalarString="MAX([db].[dbo].[TheTablesJoined].[theDate])"> 
     <Aggregate AggType="MAX" Distinct="false"> 
     <ScalarOperator> 
      <Identifier> 
      <ColumnReference Database="[db]" Schema="[dbo]" Table="[TheTablesJoined]" Column="theDate" /> 
      </Identifier> 
     </ScalarOperator> 
     </Aggregate> 
    </ScalarOperator> 
    </DefinedValue> 
</DefinedValues> 

La declaración usando TOP contenía este en lugar del XML que define lo que se agrega en la consulta MAX:

<TopExpression> 
    <ScalarOperator ScalarString="(1)"> 
    <Const ConstValue="(1)" /> 
    </ScalarOperator> 
</TopExpression> 

Se están realizando muchas menos operaciones en el plan de ejecución al utilizar TOP.

0

¿Qué edición de SQL Server? Solo Enterprise y Developer usarán vistas indexadas automáticamente, otras ediciones expandirán la consulta para ir contra las tablas subyacentes.

Desea especificar la sugerencia de consulta NOEXPAND. Ver la respuesta para How can i speed up this Indexed View?

Cuestiones relacionadas