【问题标题】: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或者谢谢你,你给我的厨房水槽搜索帮助了我

标签: sql sql-server tsql sql-server-2012


【解决方案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

【讨论】:

    猜你喜欢
    • 2017-05-07
    • 2015-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    • 2015-01-07
    • 2013-05-06
    • 2018-05-10
    相关资源
    最近更新 更多