Rob*_*Sun 1 sql sql-server nested-loops database-performance sql-server-2012
SQL Server中有3个数据量很大的表,每个表包含大约100000行。有一个 SQL 从三个表中获取行。它的性能非常糟糕。
WITH t1 AS
(
SELECT
LeadId, dbo.get_item_id(Log) AS ItemId, DateCreated AS PriceDate
FROM
(SELECT
t.ID, t.LeadID, t.Log, t.DateCreated, f.AskingPrice
FROM
t
JOIN
f ON f.PKID = t.LeadID
WHERE
t.Log LIKE '%xxx%') temp
)
SELECT COUNT(1)
FROM t1
JOIN s ON s.ItemID = t1.ItemId
Run Code Online (Sandbox Code Playgroud)
在检查其估计执行计划时,我发现它使用了大行的嵌套循环连接。抢劫看下面的截图。图像中的顶部部分返回 124277 行,底部部分执行了 124277 次!我想这就是它这么慢的原因。
我们知道嵌套循环在处理大数据时存在很大的性能问题。如何删除它,并使用散列连接或其他连接代替?
编辑:以下是相关功能。
CREATE FUNCTION [dbo].[get_item_Id](@message VARCHAR(200))
RETURNS VARCHAR(200) AS
BEGIN
DECLARE @result VARCHAR(200),
@index int
--Sold in eBay (372827580038).
SELECT @index = PatIndex('%([0-9]%)%', @message)
IF(@index = 0)
SELECT @result='';
ELSE
SELECT @result= REPLACE(REPLACE(REPLACE(SUBSTRING(@message, PatIndex('%([0-9]%)%', @message),8000), '.', ''),'(',''),')','')
-- Return the result of the function
RETURN @result
END;
Run Code Online (Sandbox Code Playgroud)
由于某种原因,它决定s cross join t1先评估函数(结果别名为 Expr1002),然后对 [s].[ItemID]=[Expr1002] 进行过滤(而不是进行等值连接)。
它估计它将有88,969和124,277行进入交叉连接(这意味着它将产生11,056,800,413)
在交叉连接后执行标量 UDF 大约 110 亿次,然后向下过滤大约 110 亿行,这看起来确实很疯狂。如果在连接之前对其进行评估,则评估次数会少得多,并且也将是等连接,因此也可以使用HASH或MERGE内部连接,并且只需读取所有表一次,而不会增加行计数。
我在本地重现了这一点,并且在创建 UDF 时行为发生了变化WITH SCHEMABINDING- SQL Server 将看到它不访问任何表,并且它的定义是确定性的。
跟踪标志8606输出似乎支持这个问题。在这两种情况下,“简化树”阶段将查询表示为与 ScalarUdf 上的谓词的交叉连接。标量 UDF 被注释为“IsDet”或“IsNonDet”,具体取决于函数是否受架构绑定。在前一种情况下,“项目规范化”阶段将计算推回连接之前,并为其提供在连接本身中引用的别名,在非确定性情况下,这种情况不会发生。
我强烈建议摆脱这个标量函数并将其替换为内联版本,尽管除此之外非内联标量函数还有许多众所周知的附加性能问题。
新函数将是
CREATE FUNCTION get_item_Id_inline (@message VARCHAR(200))
RETURNS TABLE
AS
RETURN
(SELECT item_Id = CASE
WHEN PatIndex('%([0-9]%)%', @message) = 0 THEN ''
ELSE REPLACE(REPLACE(REPLACE(SUBSTRING(@message, PatIndex('%([0-9]%)%', @message), 8000), '.', ''), '(', ''), ')', '')
END)
Run Code Online (Sandbox Code Playgroud)
并重写查询
WITH t1
AS (SELECT t.LeadID,
i.item_Id AS ItemId,
t.DateCreated AS PriceDate
FROM t
CROSS apply dbo.get_item_Id_inline(t.Log) i
JOIN f
ON f.PKID = t.LeadID
WHERE t.Log LIKE '%xxx%')
SELECT COUNT(1)
FROM t1
JOIN s
ON s.ItemID = t1.ItemId
Run Code Online (Sandbox Code Playgroud)
可能仍然有一些额外优化的空间,但这将比您当前的执行计划好几个数量级(因为这是灾难性的糟糕)。
| 归档时间: |
|
| 查看次数: |
4639 次 |
| 最近记录: |