我一直在阅读大量的查询优化,并且我已经能够优化其中的大部分。
然而,我有一个非常复杂的查询。它为我的会计科目创建累计值。该查询运行时间超过10分钟,我认为这应该是更好的方法来优化它,但我不知道它。
我想要优化的代码是:
SELECT Empresa, IDCuenta, Año, Periodo, Saldo,
((SELECT SUM(Saldo)
FROM
(SELECT Empresa, IDCuenta, ReferenciaOrden, SUM(Saldo) As Saldo
FROM
(SELECT Empresa, IDCuenta, ReferenciaOrden, SUM(Saldo) As Saldo
FROM dbo.GP_ContabilidadTrxActivas
WHERE FechaTransacción<=GETDATE()
GROUP BY Empresa, IDCuenta, ReferenciaOrden
UNION ALL
SELECT Empresa, IDCuenta, ReferenciaOrden, SUM(Saldo) As Saldo
FROM dbo.GP_ContabilidadTrxHistoricas
WHERE FechaTransacción<=GETDATE()
GROUP BY Empresa, IDCuenta, ReferenciaOrden
) As Base
GROUP BY Empresa, IDCuenta, ReferenciaOrden) As BaseInt
WHERE BaseInt.IDCuenta=BaseTotal.IDCuenta AND BaseInt.Empresa = BaseTotal.Empresa
AND BaseInt.ReferenciaOrden<=BaseTotal.ReferenciaOrden
)) As SaldoAcumulado
FROM
(SELECT Empresa, IDCuenta, Año, Periodo, ReferenciaOrden, SUM(Saldo) As Saldo
FROM
(SELECT Empresa, IDCuenta, Año, Periodo, ReferenciaOrden, SUM(Saldo) As Saldo
FROM dbo.GP_ContabilidadTrxActivas WITH (INDEX(IX_ReferenciaOrden)
WHERE FechaTransacción<=GETDATE()
GROUP BY Empresa, IDCuenta, Año, Periodo,ReferenciaOrden
UNION ALL
SELECT Empresa, IDCuenta, Año, Periodo, ReferenciaOrden, SUM(Saldo) As Saldo
FROM dbo.GP_ContabilidadTrxHistoricas WITH (INDEX(IX_ReferenciaOrden)
WHERE FechaTransacción<=GETDATE()
GROUP BY Empresa, IDCuenta, Año, Periodo,ReferenciaOrden
) As Base
GROUP BY Empresa, IDCuenta, Año, Periodo, ReferenciaOrden) As BaseTotal
我为此查询创建的索引是:
CREATE NONCLUSTERED INDEX IX_ReferenciaOrden
ON dbo.GP_ContabilidadTrxHistoricas (IDCuenta ASC, ReferenciaOrden ASC)
INCLUDE (Empresa, Año, Periodo, Saldo, FechaTransacción);
CREATE NONCLUSTERED INDEX IX_ReferenciaOrden
ON dbo.GP_ContabilidadTrxActivas (IDCuenta ASC, ReferenciaOrden ASC)
INCLUDE (Empresa, Año, Periodo, Saldo, FechaTransacción);
执行计划显示,87%的成本来自3项活动:Index Seek,Stream Aggregate和Merge Join,这是部分估计执行计划的图像:
dbo.GP_ContabilidadTrxHistoricas有3.559.617行,而dbo.GP_ContabilidadTrxActivas有102.707行
任何有关优化它的建议都非常受欢迎。提前致谢。
答案 0 :(得分:2)
如果你有SQL Server 2005
或更高,你可以试试这个:
DECLARE @tempTable TABLE (Empresa VARCHAR(100), IDCuenta INT, Año INT, Periodo INT, ReferenciaOrden INT, Saldo MONEY)
INSERT INTO @tempTable (Empresa, IDCuenta, Año, Periodo, ReferenciaOrden, Saldo)
SELECT Empresa, IDCuenta, Año, Periodo, ReferenciaOrden, SUM(Saldo) AS Saldo
FROM (
SELECT Empresa, IDCuenta, Año, Periodo, ReferenciaOrden, Saldo
FROM dbo.GP_ContabilidadTrxActivas
WHERE FechaTransacción <= GETDATE()
UNION ALL
SELECT Empresa, IDCuenta, Año, Periodo, ReferenciaOrden, Saldo
FROM dbo.GP_ContabilidadTrxHistoricas
WHERE FechaTransacción <= GETDATE()
) AS Base
GROUP BY Empresa, IDCuenta, Año, Periodo, ReferenciaOrden
SELECT Empresa, IDCuenta, Año, Periodo, Saldo
, (
SELECT SUM(Saldo)
FROM @tempTable AS BaseInt
WHERE BaseInt.IDCuenta = BaseTotal.IDCuenta
AND BaseInt.Empresa = BaseTotal.Empresa
AND BaseInt.ReferenciaOrden <= BaseTotal.ReferenciaOrden
) AS SaldoAcumulado
FROM @tempTable AS BaseTotal
也可能创建包含FechaTransacción
字段的索引可以提供帮助。因为你用它来过滤表。
答案 1 :(得分:2)
首先,看到“INCLUDE”作为索引的一部分让我感到困惑,因为我从未见过这样,所以我调查了它,并在this post中找到了一个很好的解释/答案。重要的注意事项是INCLUDE应该在不属于像group by这样的东西的字段上。您的查询肯定使用列作为组的一部分,并且应该是查询优化的常规覆盖索引的一部分。
其次,可能会浪费你的时间的是,你正在为基线查询中的每个返回记录对列Saldo进行相关查询,从而每次重复执行每次运行时的性能。我将重构您的查询以获得主FROM子句,因为这两个查询分别运行ONCE EACH并在列上加入它们。看来,对于每个更深层次的项目,您还需要父级别聚合总计。例如,给定区域内的所有销售都是一列,但也包括与ENTIRE区域进行比较的总数。我可能不正确,但这就是它的样子。
因此,我只想在每个当前和历史事务表上创建索引作为以下键。前三列特别是这个顺序,以匹配您的更高级别的聚合,所以THAT TOO优化,而不会达到颗粒级别的Ano,Periodo,FechaTransaccion。
(Empresa,IdCuenta,ReferenciaOrden,Ano,Periodo,FechaTransaccion)包括(saldo)
SELECT
BaseTotal.Empresa,
BaseTotal.IDCuenta,
BaseTotal.Año,
BaseTotal.Periodo,
BaseTotal.Saldo,
SUM( BaseInt.Saldo ) as OrdenSaldo
FROM
( SELECT
Empresa,
IDCuenta,
ReferenciaOrden,
Año,
Periodo,
SUM(Saldo) As Saldo
FROM
( SELECT
Empresa,
IDCuenta,
ReferenciaOrden,
Año,
Periodo,
SUM(Saldo) As Saldo
FROM
dbo.GP_ContabilidadTrxActivas
WHERE
FechaTransacción <= GETDATE()
GROUP BY
Empresa,
IDCuenta,
ReferenciaOrden,
Año,
Periodo
UNION ALL
SELECT
Empresa,
IDCuenta,
Año,
Periodo,
ReferenciaOrden,
SUM(Saldo) As Saldo
FROM
dbo.GP_ContabilidadTrxHistoricas
WHERE
FechaTransacción <= GETDATE()
GROUP BY
Empresa,
IDCuenta,
ReferenciaOrden,
Año,
Periodo ) As Base
GROUP BY
Empresa,
IDCuenta,
ReferenciaOrden,
Año,
Periodo ) As BaseTotal
JOIN
( SELECT
Empresa,
IDCuenta,
ReferenciaOrden,
SUM(Saldo) As Saldo
FROM
( SELECT
Empresa,
IDCuenta,
ReferenciaOrden,
SUM(Saldo) As Saldo
FROM
dbo.GP_ContabilidadTrxActivas
WHERE
FechaTransacción <= GETDATE()
GROUP BY
Empresa,
IDCuenta,
ReferenciaOrden
UNION ALL
SELECT
Empresa,
IDCuenta,
ReferenciaOrden,
SUM(Saldo) As Saldo
FROM
dbo.GP_ContabilidadTrxHistoricas
WHERE
FechaTransacción <= GETDATE()
GROUP BY
Empresa,
IDCuenta,
ReferenciaOrden ) As Base
GROUP BY
Empresa,
IDCuenta,
ReferenciaOrden ) As BaseInt
ON BaseTotal.Empresa = BaseInt.Empresa
AND BaseTotal.IDCuenta = BaseInt.IDCuenta
AND BaseInt.ReferenciaOrden <= BaseTotal.ReferenciaOrden
GROUP BY
BaseTotal.Empresa,
BaseTotal.IDCuenta,
BaseTotal.Año,
BaseTotal.Periodo,
BaseTotal.Saldo,
ORDER BY
BaseTotal.Empresa,
BaseTotal.IDCuenta,
BaseTotal.Año,
BaseTotal.Periodo
答案 2 :(得分:1)
您按日期过滤,我建议您像这样创建索引
CREATE NONCLUSTERED INDEX IX_ReferenciaOrden
ON dbo.GP_ContabilidadTrxHistoricas (FechaTransacción)
如果这对您没有帮助,请尝试在GROUP By子句中添加列。这样,索引的排序方式与GROUp BY
的排序方式相同CREATE NONCLUSTERED INDEX IX_ReferenciaOrden
ON dbo.GP_ContabilidadTrxHistoricas (FechaTransacción, Empresa, IDCuenta, Año, Periodo,ReferenciaOrden)
如果您仍然认为这很慢,请使用select子句中的列创建覆盖索引,这样就不需要访问聚簇索引
CREATE NONCLUSTERED INDEX IX_ReferenciaOrden
ON dbo.GP_ContabilidadTrxHistoricas (FechaTransacción, Empresa, IDCuenta, Año, Periodo,ReferenciaOrden)
INCLUDE(Saldo)
您还可以尝试使用CTE重新格式化查询