帮助优化SQL查询
我已经阅读了很多查询优化,并且能够优化其中的大部分 然而,我有一个非常复杂的问题。它为我的会计账户创造了累积价值。查询运行需要10分钟以上,我认为这应该是一种更好的优化方法,但我没有弄清楚 我要优化的代码如下:帮助优化SQL查询,sql,sql-server,optimization,sql-server-2008-r2,Sql,Sql Server,Optimization,Sql Server 2008 R2,我已经阅读了很多查询优化,并且能够优化其中的大部分 然而,我有一个非常复杂的问题。它为我的会计账户创造了累积价值。查询运行需要10分钟以上,我认为这应该是一种更好的优化方法,但我没有弄清楚 我要优化的代码如下: SELECT Empresa, IDCuenta, Año, Periodo, Saldo, ((SELECT SUM(Saldo) FROM (SELECT Empresa, IDCuenta, ReferenciaOrden, SUM(Saldo) As Saldo
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
执行计划显示87%的成本来自3个活动:索引搜索、流聚合和合并连接,这是部分估计执行计划的图像:
dbo.GP_conttabilidadtrxhistoricas有3.559.617行,dbo.GP_conttabilidadtrxactivas有102.707行
任何优化它的建议都是非常受欢迎的。提前谢谢 如果您有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字段的索引可能会有所帮助。因为您可以按它筛选表。如果您有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字段的索引可能会有所帮助。因为您是按日期筛选表的。您是按日期筛选表的,我建议您像这样创建索引
CREATE NONCLUSTERED INDEX IX_ReferenciaOrden
ON dbo.GP_ContabilidadTrxHistoricas (FechaTransacción)
如果这对您没有帮助,请尝试添加列,因为它们位于GROUPBY子句中。这样,索引的排序方式与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)
您还可以尝试使用CTEs重新格式化查询,因为您正在按日期筛选,我建议您创建如下索引
CREATE NONCLUSTERED INDEX IX_ReferenciaOrden
ON dbo.GP_ContabilidadTrxHistoricas (FechaTransacción)
如果这对您没有帮助,请尝试添加列,因为它们位于GROUPBY子句中。这样,索引的排序方式与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)
您也可以尝试首先使用CTEs重新格式化查询,将INCLUDE作为索引的一部分让我感到困惑,因为我从未看到过这一点,因此我查看了它,并在中找到了一个很好的解释/答案。重要的一点是,INCLUDE应该位于不属于groupby的字段上。您的查询肯定使用列作为GROUPBY的一部分,并且应该是查询优化的正常覆盖索引的一部分 其次,可能会浪费时间的是,您正在对基线查询中返回的每个记录的列Saldo执行相关查询,从而导致每次重复运行的性能降低。我将重新构造您的查询,使其具有main FROM子句,因为这两个查询各运行一次,并分别在列上连接它们。似乎对于每个更深层次的项目,您还需要父级聚合合计。例如,给定区域内的所有销售额为一列,但也包括与整个区域进行比较的总额。我可能是不正确的,但这似乎是事实 因此,我只需要在每个当前和历史事务表上创建索引,作为以下键。前3列特别按此顺序排列,以匹配更高级别的聚合,这样也可以在不使用Ano、Periodo、FechaTransaction等粒度级别的情况下进行优化 Empresa、IdCuenta、REFENCIAORDEN、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
首先,将INCLUDE作为索引的一部分让我感到困惑,因为我从来没有看到过这一点,所以我研究了一下,并在中找到了一个很好的解释/答案。重要的一点是,INCLUDE应该位于不属于groupby的字段上。您的查询肯定使用列作为GROUPBY的一部分,并且应该是查询优化的正常覆盖索引的一部分 其次,可能会浪费时间的是,您正在对基线查询中返回的每个记录的列Saldo执行相关查询,从而导致每次重复运行的性能降低。我将重新构造您的查询,使其具有main FROM子句,因为这两个查询各运行一次,并分别在列上连接它们。似乎对于每个更深层次的项目,您还需要父级聚合合计。例如,给定区域内的所有销售额为一列,但也包括与整个区域进行比较的总额。我可能是不正确的,但这似乎是事实 因此,我只需要在每个当前和历史事务表上创建索引,作为以下键。前3列特别按此顺序排列,以匹配更高级别的聚合,这样也可以在不使用Ano、Periodo、FechaTransaction等粒度级别的情况下进行优化 Empresa、IdCuenta、REFENCIAORDEN、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
您是否也尝试过不使用索引提示?这是否给出了相同的执行者
计划?也许在cte中获取UNION ALL查询,然后使用它?您似乎使用了该查询两次,对吗?在查询提示后,您丢失了。@查询运行正常,我只是错过了在此处键入的问题。@NickyvV是的,我使用了两次,因为我需要计算累计金额。我在没有索引提示的情况下做了这件事,但给了我一个更昂贵的执行计划。我会试试CTE,看看是否效果更好。谢谢您是否也尝试过不使用索引提示?这是否给出了相同的执行计划?也许在cte中获取UNION ALL查询,然后使用它?您似乎使用了该查询两次,对吗?在查询提示后,您丢失了。@查询运行正常,我只是错过了在此处键入的问题。@NickyvV是的,我使用了两次,因为我需要计算累计金额。我在没有索引提示的情况下做了这件事,但给了我一个更昂贵的执行计划。我会试试CTE,看看是否效果更好。谢谢谢谢,我尝试了这个查询,甚至是更优化的,它没有给我正确的结果,因为SaldoAcumulado的结果与Saldo的值相同。如果我去掉参考值,我得到的是上个月的值,而不是在特定时期之前的累计值。还有别的主意吗?对不起。我看错了你的问题。您有哪个版本的SQL Server?这很重要。SQL Server 2008 R2谢谢!请参阅更新的答案。在SQLServer2008中,没有其他方法可以计算运行总数。请尝试使用表变量。谢谢,我尝试了这个查询,甚至是更优化的查询,它没有给我正确的结果,因为SaldoAcumulado得到的值与Saldo相同。如果我去掉参考值,我得到的是上个月的值,而不是在特定时期之前的累计值。还有别的主意吗?对不起。我看错了你的问题。您有哪个版本的SQL Server?这很重要。SQL Server 2008 R2谢谢!请参阅更新的答案。在SQLServer2008中,没有其他方法可以计算运行总数。请尝试使用table变量。谢谢,我创建了第三个索引,它略有改进,现在大约是8分钟。我尝试了CTEs,但没有任何区别,只是改进了查询的顺序。谢谢,我创建了第三个索引,它略有改进,现在大约8分钟。我尝试了CTE,但没有任何区别,只是改进了查询的顺序。它不起作用,因为它没有遵循业务规则。请记住,我需要获取跑步总量,我做了以下更改:BaseTotal.referenceaorden=BaseInt.IDCuenta for BaseTotal.referenceaorden@MariPlaza,那么您能否在文章末尾展示一些您希望查询显示的跑步总量列的示例,如您所述。在这篇和以后的文章中,这样的事情通常会让我和其他人得到更好的初步答案。而且我修改了查询,希望能够处理您的汇总值。请也检查一下。@DRappp好的,谢谢你的反馈,我会在将来包括它。我检查了您的查询,并尝试修改它,以获得使用您的技术所期望的数据,但它不起作用。我已经研究了很多关于运行总计的内容,这不是一个简单的查询,也不是所有的技术都能正确地检索到它。谢谢你的帮助。它不起作用,因为它没有遵守业务规则。请记住,我需要获取跑步总量,我做了以下更改:BaseTotal.referenceaorden=BaseInt.IDCuenta for BaseTotal.referenceaorden@MariPlaza,那么您能否在文章末尾展示一些您希望查询显示的跑步总量列的示例,如您所述。在这篇和以后的文章中,这样的事情通常会让我和其他人得到更好的初步答案。而且我修改了查询,希望能够处理您的汇总值。请也检查一下。@DRappp好的,谢谢你的反馈,我会在将来包括它。我检查了您的查询,并尝试修改它,以获得使用您的技术所期望的数据,但它不起作用。我已经研究了很多关于运行总计的内容,这不是一个简单的查询,也不是所有的技术都能正确地检索到它。谢谢你的帮助。