gpt4 book ai didi

sql - 加入时 CTE 非常慢

转载 作者:行者123 更新时间:2023-12-04 01:37:59 25 4
gpt4 key购买 nike

我之前发布过类似的内容,但我现在从不同的方向接近这个问题,所以我提出了一个新问题。我希望这没问题。

我一直在与 CTE 合作,该 CTE 会根据父收费创建一笔收费。可以在此处查看 SQL 和详细信息:

CTE Index recommendations on multiple keyed table

我不认为我在 CTE 上遗漏了任何东西,但是当我将它与大数据表(350 万行)一起使用时,我遇到了问题。

tblChargeShare包含一些我需要的其他信息,例如 InvoiceID ,所以我将我的 CTE 放在 View 中 vwChargeShareSubCharges并把它放到 table 上。

查询:

Select t.* from vwChargeShareSubCharges t
inner join
tblChargeShare s
on t.CustomerID = s.CustomerID
and t.MasterChargeID = s.ChargeID
Where s.ChargeID = 1291094

在几毫秒内返回结果。

查询:
Select ChargeID from tblChargeShare Where InvoiceID = 1045854

返回 1 行:
1291094

但是查询:
Select t.* from vwChargeShareSubCharges t
inner join
tblChargeShare s
on t.CustomerID = s.CustomerID
and t.MasterChargeID = s.ChargeID
Where InvoiceID = 1045854

运行需要2-3分钟。

我保存了执行计划并将它们加载到 SQL Sentry 中。快速查询的树看起来像这样:

Fast Query

慢查询的计划是:

Slow Query

我尝试重新索引,通过调优顾问和各种子查询组合运行查询。每当联接包含 PK 以外的任何内容时,查询就会很慢。

我在这里有一个类似的问题:

SQL Server Query time out depending on Where Clause

其中使用函数来对子行而不是 CTE 进行汇总。这是使用 CTE 重写以尝试避免我现在遇到的相同问题。我已经阅读了该答案中的回复,但我并不聪明 - 我阅读了一些有关提示和参数的信息,但我无法使其工作。我原以为使用 CTE 重写会解决我的问题。在具有几千行的 tblCharge 上运行时,查询速度很快。

在 SQL 2008 R2 和 SQL 2012 中测试

编辑:

我已将查询压缩为单个语句,但同样的问题仍然存在:
WITH RCTE AS
(
SELECT ParentChargeId, s.ChargeID, 1 AS Lvl, ISNULL(TotalAmount, 0) as TotalAmount, ISNULL(s.TaxAmount, 0) as TaxAmount,
ISNULL(s.DiscountAmount, 0) as DiscountAmount, s.CustomerID, c.ChargeID as MasterChargeID
from tblCharge c inner join tblChargeShare s
on c.ChargeID = s.ChargeID Where s.ChargeShareStatusID < 3 and ParentChargeID is NULL

UNION ALL

SELECT c.ParentChargeID, c.ChargeID, Lvl+1 AS Lvl, ISNULL(s.TotalAmount, 0), ISNULL(s.TaxAmount, 0), ISNULL(s.DiscountAmount, 0) , s.CustomerID
, rc.MasterChargeID
from tblCharge c inner join tblChargeShare s
on c.ChargeID = s.ChargeID
INNER JOIN RCTE rc ON c.PArentChargeID = rc.ChargeID and s.CustomerID = rc.CustomerID Where s.ChargeShareStatusID < 3
)

Select MasterChargeID as ChargeID, rcte.CustomerID, Sum(rcte.TotalAmount) as TotalCharged, Sum(rcte.TaxAmount) as TotalTax, Sum(rcte.DiscountAmount) as TotalDiscount
from RCTE inner join tblChargeShare s on rcte.ChargeID = s.ChargeID and RCTE.CustomerID = s.CustomerID
Where InvoiceID = 1045854
Group by MasterChargeID, rcte.CustomerID
GO

编辑:
玩多了,我只是不明白这一点。

此查询是即时的(2 毫秒):
Select t.* from
vwChargeShareSubCharges t
Where t.MasterChargeID = 1291094

而这需要 3 分钟:
DECLARE @ChargeID int = 1291094

Select t.* from
vwChargeShareSubCharges t
Where t.MasterChargeID = @ChargeID

即使我将大量数字放入“In”中,查询仍然是即时的:
Where  t.MasterChargeID in (1291090, 1291091, 1291092, 1291093,  1291094, 1291095, 1291096, 1291097, 1291098, 1291099, 129109)

编辑2:

我可以使用以下示例数据从头开始复制它:

