提高 sql 查询的性能
Improve performance of a sql query
我正在寻找一些 tips/tricks 来提高将多个 SELECT 语句插入 table 的存储过程的性能。我加入的所有对象都已编入索引。
我认为这个存储过程需要将近一个小时才能完成 运行 的原因是因为有多个 SELECT 语句使用以下两个视图:rvw_FinancialLineItemValues 和 rvw_FinancialLineItems
此外,每个 SELECT 语句都对 AccountNumber、LineItemTypeID 和来自上述两个视图的其他一些字段值使用特定的硬编码值。
如果我创建一个临时 table,它会立即获取这些 SELECT 语句所需的所有数据,然后在我的连接中使用这个临时 table,它会提高性能吗?
是否有任何其他方法可以提高性能和可管理性?
SELECT
@scenarioid,
@portfolioid,
pa.Id,
pa.ExternalID,
(select value from fn_split(i.AccountNumber,'.') where id = 1),
ac.[Description],
cl.Name,
NullIf((select value from fn_split(i.AccountNumber,'.') where id = 2),''),
NullIf((select value from fn_split(i.AccountNumber,'.') where id = 3),''),
ty.Name,
v.[Date],
cast(SUM(v.Amount) as decimal(13,2)),
GETDATE()
FROM rvw_FinancialLineItems i
INNER JOIN rvw_Scenarios sc
ON i.ScenarioId = sc.Id
AND sc.Id = @scenarioid
AND sc.PortfolioId = @portfolioid
INNER JOIN #pa AS pa
ON i.PropertyAssetID = pa.Id
INNER JOIN rvw_FinancialLineItemValues v
ON i.ScenarioId = v.ScenarioId
AND i.PropertyAssetID = v.PropertyAssetID
AND i.Id = v.FinancialLineItemId
AND ((i.BusinessEntityTypeId = 11
AND i.LineItemTypeId = 3002)
OR (i.LineItemTypeId IN (2005, 2010, 2003, 2125, 2209, 5012, 6001)
AND i.ModeledEntityKey = 1))
AND i.AccountNumber not in ('401ZZ','403ZZ')
AND i.AccountNumber not in ('401XX')
AND i.AccountNumber not in ('40310','41110','42010','41510','40190','40110') -- exclude lease-level revenues selected below
AND v.[Date] BETWEEN @fromdate AND
CASE
WHEN pa.AnalysisEnd < @todate THEN pa.AnalysisEnd
ELSE @todate
END
AND v.ResultSet IN (0, 4)
INNER JOIN rvw_Portfolios po
ON po.Id = @portfolioid
INNER JOIN Accounts ac
ON po.ChartOfAccountId = ac.ChartOfAccountId
AND i.AccountNumber = ac.AccountNumber
AND ac.HasSubAccounts = 0
INNER JOIN fn_LookupClassTypes() cl
ON ac.ClassTypeId = cl.Id
INNER JOIN LineItemTypes ty
ON ac.LineItemTypeId = ty.Id
LEFT JOIN OtherRevenues r
ON i.PropertyAssetID = r.PropertyAssetID
AND i.AccountNumber = r.AccountID
AND v.[Date] BETWEEN r.[Begin] AND r.[End]
WHERE (r.IsMemo IS NULL
OR r.IsMemo = 0)
GROUP BY pa.AnalysisBegin
,pa.Id
,pa.ExternalID
,i.AccountNumber
,ac.[Description]
,cl.Name
,ty.Name
,v.[Date]
HAVING SUM(v.amount) <> 0
您应该 运行 使用 SET SHOWPLAN ALL ON
或使用 Management Studio Save Execution Plan
进行查询并查找效率低下的地方。
有一些在线资源可以帮助分析结果,例如:
http://www.sqlservercentral.com/articles/books/65831/
另见 How do I obtain a Query Execution Plan?
我会先看下面的。此处与您的存储过程相关的等待类型是什么?您是否看到很多磁盘 io 时间?事情是在记忆中完成的吗?也许有网络延迟拉动那么多信息。
接下来,该程序的计划是什么样的,它在哪里显示所有工作都已完成?
正如你所说,观点肯定是个问题。您可能已经对表进行了预处理,因此您不必进行那么多的连接。特别是您看到 CPU 花费最多的加入。
相关子查询通常很慢,在尝试提高性能时切勿使用。使用 fn_split 创建一个临时 table 索引,如果需要的话,然后加入它以获得你需要的值。您可能需要多次加入以获得不同的价值,而实际上并不知道我很难可视化的数据。
使用OR也不利于性能。在派生的 table 中使用 UNION ALL 代替。
由于您在视图 rvw_FinancialLineItems 上拥有所有这些条件,是的,将它们拉出到临时 table 然后索引临时 table.[=10 可能会起作用=]
您可能还会看看使用视图是否是个好主意。通常视图与许多 table 连接,您没有从中获取数据,因此性能不如仅查询您实际需要的 table。如果您的组织愚蠢到制作调用视图的视图,则尤其如此。
首先,您使用的是哪个 fn_split()
UDF?如果您没有使用 table-Valued 内联 UDF,那么这是出了名的慢。
其次,UDF fn_LookupClassTypes()
是内联 table 值 UDF 吗?如果不是,将其转换为内联 Table-Valued UDF。
最后,您的 SQL 查询有一些冗余。试试这个,看看效果如何。
SELECT @scenarioid, @portfolioid, pa.Id, pa.ExternalID,
(select value from fn_split(i.AccountNumber,'.')
where id = 1), ac.[Description], cl.Name,
NullIf((select value from fn_split(i.AccountNumber,'.')
where id = 2),''),
NullIf((select value from fn_split(i.AccountNumber,'.')
where id = 3),''), ty.Name, v.[Date],
cast(SUM(v.Amount) as decimal(13,2)), GETDATE()
FROM rvw_FinancialLineItems i
JOIN rvw_Scenarios sc ON sc.Id = i.ScenarioId
JOIN #pa AS pa ON pa.Id = i.PropertyAssetID
JOIN rvw_FinancialLineItemValues v
ON v.ScenarioId = i.ScenarioId
AND v.PropertyAssetID = i.PropertyAssetID
AND v.FinancialLineItemId = i.Id
JOIN rvw_Portfolios po ON po.Id = sc.portfolioid
JOIN Accounts ac
ON ac.ChartOfAccountId = po.ChartOfAccountId
AND ac.AccountNumber = i.AccountNumber
JOIN fn_LookupClassTypes() cl On cl.Id = ac.ClassTypeId
JOIN LineItemTypes ty On ty.Id = ac.LineItemTypeId
Left JOIN OtherRevenues r
ON r.PropertyAssetID = i.PropertyAssetID
AND r.AccountID = i.AccountNumber
AND v.[Date] BETWEEN r.[Begin] AND r.[End]
WHERE i.ScenarioId = @scenarioid
and ac.HasSubAccounts = 0
and sc.PortfolioId = @portfolioid
and IsNull(r.IsMemo, 0) = 0)
and v.ResultSet In (0, 4)
and i.AccountNumber not in
('401XX', '401ZZ','403ZZ','40310','41110',
'42010','41510','40190','40110')
and v.[Date] BETWEEN @fromdate AND
CASE WHEN pa.AnalysisEnd < @todate
THEN pa.AnalysisEnd ELSE @todate END
and ((i.LineItemTypeId = 3002 and i.BusinessEntityTypeId = 11) OR
(i.ModeledEntityKey = 1 and i.LineItemTypeId IN
(2005, 2010, 2003, 2125, 2209, 5012, 6001)))
GROUP BY pa.AnalysisBegin,pa.Id, pa.ExternalID, i.AccountNumber,
ac.[Description],cl.Name,ty.Name,v.[Date]
HAVING SUM(v.amount) <> 0
我正在寻找一些 tips/tricks 来提高将多个 SELECT 语句插入 table 的存储过程的性能。我加入的所有对象都已编入索引。
我认为这个存储过程需要将近一个小时才能完成 运行 的原因是因为有多个 SELECT 语句使用以下两个视图:rvw_FinancialLineItemValues 和 rvw_FinancialLineItems
此外,每个 SELECT 语句都对 AccountNumber、LineItemTypeID 和来自上述两个视图的其他一些字段值使用特定的硬编码值。
如果我创建一个临时 table,它会立即获取这些 SELECT 语句所需的所有数据,然后在我的连接中使用这个临时 table,它会提高性能吗?
是否有任何其他方法可以提高性能和可管理性?
SELECT
@scenarioid,
@portfolioid,
pa.Id,
pa.ExternalID,
(select value from fn_split(i.AccountNumber,'.') where id = 1),
ac.[Description],
cl.Name,
NullIf((select value from fn_split(i.AccountNumber,'.') where id = 2),''),
NullIf((select value from fn_split(i.AccountNumber,'.') where id = 3),''),
ty.Name,
v.[Date],
cast(SUM(v.Amount) as decimal(13,2)),
GETDATE()
FROM rvw_FinancialLineItems i
INNER JOIN rvw_Scenarios sc
ON i.ScenarioId = sc.Id
AND sc.Id = @scenarioid
AND sc.PortfolioId = @portfolioid
INNER JOIN #pa AS pa
ON i.PropertyAssetID = pa.Id
INNER JOIN rvw_FinancialLineItemValues v
ON i.ScenarioId = v.ScenarioId
AND i.PropertyAssetID = v.PropertyAssetID
AND i.Id = v.FinancialLineItemId
AND ((i.BusinessEntityTypeId = 11
AND i.LineItemTypeId = 3002)
OR (i.LineItemTypeId IN (2005, 2010, 2003, 2125, 2209, 5012, 6001)
AND i.ModeledEntityKey = 1))
AND i.AccountNumber not in ('401ZZ','403ZZ')
AND i.AccountNumber not in ('401XX')
AND i.AccountNumber not in ('40310','41110','42010','41510','40190','40110') -- exclude lease-level revenues selected below
AND v.[Date] BETWEEN @fromdate AND
CASE
WHEN pa.AnalysisEnd < @todate THEN pa.AnalysisEnd
ELSE @todate
END
AND v.ResultSet IN (0, 4)
INNER JOIN rvw_Portfolios po
ON po.Id = @portfolioid
INNER JOIN Accounts ac
ON po.ChartOfAccountId = ac.ChartOfAccountId
AND i.AccountNumber = ac.AccountNumber
AND ac.HasSubAccounts = 0
INNER JOIN fn_LookupClassTypes() cl
ON ac.ClassTypeId = cl.Id
INNER JOIN LineItemTypes ty
ON ac.LineItemTypeId = ty.Id
LEFT JOIN OtherRevenues r
ON i.PropertyAssetID = r.PropertyAssetID
AND i.AccountNumber = r.AccountID
AND v.[Date] BETWEEN r.[Begin] AND r.[End]
WHERE (r.IsMemo IS NULL
OR r.IsMemo = 0)
GROUP BY pa.AnalysisBegin
,pa.Id
,pa.ExternalID
,i.AccountNumber
,ac.[Description]
,cl.Name
,ty.Name
,v.[Date]
HAVING SUM(v.amount) <> 0
您应该 运行 使用 SET SHOWPLAN ALL ON
或使用 Management Studio Save Execution Plan
进行查询并查找效率低下的地方。
有一些在线资源可以帮助分析结果,例如: http://www.sqlservercentral.com/articles/books/65831/
另见 How do I obtain a Query Execution Plan?
我会先看下面的。此处与您的存储过程相关的等待类型是什么?您是否看到很多磁盘 io 时间?事情是在记忆中完成的吗?也许有网络延迟拉动那么多信息。
接下来,该程序的计划是什么样的,它在哪里显示所有工作都已完成?
正如你所说,观点肯定是个问题。您可能已经对表进行了预处理,因此您不必进行那么多的连接。特别是您看到 CPU 花费最多的加入。
相关子查询通常很慢,在尝试提高性能时切勿使用。使用 fn_split 创建一个临时 table 索引,如果需要的话,然后加入它以获得你需要的值。您可能需要多次加入以获得不同的价值,而实际上并不知道我很难可视化的数据。
使用OR也不利于性能。在派生的 table 中使用 UNION ALL 代替。
由于您在视图 rvw_FinancialLineItems 上拥有所有这些条件,是的,将它们拉出到临时 table 然后索引临时 table.[=10 可能会起作用=]
您可能还会看看使用视图是否是个好主意。通常视图与许多 table 连接,您没有从中获取数据,因此性能不如仅查询您实际需要的 table。如果您的组织愚蠢到制作调用视图的视图,则尤其如此。
首先,您使用的是哪个 fn_split()
UDF?如果您没有使用 table-Valued 内联 UDF,那么这是出了名的慢。
其次,UDF fn_LookupClassTypes()
是内联 table 值 UDF 吗?如果不是,将其转换为内联 Table-Valued UDF。
最后,您的 SQL 查询有一些冗余。试试这个,看看效果如何。
SELECT @scenarioid, @portfolioid, pa.Id, pa.ExternalID,
(select value from fn_split(i.AccountNumber,'.')
where id = 1), ac.[Description], cl.Name,
NullIf((select value from fn_split(i.AccountNumber,'.')
where id = 2),''),
NullIf((select value from fn_split(i.AccountNumber,'.')
where id = 3),''), ty.Name, v.[Date],
cast(SUM(v.Amount) as decimal(13,2)), GETDATE()
FROM rvw_FinancialLineItems i
JOIN rvw_Scenarios sc ON sc.Id = i.ScenarioId
JOIN #pa AS pa ON pa.Id = i.PropertyAssetID
JOIN rvw_FinancialLineItemValues v
ON v.ScenarioId = i.ScenarioId
AND v.PropertyAssetID = i.PropertyAssetID
AND v.FinancialLineItemId = i.Id
JOIN rvw_Portfolios po ON po.Id = sc.portfolioid
JOIN Accounts ac
ON ac.ChartOfAccountId = po.ChartOfAccountId
AND ac.AccountNumber = i.AccountNumber
JOIN fn_LookupClassTypes() cl On cl.Id = ac.ClassTypeId
JOIN LineItemTypes ty On ty.Id = ac.LineItemTypeId
Left JOIN OtherRevenues r
ON r.PropertyAssetID = i.PropertyAssetID
AND r.AccountID = i.AccountNumber
AND v.[Date] BETWEEN r.[Begin] AND r.[End]
WHERE i.ScenarioId = @scenarioid
and ac.HasSubAccounts = 0
and sc.PortfolioId = @portfolioid
and IsNull(r.IsMemo, 0) = 0)
and v.ResultSet In (0, 4)
and i.AccountNumber not in
('401XX', '401ZZ','403ZZ','40310','41110',
'42010','41510','40190','40110')
and v.[Date] BETWEEN @fromdate AND
CASE WHEN pa.AnalysisEnd < @todate
THEN pa.AnalysisEnd ELSE @todate END
and ((i.LineItemTypeId = 3002 and i.BusinessEntityTypeId = 11) OR
(i.ModeledEntityKey = 1 and i.LineItemTypeId IN
(2005, 2010, 2003, 2125, 2209, 5012, 6001)))
GROUP BY pa.AnalysisBegin,pa.Id, pa.ExternalID, i.AccountNumber,
ac.[Description],cl.Name,ty.Name,v.[Date]
HAVING SUM(v.amount) <> 0