【问题标题】:Estimated number of rows is way off in execution plan执行计划中的估计行数有偏差
【发布时间】:2020-11-05 22:37:38
【问题描述】:

我遇到的情况是执行计划中的估计行数有偏差

我在联接中的列是varchar(50)。我尝试了不同的索引,但它并没有减少这个问题。我什至尝试过使用临时表上的索引。我还能做什么?

PS 这是估计数字开始漂移的第一个地方......而且表格并不大(48000 行)。

代码是:

SELECT DISTINCT householdnumber, householdid, primaryCustomerID
INTO #Households
FROM TableA

    
SELECT 
A.*,
MIN(B.[ProfileCreatedDate]) PROFILECREATEDDATE 
INTO #Profile
from #Households AS a 
LEFT JOIN TableA AS B
    
ON A.[HouseholdNumber]= B.[HouseholdNumber] and A.[HouseholdId]=B.[HouseholdId]
GROUP BY a.householdnumber, a.householdid, a.primaryCustomerID;

我知道这似乎可以重写为:

SELECT householdnumber, householdid, primaryCustomerID, MIN([ProfileCreatedDate]) AS PROFILECREATEDDATE 
INTO #Profile2
from TableA
GROUP BY householdnumber, householdid, primaryCustomerID;

但结果并不相同,我不想更改结果,因为我不确定这段代码的创建者是否知道他们在做什么。

关于列的一些统计信息: householdnumber 始终等于 householdidhouseholdidnvarchar(50)householdnumbervarchar(40)。该表有 48877 行。 householdnumber, householdid, primaryCustomerID 的不同组合有 48029 行。 primaryCustomerID 的不同数量是 47152。

【问题讨论】:

  • 如果不了解正在发生的一切,我不确定这是否有用,但是:您知道您可以将UPDATE STATISTICS #Households 作为脚本的一部分运行吗?或者,可能更好的是,将OPTION (RECOMPILE) 添加到您的查询中(在GROUP BY 行之后)。
  • @EdmCoff:谢谢,我会试试UPDATE STATISTICS。实际上我曾尝试重新编译,但这也无济于事。我让你知道它是否有效。但是表是刚刚创建的?我必须阅读有关统计数据的更多信息...
  • @EdmCoff:更新统计数据没有帮助......
  • 请参阅paste the plan,了解在您的问题中包含执行计划的方法。
  • @HABO:我很愿意,但我有点偏执地发布敏感内容......

标签: sql sql-server tsql query-performance


【解决方案1】:

关于代码 - 似乎较大(原始)版本和您更简单的 GROUP BY 版本之间的区别在于,原始版本为该家庭中的任何人找到了最小值 profilecreateddate,而您的更简单的版本为特定的primarycustomerid 找到profilecreateddate

例如(使用更简单的数据)

CREATE TABLE #TableA (householdnumber int, householdid int, primaryCustomerID int, ProfileCreatedDate datetime);
INSERT INTO #TableA (householdnumber, householdid, primaryCustomerID, ProfileCreatedDate) VALUES
(1, 1, 1, '20201001'),
(1, 1, 1, '20201002'),
(1, 1, 2, '20201003');

SELECT DISTINCT householdnumber, householdid, primaryCustomerID
INTO #Households
FROM #TableA;

SELECT 
    A.*,
    MIN(B.[ProfileCreatedDate]) PROFILECREATEDDATE 
INTO #Profile
from #Households AS a 
    LEFT JOIN #TableA AS B  
      ON A.[HouseholdNumber]= B.[HouseholdNumber] and A.[HouseholdId]=B.[HouseholdId]
GROUP BY a.householdnumber, a.householdid, a.primaryCustomerID;

SELECT * FROM #Profile;
/* -- Results
householdnumber  householdid  primaryCustomerID  PROFILECREATEDDATE
1                1            1                  2020-10-01 00:00:00.000
1                1            2                  2020-10-01 00:00:00.000
*/

SELECT householdnumber, householdid, primaryCustomerID, MIN([ProfileCreatedDate]) AS PROFILECREATEDDATE 
INTO #Profile2
from #TableA
GROUP BY householdnumber, householdid, primaryCustomerID;

SELECT * FROM #Profile2;
/* -- Results
householdnumber  householdid  primaryCustomerID  PROFILECREATEDDATE
1                1            1                  2020-10-01 00:00:00.000
1                1            2                  2020-10-03 00:00:00.000
*/

如果您在上面注意到,第 2 行的 PROFILECREATEDATE 是不同的。

因此,您可以尝试以下代码,它应该会给出与原始集相同的结果 - 看看它在一段时间内的表现如何(并确认它与原始结果匹配)。

SELECT DISTINCT t1.householdnumber, t1.householdid, primaryCustomerID, 
        MIN([ProfileCreatedDate]) OVER (PARTITION BY t1.householdnumber, t1.householdid) AS PROFILECREATEDDATE 
INTO #Profile3
FROM #TableA t1;

SELECT * FROM #Profile3;
/* -- Results
householdnumber  householdid  primaryCustomerID  PROFILECREATEDDATE
1                1            1                  2020-10-01 00:00:00.000
1                1            2                  2020-10-01 00:00:00.000
*/

【讨论】:

  • 你是对的。谢谢!结果与使用(SELECT * FROM #Profile EXCEPT SELECT * FROM #Profile2) UNION all (SELECT * FROM #Profile2 EXCEPT SELECT * FROM #Profile) 相同
  • 现在超级快。有时需要 10 分钟,现在它在 2 秒内完成。但是为什么到目前为止的估计值在原始代码中?
  • +1 用于添加窗口函数选项,这应该可以解决 OP 的原始问题,还有助于了解如何利用 SQL Server 的优势
  • 关于估计 - 通常很难弄清楚发生了什么,但请记住,估计仅基于少量的 元数据,而无需实际查看数据。通常分组会严重抛出估计,因为只需要猜测有多少出来(例如,如果分组,让我们猜测结果将是大小的 20%)。我不是 100% 知道(不看整个执行计划)为什么它这么远。我在那里的解决方案 a) 最小化读取(只读取表一次),然后只在最后进行分组以最小化导致错误成倍增加的分组。
  • 好的,谢谢。实际上窗口函数方法也使代码的含义更加清晰,而原始代码则难以理解。无论如何,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-06
  • 2021-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-31
相关资源
最近更新 更多