Como mis habilidades de ajuste de rendimiento nunca parecen ser suficientes, siempre me pregunto si hay más optimización que pueda realizar en algunas consultas. La situación a la que pertenece esta pregunta es una función MAX de ventana anidada dentro de una subconsulta.
Los datos que estoy investigando son una serie de transacciones en varios grupos de conjuntos más grandes. Tengo 4 campos de importancia, la identificación única de una transacción, la identificación de grupo de un lote de transacciones y las fechas asociadas con la transacción única o grupo de transacciones respectivo. La mayoría de las veces, la Fecha de grupo coincide con la Fecha máxima de transacción única para un lote, pero hay veces en que los ajustes manuales se realizan a través de nuestro sistema y se produce una operación de fecha única después de capturar la fecha de transacción del grupo. Esta edición manual no ajusta la fecha del grupo por diseño.
Lo que identifico en esta consulta son aquellos registros en los que la Fecha única cae después de la Fecha de grupo. La siguiente consulta de muestra genera un equivalente aproximado de mi escenario y la instrucción SELECT devuelve los registros que estoy buscando, sin embargo, ¿estoy abordando esta solución de la manera más eficiente? Esto tarda un tiempo en ejecutarse durante la carga de mi tabla de hechos, ya que mi registro cuenta el número en los 9 dígitos superiores, pero sobre todo mi desdén por las subconsultas me hace preguntarme si hay un mejor enfoque aquí. No estoy tan preocupado por los índices como estoy seguro de que ya están en su lugar; Lo que estoy buscando es un enfoque de consulta alternativo que logre lo mismo, pero aún más eficientemente. Cualquier comentario es bienvenido.
CREATE TABLE #Example
(
UniqueID INT IDENTITY(1,1)
, GroupID INT
, GroupDate DATETIME
, UniqueDate DATETIME
)
CREATE CLUSTERED INDEX [CX_1] ON [#Example]
(
[UniqueID] ASC
)
SET NOCOUNT ON
--Populate some test data
DECLARE @i INT = 0, @j INT = 5, @UniqueDate DATETIME, @GroupDate DATETIME
WHILE @i < 10000
BEGIN
IF((@i + @j)%173 = 0)
BEGIN
SET @UniqueDate = GETDATE()+@i+5
END
ELSE
BEGIN
SET @UniqueDate = GETDATE()+@i
END
SET @GroupDate = GETDATE()+(@j-1)
INSERT INTO #Example (GroupID, GroupDate, UniqueDate)
VALUES (@j, @GroupDate, @UniqueDate)
SET @i = @i + 1
IF (@i % 5 = 0)
BEGIN
SET @j = @j+5
END
END
SET NOCOUNT OFF
CREATE NONCLUSTERED INDEX [IX_2_4_3] ON [#Example]
(
[GroupID] ASC,
[UniqueDate] ASC,
[GroupDate] ASC
)
INCLUDE ([UniqueID])
-- Identify any UniqueDates that are greater than the GroupDate within their GroupID
SELECT UniqueID
, GroupID
, GroupDate
, UniqueDate
FROM (
SELECT UniqueID
, GroupID
, GroupDate
, UniqueDate
, MAX(UniqueDate) OVER (PARTITION BY GroupID) AS maxUniqueDate
FROM #Example
) calc_maxUD
WHERE maxUniqueDate > GroupDate
AND maxUniqueDate = UniqueDate
DROP TABLE #Example
dbfiddle aquí
fuente
Respuestas:
Supongo que no hay índice, ya que no ha proporcionado ninguno.
De inmediato, el siguiente índice eliminará un operador de clasificación en su plan, que de lo contrario potencialmente consumiría mucha memoria:
La subconsulta no es un problema de rendimiento en este caso. En todo caso, buscaría formas de eliminar la función de ventana (MAX ... OVER) para evitar la construcción Nested Loop y Table Spool.
Con el mismo índice, la siguiente consulta puede parecer menos eficiente a primera vista, y va de dos a tres escaneos en la tabla base, pero elimina una gran cantidad de lecturas internamente porque carece de operadores de Spool. Supongo que aún funcionará mejor, especialmente si tiene suficientes núcleos de CPU y rendimiento de E / S en su servidor:
(Nota: agregué una
MERGE JOIN
sugerencia de consulta, pero esto probablemente debería suceder automáticamente si sus estadísticas están en orden. La mejor práctica es dejar sugerencias como estas si puede).fuente
Cuando y si puede actualizar SQL Server 2012 a SQL Server 2016, puede aprovechar el rendimiento mejorado (especialmente para los agregados de ventana sin marco) que proporciona el nuevo operador de Agregado de ventana en modo por lotes.
Casi todos los escenarios de procesamiento de datos grandes funcionan mejor con el almacenamiento del almacén de columnas que con el almacén de filas. Incluso sin cambiar al almacén de columnas para sus tablas base, aún puede obtener los beneficios del nuevo operador 2016 y la ejecución del modo por lotes mediante la creación de un índice filtrado de almacén de columnas no agrupado vacío en una de las tablas base, o mediante la unión externa redundante a un almacén de columnas organizado mesa.
Usando la segunda opción, la consulta se convierte en:
db <> violín
Tenga en cuenta que el único cambio en la consulta original es crear una tabla temporal vacía y agregar la combinación izquierda. El plan de ejecución es:
Para obtener más información y opciones, consulte la excelente serie de Itzik Ben-Gan, Lo que necesita saber sobre el operador agregado de ventana de modo de lote en SQL Server 2016 (en tres partes).
fuente
Voy a tirar la vieja Cruz. Solicítela ahí afuera:
Con algún tipo de índice, funciona bastante bien.
El tiempo de estadísticas y io se ven así (su consulta es el primer resultado)
Los planes de consulta están aquí (nuevamente, el suyo es el primero):
https://www.brentozar.com/pastetheplan/?id=BJYJvqAal
¿Por qué prefiero esta versión? Evito los carretes. Si esos comienzan a derramarse en el disco, se pondrá feo.
Pero es posible que también quieras probar esto.
Si se trata de un DW grande, es posible que prefiera la combinación Hash y el filtrado de filas en la combinación, en lugar de al final de la
TOP 1
consulta como operador de filtro.El plan está aquí: https://www.brentozar.com/pastetheplan/?id=BkUF55ATx
Estadísticas de tiempo y io aquí:
¡Espero que esto ayude!
Una edición, basada en la idea de @ ypercube, y un nuevo índice.
Aquí está el tiempo de estadísticas y io:
Aquí está el plan:
https://www.brentozar.com/pastetheplan/?id=SJv8foR6g
fuente
Echaría un vistazo a
top with ties
Si
GroupDate
es lo mismo porGroupId
entonces:De lo contrario: uso
top with ties
en una expresión de tabla comúndbfiddle: http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=c058994c2f5f3d99b212f06e1dae9fd3
Consulta original
vs
top with ties
en una expresión de tabla comúnfuente
Así que hice un análisis sobre los diversos enfoques publicados hasta ahora, y en mi entorno, parece que el enfoque de Daniel gana constantemente en los tiempos de ejecución. Sorprendentemente (para mí) el tercer enfoque de CROSS APPLY de sp_BlitzErik no estaba tan lejos. Aquí están los resultados si alguien está interesado, pero gracias un TON por todos los enfoques alternativos. ¡Aprendí más al investigar las respuestas a esta pregunta que en mucho tiempo!
fuente
top with ties
hebillas con tantas filas. dbfiddle.uk/…