Select SQL 使用 table 别名查看速度慢
Select SQL View Slow with table alias
我很困惑为什么在使用 table 别名(25 秒)时选择我的 SQL 视图如此缓慢,但在删除别名时运行速度如此之快(2 秒)
-本次查询耗时25秒。
SELECT [Extent1].[Id] AS [Id],
[Extent1].[ProjectId] AS [ProjectId],
[Extent1].[ProjectWorkOrderId] AS [ProjectWorkOrderId],
[Extent1].[Project] AS [Project],
[Extent1].[SubcontractorId] AS [SubcontractorId],
[Extent1].[Subcontractor] AS [Subcontractor],
[Extent1].[ValuationNumber] AS [ValuationNumber],
[Extent1].[WorksOrderName] AS [WorksOrderName],
[Extent1].[NewGross],
[Extent1].[CumulativeGross],
[Extent1].[CreateByName] AS [CreateByName],
[Extent1].[CreateDate] AS [CreateDate],
[Extent1].[FinalDateForPayment] AS [FinalDateForPayment],
[Extent1].[CreateByEmail] AS [CreateByEmail],
[Extent1].[Deleted] AS [Deleted],
[Extent1].[ValuationStatusCategoryId] AS [ValuationStatusCategoryId]
FROM [dbo].[ValuationsTotal] AS [Extent1]
-本次查询耗时2秒。
SELECT [Id],
[ProjectId],
[Project],
[SubcontractorId],
[Subcontractor],
[NewGross],
[ProjectWorkOrderId],
[ValuationNumber],
[WorksOrderName],
[CreateByName],
[CreateDate],
[CreateByEmail],
[Deleted],
[ValuationStatusCategoryId],
[FinalDateForPayment],
[CumulativeGross]
FROM [dbo].[ValuationsTotal]
这是我的SQL查看代码-
WITH ValuationTotalsTemp(Id, ProjectId, Project, SubcontractorId, Subcontractor, WorksOrderName, NewGross, ProjectWorkOrderId, ValuationNumber, CreateByName, CreateDate, CreateByEmail, Deleted, ValuationStatusCategoryId, FinalDateForPayment)
AS (SELECT vi.ValuationId AS Id,
v.ProjectId,
p.NAME,
b.Id AS Expr1,
b.NAME AS Expr2,
wo.OrderNumber,
SUM(vi.ValuationQuantity * pbc.BudgetRate) AS 'NewGross',
sa.ProjectWorkOrderId,
v.ValuationNumber,
up.FirstName + ' ' + up.LastName AS Expr3,
v.CreateDate,
up.Email,
v.Deleted,
v.ValuationStatusCategoryId,
sa.FinalDateForPayment
FROM dbo.ValuationItems AS vi
INNER JOIN dbo.ProjectBudgetCosts AS pbc
ON vi.ProjectBudgetCostId = pbc.Id
INNER JOIN dbo.Valuations AS v
ON vi.ValuationId = v.Id
INNER JOIN dbo.ProjectSubcontractorApplications AS sa
ON sa.Id = v.ProjectSubcontractorApplicationId
INNER JOIN dbo.Projects AS p
ON p.Id = v.ProjectId
INNER JOIN dbo.ProjectWorkOrders AS wo
ON wo.Id = sa.ProjectWorkOrderId
INNER JOIN dbo.ProjectSubcontractors AS sub
ON sub.Id = wo.ProjectSubcontractorId
INNER JOIN dbo.Businesses AS b
ON b.Id = sub.BusinessId
INNER JOIN dbo.UserProfile AS up
ON up.Id = v.CreateBy
WHERE ( vi.Deleted = 0 )
AND ( v.Deleted = 0 )
GROUP BY vi.ValuationId,
v.ProjectId,
p.NAME,
b.Id,
b.NAME,
wo.OrderNumber,
sa.ProjectWorkOrderId,
v.ValuationNumber,
up.FirstName + ' ' + up.LastName,
v.CreateDate,
up.Email,
v.Deleted,
v.ValuationStatusCategoryId,
sa.FinalDateForPayment)
SELECT Id,
ProjectId,
Project,
SubcontractorId,
Subcontractor,
NewGross,
ProjectWorkOrderId,
ValuationNumber,
WorksOrderName,
CreateByName,
CreateDate,
CreateByEmail,
Deleted,
ValuationStatusCategoryId,
FinalDateForPayment,
(SELECT SUM(NewGross) AS Expr1
FROM ValuationTotalsTemp AS tt
WHERE ( ProjectWorkOrderId = t.ProjectWorkOrderId )
AND ( t.ValuationNumber >= ValuationNumber )
GROUP BY ProjectWorkOrderId) AS CumulativeGross
FROM ValuationTotalsTemp AS t
知道这是为什么吗?
SQL 查询使用 table 别名运行,因为这是从 Entity Framework 生成的,所以我无法更改它。我将需要修改我的 SQL 视图,以便能够在不影响性能的情况下处理 table 别名。
这两个查询不同(列顺序!)。假设第一个查询使用索引并因此更快是合理的。我怀疑它与别名有什么关系。
对于 grins 会拿出 where 试试这个吗?
我可能会在最后做一堆循环连接和过滤
这可能会使其预先过滤
FROM dbo.ValuationItems AS vi
INNER JOIN dbo.Valuations AS v
ON vi.ValuationId = v.Id
AND vi.Deleted = 0
AND v.Deleted = 0
-- other joins
-- NO where
如果您正在进行大量循环连接,请尝试内部散列连接(全部)
执行计划大不相同。
慢的有一部分跳出来是有问题的。它估计单行将被输入到嵌套循环连接中,并导致对 ValuationItems 进行单次扫描。实际上,它最终会执行超过 1,000 次这样的扫描。
估计
实际
SQL Server 2014 引入了新的基数估计器。你的快速计划正在使用它。这在 XML 中显示为 CardinalityEstimationModelVersion="120"
您的缓慢计划不是 (CardinalityEstimationModelVersion="70"
)。
因此,在这种情况下,新估算器使用的假设似乎可以为您提供更好的计划。
差异的原因可能是因为速度快的是 运行 跨数据库(参考 [ProbeProduction].[dbo].[ValuationsTotal])并且您正在执行它的数据库可能具有兼容级别2014 年自动获得新的 CardinalityEstimator。
慢的一个在 ProbeProduction
本身的上下文中执行,我假设该数据库的兼容级别必须 < 2014 - 所以您默认使用遗留基数估计器。
您可以使用 OPTION (QUERYTRACEON 2312)
让慢速查询使用新的基数估计器(更改数据库兼容模式以全局改变行为不应该在没有仔细测试现有查询的情况下完成,因为它可能会导致回归和改进)。
或者,您可以尝试在旧 CE 的限制范围内调整查询。也许添加连接提示以鼓励它使用更类似于更快计划的东西。
我很困惑为什么在使用 table 别名(25 秒)时选择我的 SQL 视图如此缓慢,但在删除别名时运行速度如此之快(2 秒)
-本次查询耗时25秒。
SELECT [Extent1].[Id] AS [Id],
[Extent1].[ProjectId] AS [ProjectId],
[Extent1].[ProjectWorkOrderId] AS [ProjectWorkOrderId],
[Extent1].[Project] AS [Project],
[Extent1].[SubcontractorId] AS [SubcontractorId],
[Extent1].[Subcontractor] AS [Subcontractor],
[Extent1].[ValuationNumber] AS [ValuationNumber],
[Extent1].[WorksOrderName] AS [WorksOrderName],
[Extent1].[NewGross],
[Extent1].[CumulativeGross],
[Extent1].[CreateByName] AS [CreateByName],
[Extent1].[CreateDate] AS [CreateDate],
[Extent1].[FinalDateForPayment] AS [FinalDateForPayment],
[Extent1].[CreateByEmail] AS [CreateByEmail],
[Extent1].[Deleted] AS [Deleted],
[Extent1].[ValuationStatusCategoryId] AS [ValuationStatusCategoryId]
FROM [dbo].[ValuationsTotal] AS [Extent1]
-本次查询耗时2秒。
SELECT [Id],
[ProjectId],
[Project],
[SubcontractorId],
[Subcontractor],
[NewGross],
[ProjectWorkOrderId],
[ValuationNumber],
[WorksOrderName],
[CreateByName],
[CreateDate],
[CreateByEmail],
[Deleted],
[ValuationStatusCategoryId],
[FinalDateForPayment],
[CumulativeGross]
FROM [dbo].[ValuationsTotal]
这是我的SQL查看代码-
WITH ValuationTotalsTemp(Id, ProjectId, Project, SubcontractorId, Subcontractor, WorksOrderName, NewGross, ProjectWorkOrderId, ValuationNumber, CreateByName, CreateDate, CreateByEmail, Deleted, ValuationStatusCategoryId, FinalDateForPayment)
AS (SELECT vi.ValuationId AS Id,
v.ProjectId,
p.NAME,
b.Id AS Expr1,
b.NAME AS Expr2,
wo.OrderNumber,
SUM(vi.ValuationQuantity * pbc.BudgetRate) AS 'NewGross',
sa.ProjectWorkOrderId,
v.ValuationNumber,
up.FirstName + ' ' + up.LastName AS Expr3,
v.CreateDate,
up.Email,
v.Deleted,
v.ValuationStatusCategoryId,
sa.FinalDateForPayment
FROM dbo.ValuationItems AS vi
INNER JOIN dbo.ProjectBudgetCosts AS pbc
ON vi.ProjectBudgetCostId = pbc.Id
INNER JOIN dbo.Valuations AS v
ON vi.ValuationId = v.Id
INNER JOIN dbo.ProjectSubcontractorApplications AS sa
ON sa.Id = v.ProjectSubcontractorApplicationId
INNER JOIN dbo.Projects AS p
ON p.Id = v.ProjectId
INNER JOIN dbo.ProjectWorkOrders AS wo
ON wo.Id = sa.ProjectWorkOrderId
INNER JOIN dbo.ProjectSubcontractors AS sub
ON sub.Id = wo.ProjectSubcontractorId
INNER JOIN dbo.Businesses AS b
ON b.Id = sub.BusinessId
INNER JOIN dbo.UserProfile AS up
ON up.Id = v.CreateBy
WHERE ( vi.Deleted = 0 )
AND ( v.Deleted = 0 )
GROUP BY vi.ValuationId,
v.ProjectId,
p.NAME,
b.Id,
b.NAME,
wo.OrderNumber,
sa.ProjectWorkOrderId,
v.ValuationNumber,
up.FirstName + ' ' + up.LastName,
v.CreateDate,
up.Email,
v.Deleted,
v.ValuationStatusCategoryId,
sa.FinalDateForPayment)
SELECT Id,
ProjectId,
Project,
SubcontractorId,
Subcontractor,
NewGross,
ProjectWorkOrderId,
ValuationNumber,
WorksOrderName,
CreateByName,
CreateDate,
CreateByEmail,
Deleted,
ValuationStatusCategoryId,
FinalDateForPayment,
(SELECT SUM(NewGross) AS Expr1
FROM ValuationTotalsTemp AS tt
WHERE ( ProjectWorkOrderId = t.ProjectWorkOrderId )
AND ( t.ValuationNumber >= ValuationNumber )
GROUP BY ProjectWorkOrderId) AS CumulativeGross
FROM ValuationTotalsTemp AS t
知道这是为什么吗?
SQL 查询使用 table 别名运行,因为这是从 Entity Framework 生成的,所以我无法更改它。我将需要修改我的 SQL 视图,以便能够在不影响性能的情况下处理 table 别名。
这两个查询不同(列顺序!)。假设第一个查询使用索引并因此更快是合理的。我怀疑它与别名有什么关系。
对于 grins 会拿出 where 试试这个吗?
我可能会在最后做一堆循环连接和过滤
这可能会使其预先过滤
FROM dbo.ValuationItems AS vi
INNER JOIN dbo.Valuations AS v
ON vi.ValuationId = v.Id
AND vi.Deleted = 0
AND v.Deleted = 0
-- other joins
-- NO where
如果您正在进行大量循环连接,请尝试内部散列连接(全部)
执行计划大不相同。
慢的有一部分跳出来是有问题的。它估计单行将被输入到嵌套循环连接中,并导致对 ValuationItems 进行单次扫描。实际上,它最终会执行超过 1,000 次这样的扫描。
估计
实际
SQL Server 2014 引入了新的基数估计器。你的快速计划正在使用它。这在 XML 中显示为 CardinalityEstimationModelVersion="120"
您的缓慢计划不是 (CardinalityEstimationModelVersion="70"
)。
因此,在这种情况下,新估算器使用的假设似乎可以为您提供更好的计划。
差异的原因可能是因为速度快的是 运行 跨数据库(参考 [ProbeProduction].[dbo].[ValuationsTotal])并且您正在执行它的数据库可能具有兼容级别2014 年自动获得新的 CardinalityEstimator。
慢的一个在 ProbeProduction
本身的上下文中执行,我假设该数据库的兼容级别必须 < 2014 - 所以您默认使用遗留基数估计器。
您可以使用 OPTION (QUERYTRACEON 2312)
让慢速查询使用新的基数估计器(更改数据库兼容模式以全局改变行为不应该在没有仔细测试现有查询的情况下完成,因为它可能会导致回归和改进)。
或者,您可以尝试在旧 CE 的限制范围内调整查询。也许添加连接提示以鼓励它使用更类似于更快计划的东西。