Creé la tabla big_table de acuerdo con su esquema
create table big_table
(
updatetime datetime not null,
name char(14) not null,
TheData float,
primary key(Name,updatetime)
)
Luego llené la tabla con 50,000 filas con este código:
DECLARE @ROWNUM as bigint = 1
WHILE(1=1)
BEGIN
set @rownum = @ROWNUM + 1
insert into big_table values(getdate(),'name' + cast(@rownum as CHAR), cast(@rownum as float))
if @ROWNUM > 50000
BREAK;
END
Usando SSMS, probé ambas consultas y me di cuenta de que en la primera consulta está buscando el MAX de TheData y en el segundo, el MAX del tiempo de actualización
Modifiqué así la primera consulta para obtener también el MAX del tiempo de actualización
set statistics time on -- execution time
set statistics io on -- io stats (how many pages read, temp tables)
-- query 1
SELECT MAX([UpdateTime])
FROM big_table
-- query 2
SELECT MAX([UpdateTime]) AS value
from
(
SELECT [UpdateTime]
FROM big_table
group by [UpdateTime]
) as t
set statistics time off
set statistics io off
Usando Statistics Time obtengo la cantidad de milisegundos necesarios para analizar, compilar y ejecutar cada instrucción
Usando Statistics IO obtengo información sobre la actividad del disco
ESTADÍSTICAS TIEMPO y ESTADÍSTICAS IO proporcionan información útil. Tal como se usaron las tablas temporales (indicado por la tabla de trabajo). También cuántas páginas lógicas leídas se leyeron, lo que indica el número de páginas de la base de datos leídas de la memoria caché.
Luego activo el plan de ejecución con CTRL + M (activa mostrar el plan de ejecución real) y luego ejecuto con F5.
Esto proporcionará una comparación de ambas consultas.
Aquí está la salida de la pestaña Mensajes
- Consulta 1
Tabla 'tabla_grande'. Cuenta de escaneo 1, lecturas lógicas 543 , lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas de lectura lob 0.
Tiempos de ejecución de SQL Server:
tiempo de CPU = 16 ms, tiempo transcurrido = 6 ms .
- Consulta 2
Mesa 'Mesa de trabajo '. Recuento de exploración 0, lecturas lógicas 0, lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas de lectura lob 0.
Tabla 'tabla_grande'. Cuenta de escaneo 1, lecturas lógicas 543 , lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas de lectura lob 0.
Tiempos de ejecución de SQL Server:
tiempo de CPU = 0 ms, tiempo transcurrido = 35 ms .
Ambas consultas dan como resultado 543 lecturas lógicas, pero la segunda consulta tiene un tiempo transcurrido de 35 ms, mientras que la primera tiene solo 6 ms. También notará que la segunda consulta da como resultado el uso de tablas temporales en tempdb, indicadas por la palabra tabla de trabajo . Aunque todos los valores de la tabla de trabajo están en 0, el trabajo todavía se realizó en tempdb.
Luego está la salida de la pestaña del plan de ejecución real al lado de la pestaña Mensajes
De acuerdo con el plan de ejecución proporcionado por MSSQL, la segunda consulta que proporcionó tiene un costo de lote total del 64%, mientras que la primera solo cuesta el 36% del lote total, por lo que la primera consulta requiere menos trabajo.
Con SSMS, puede probar y comparar sus consultas y descubrir exactamente cómo MSSQL analiza sus consultas y qué objetos: tablas, índices y / o estadísticas, si se están utilizando, para satisfacer esas consultas.
Una nota adicional adicional a tener en cuenta cuando se realizan pruebas es limpiar el caché antes de realizar las pruebas, si es posible. Esto ayuda a garantizar que las comparaciones sean precisas y esto es importante cuando se piensa en la actividad del disco. Comienzo con DBCC DROPCLEANBUFFERS y DBCC FREEPROCCACHE para limpiar todo el caché. Sin embargo, tenga cuidado de no utilizar estos comandos en un servidor de producción realmente en uso, ya que obligará efectivamente al servidor a leer todo desde el disco a la memoria.
Aquí está la documentación relevante.
- Borre el caché del plan con DBCC FREEPROCCACHE
- Elimine todo de la agrupación de almacenamientos intermedios con DBCC DROPCLEANBUFFERS
El uso de estos comandos puede no ser posible dependiendo de cómo se use su entorno.
Actualizado 28/10 12:46 pm
Se realizaron correcciones en la imagen del plan de ejecución y en la salida de estadísticas.
getdate()
salir del círculoLa reescritura podría haber ayudado si SQL Server implementara el escaneo de índice, pero no lo hace.
El escaneo de índice permite que un motor de base de datos busque el siguiente valor de índice diferente en lugar de escanear todos los duplicados (o subclaves irrelevantes) intermedios. En su caso, skip-scan permitiría que el motor encuentre
MAX(UpdateTime)
el primeroName
, salte alMAX(UpdateTime)
segundoName
... y así sucesivamente. El último paso sería encontrar losMAX(UpdateTime)
candidatos de uno por nombre.Puede simular esto hasta cierto punto utilizando un CTE recursivo, pero es un poco desordenado y no es tan eficiente como lo sería el omitir escaneo incorporado:
Ese plan realiza una búsqueda única para cada distintivo
Name
, luego encuentra el más altoUpdateTime
de los candidatos. Su rendimiento en relación con un escaneo completo simple de la tabla depende de cuántos duplicados haya por personaName
y de si las páginas tocadas por las búsquedas individuales están en la memoria o no.Soluciones alternativas
Si puede crear un nuevo índice en esta tabla, una buena opción para esta consulta sería un índice
UpdateTime
solo:Este índice permitirá que el motor de ejecución encuentre el más alto
UpdateTime
con una búsqueda única hasta el final del índice b-tree:Este plan consume solo unas pocas IO lógicas (para navegar por los niveles de b-tree) y se completa de inmediato. Tenga en cuenta que la exploración de índice en el plan no es una exploración completa del nuevo índice, simplemente devuelve una fila desde el 'final' del índice.
Si no desea crear un nuevo índice completo en la tabla, puede considerar una vista indizada que contenga solo los
UpdateTime
valores únicos :Esto tiene la ventaja de crear solo una estructura con tantas filas como
UpdateTime
valores únicos , aunque cada consulta que cambie datos en la tabla base tendrá operadores adicionales agregados a su plan de ejecución para mantener la vista indexada. La consulta para encontrar elUpdateTime
valor máximo sería:fuente