具有巨大性能问题的不同***记录

Posted

技术标签:

【中文标题】具有巨大性能问题的不同***记录【英文标题】:Distinct Top Records having huge performance Issue 【发布时间】:2021-09-12 23:46:15 【问题描述】:

我有两个包含大量数据集的表,我正在尝试根据可选的搜索条件过滤数据。

Table 1 : Item
Table 2 : ScanCode

Item 可以有零个或 n 个 ScanCode,因此它们之间是一对 0 或多个关系,这就是我在 ScanCode 表上留下联接的原因,我正在尝试通过可选的 ScanCode 搜索让 Item 数据与 ScanCode 联接。

Declare @p_scanCode BIGINT = NULL, @p_limit INT = 500

SELECT TOP(@p_limit) i.ItemCode, i.StandardDescription,i.ItemType 
FROM Item i
LEFT OUTER JOIN ScanCode sc
ON sc.FK_ItemCode = i.ItemCode
WHERE ((@p_scanCode IS NULL) OR (@p_scanCode IS NOT NULL AND sc.ScanCode = @p_scanCode))
ORDER BY ItemCode

上面的查询工作得很好,但由于我们有多个 ScanCode 用于一个项目,我们将在结果集中有重复,所以我更改了查询以包括 distinct。

Declare @p_scanCode BIGINT = NULL, @p_limit INT = 500

SELECT DISTINCT TOP(@p_limit) i.ItemCode, i.StandardDescription,i.ItemType 
FROM Item i
LEFT OUTER JOIN ScanCode sc
ON sc.FK_ItemCode = i.ItemCode
WHERE ((@p_scanCode IS NULL) OR (@p_scanCode IS NOT NULL AND sc.ScanCode = @p_scanCode))
ORDER BY ItemCode

在查询上添加 distinct 后,现在需要 30 多秒才能获得结果,而以前需要不到 1 秒。 ScanCode 表很大,有 1200 万条数据。

我如何在没有任何性能问题的情况下根据提供的限制获得不同的***记录。

请提出建议。

【问题讨论】:

性能相关问题我们需要看执行计划。 缺少上下文,但听起来你有一个典型的kitchen sink search issue。而且使用没有 ORDER BY 子句的 TOP 通常是一个逻辑缺陷。 请通过brentozar.com/pastetheplan分享执行计划 @SM或者谢谢你,你给我的厨房水槽搜索帮助了我 【参考方案1】:

你只需要从Item输出项目吗? ScanCode 仅作为过滤器起作用?好像你需要exists。如果关系是 1:many 那么你不需要担心distinct

declare 
    @p_scanCode bigint = null, 
    @p_limit int = 500

select      
top (@p_limit)  i.ItemCode, i.StandardDescription,i.ItemType 
from            item i
where           @p_scanCode is null
or              exists (
                    select    0
                    from      scanCode sc
                    where     sc.FK_ItemCode = i.ItemCode
                    and       scanCode = @p_scanCode
                )

【讨论】:

我不能在 where 子句中使用存在,因为 scanCode 是可选的,我们可以有 0 或 n 个扫描码,所以我留下了可选的搜索条件加入 抱歉,我重新设计了过滤器以满足该需求。如果 p_scancode 为空,则输出所有记录,否则仅输出具有现有 p_scanCode 的记录。这是否符合您的需求? 感谢 pwilcox,但他们可以选择将 p_scanCode 作为过滤器传递,如果他们通过 p_scanCode 我只需要过滤匹配的扫描码项目,否则获得前 500 个项目也可以有零或 n 个扫描码,如果我放scanCode = @p_Scancode 在子查询中需要很长时间才能得到结果 我再次编辑以匹配您的最后一条语句:'如果我将 scanCode = @p_scancode 放在子查询中......'。我以为我做到了,但一定是删除太多了。抱歉,它让它变慢了。希望其他人有一个好的解决方案。 小心点。动态查询听起来像一个创可贴。从长远来看,我会继续寻找非动态解决方案。祝你好运!【参考方案2】:

使用条件连接和动态查询可以解决这个问题,因为问题是与 ScanCode 表连接时出现重复项,如果没有提供扫描码,我们可以进行条件连接,我们不加入,所以我们不会得到重复。如果提供了 ScanCode,那么每个扫描码将只有一个项目,在这种情况下我们不会得到重复。

此外,如果我们对 top(@p_limit) 记录使用动态限制,则在查询结束时需要 OPTION (RECOMPILE),这样它就不会使用具有先前限制的缓存执行计划,并且性能会下降。

    Declare @p_scanCode BIGINT = '12345', @p_limit INT = 500
    
    DECLARE @sql nvarchar(max) = N'
    SELECT TOP(@p_limit) i.ItemCode, i.StandardDescription 
    FROM Item i '
            
    + 
    CASE WHEN @p_scanCode IS NOT NULL THEN
    N' LEFT OUTER JOIN          SCANCODE AS SC
    ON  I.ItemCode = SC.FK_RetailItemCode
    AND GETDATE() BETWEEN SC.EffectiveDate AND SC.TerminationDate ' ELSE N'' END
    +

   'WHERE  1 = 1'
    
    + CASE WHEN @p_scanCode IS NOT NULL THEN
            N' AND sc.ScanCode = @p_scanCode' ELSE N'' END
    +

    ' ORDER BY  I.ItemCode asc OPTION (RECOMPILE)'
    
    print @sql

【讨论】:

以上是关于具有巨大性能问题的不同***记录的主要内容,如果未能解决你的问题,请参考以下文章

18 | 为什么这些SQL语句逻辑相同,性能却差异巨大?

18 | 为什么这些SQL语句逻辑相同,性能却差异巨大?

具有大量记录的表的分区视图和性能

在 Java 中解压缩巨大的 gz 文件和性能

两个相似查询的巨大速度差异(MySQL ORDER 子句)

由于网络导致的性能问题