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

Distinct Top Records having huge performance Issue

我有两个 table 具有庞大的数据集,我正在尝试根据可选的搜索条件过滤数据。

Table 1 : Item
Table 2 : ScanCode

项目可以有零个或 n 个 ScanCode,所以它们之间是一对 0 或多关系,这就是为什么我离开加入 ScanCode table,我正在尝试通过可选搜索获取项目数据与 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

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

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 多秒才能获得结果,而以前只需不到一秒。 ScanCode table 非常庞大,它有 1200 万条数据。

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

求推荐。

您只需要输出来自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
                )

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

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

    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