我创建了一些虚拟数据来复制这个问题。它不是那么重要,因为我只添加了 100,000 行,但错误的执行计划仍然发生(在 SQLCMD 模式下运行):
CREATE TABLE [tblChargeTest](
[ChargeID] [int] IDENTITY(1,1) NOT NULL,
[ParentChargeID] [int] NULL,
[TotalAmount] [money] NULL,
[TaxAmount] [money] NULL,
[DiscountAmount] [money] NULL,
[InvoiceID] [int] NULL,
CONSTRAINT [PK_tblChargeTest] PRIMARY KEY CLUSTERED
(
[ChargeID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
GO

Insert into tblChargeTest
(discountAmount, TotalAmount, TaxAmount)
Select ABS(CHECKSUM(NewId())) % 10, ABS(CHECKSUM(NewId())) % 100, ABS(CHECKSUM(NewId())) % 10
GO 100000

Update tblChargeTest
Set ParentChargeID = (ABS(CHECKSUM(NewId())) % 60000) + 20000
Where ChargeID = (ABS(CHECKSUM(NewId())) % 20000)
GO 5000

CREATE VIEW [vwChargeShareSubCharges] AS
WITH RCTE AS
(
SELECT ParentChargeId, ChargeID, 1 AS Lvl, ISNULL(TotalAmount, 0) as TotalAmount, ISNULL(TaxAmount, 0) as TaxAmount,
ISNULL(DiscountAmount, 0) as DiscountAmount, ChargeID as MasterChargeID
FROM tblChargeTest Where ParentChargeID is NULL

UNION ALL

SELECT rh.ParentChargeID, rh.ChargeID, Lvl+1 AS Lvl, ISNULL(rh.TotalAmount, 0), ISNULL(rh.TaxAmount, 0), ISNULL(rh.DiscountAmount, 0)
, rc.MasterChargeID
FROM tblChargeTest rh
INNER JOIN RCTE rc ON rh.PArentChargeID = rc.ChargeID --and rh.CustomerID = rc.CustomerID
)

Select MasterChargeID, ParentChargeID, ChargeID, TotalAmount, TaxAmount, DiscountAmount , Lvl
FROM RCTE r
GO

然后运行这两个查询:
--Slow Query:
Declare @ChargeID int = 60900

Select *
from [vwChargeShareSubCharges]
Where MasterChargeID = @ChargeID

--Fast Query:
Select *
from [vwChargeShareSubCharges]
Where MasterChargeID = 60900

最佳答案

在这里,SQL Server 可以为您做的最好的事情是将过滤器推送到 ChargeID向下进入 View 内递归 CTE 的 anchor 部分。这允许查找您需要从中构建层次结构的唯一行。当您将参数作为常量值提供时,SQL Server 可以进行优化(使用名为 SelOnIterator 的规则,对于那些对此类事情感兴趣的人):

Pushed predicate with a constant value

当你使用局部变量时它不能这样做,所以 ChargeID 上的谓词卡在 View 之外(从所有 NULL id 开始构建完整的层次结构):

Stuck Predicate

使用变量时获得最佳计划的一种方法是强制优化器在每次执行时编译新计划。然后在执行时根据变量中的特定值定制生成的计划。这是通过添加 OPTION (RECOMPILE) 来实现的。查询提示:

Declare @ChargeID int = 60900;

-- Produces a fast execution plan, at the cost of a compile on every execution
Select *
from [vwChargeShareSubCharges]
Where MasterChargeID = @ChargeID
OPTION (RECOMPILE);

第二种选择是将 View 更改为内联表函数。这允许您明确指定过滤谓词的位置:
CREATE FUNCTION [dbo].[udfChargeShareSubCharges]
(
@ChargeID int
)
RETURNS TABLE AS RETURN
(
WITH RCTE AS
(
SELECT ParentChargeID, ChargeID, 1 AS Lvl, ISNULL(TotalAmount, 0) as TotalAmount, ISNULL(TaxAmount, 0) as TaxAmount,
ISNULL(DiscountAmount, 0) as DiscountAmount, ChargeID as MasterChargeID
FROM tblChargeTest
Where ParentChargeID is NULL
AND ChargeID = @ChargeID -- Filter placed here explicitly

UNION ALL

SELECT rh.ParentChargeID, rh.ChargeID, Lvl+1 AS Lvl, ISNULL(rh.TotalAmount, 0), ISNULL(rh.TaxAmount, 0), ISNULL(rh.DiscountAmount, 0)
, rc.MasterChargeID
FROM tblChargeTest rh
INNER JOIN RCTE rc ON rh.ParentChargeID = rc.ChargeID --and rh.CustomerID = rc.CustomerID
)

Select MasterChargeID, ParentChargeID, ChargeID, TotalAmount, TaxAmount, DiscountAmount , Lvl
FROM RCTE r
)

像这样使用它:
Declare @ChargeID int = 60900

select *
from dbo.udfChargeShareSubCharges(@ChargeID)

查询还可以从 ParentChargeID 上的索引中受益。 .
create index ix_ParentChargeID on tblChargeTest(ParentChargeID)

这是关于类似场景中类似优化规则的另一个答案。
Optimizing Execution Plans for Parameterized T-SQL Queries Containing Window Functions

关于sql - 加入时 CTE 非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22807213/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com