You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多列不等式关联的SQL Server查询优化方案问询

问题背景与需求

我拥有一张名为#TempRule的表,包含多组范围最小值与最大值对;另有一张#TestSet表,存储大量单值记录,需判断这些单值是否落在Rule表对应行的所有范围内(可类比人员属性与规则匹配场景)。Rule表行数可达数十万至千万级,TestSet表可达数亿级,可分块处理。

需求:

  • 快速判断TestSet每行是否被至少一条Rule记录的所有范围覆盖(即标记'被覆盖'状态);
  • 可选统计每行被覆盖的规则数量;
  • 寻求SQL Server环境下的查询或算法优化方案,包括如何实现多不等式列关联时仅对每张表做一次扫描。
当前问题

现有查询处理40万条TestSet记录与630万条Rule记录耗时约23分钟,执行计划显示存在表扫描,核心慢查询部分为嵌套循环左反半连接中的表扫描操作。

测试代码与执行计划
--TEST DATA SET UP 'RULES'
CREATE TABLE #TempRule (
    AMin INT,
    AMax INT,
    BMin INT,
    BMax INT,
    CMin INT,
    CMax INT,
    DMin INT,
    DMax INT,
    EMin INT,
    EMax INT,
    FMin INT,
    FMax INT
);


INSERT INTO #TempRule (AMin, AMax, BMin, BMax, CMin, CMax, DMin, DMax, EMin, EMax, FMin, FMax)
SELECT
    AMin,
    CASE WHEN AMin > AMax THEN AMin ELSE AMax END AS AMax,
    BMin,
    CASE WHEN BMin > BMax THEN BMin ELSE BMax END AS BMax,
    CMin,
    CASE WHEN CMin > CMax THEN CMin ELSE CMax END AS CMax,
    DMin,
    CASE WHEN DMin > DMax THEN DMin ELSE DMax END AS DMax,
    EMin,
    CASE WHEN EMin > EMax THEN EMin ELSE EMax END AS EMax,
    FMin,
    CASE WHEN FMin > FMax THEN FMin ELSE FMax END AS FMax
FROM (
    SELECT
        FLOOR(RAND(CHECKSUM(NEWID())) * 100000) + 1 AS AMin,
        FLOOR(RAND(CHECKSUM(NEWID())) * 100000) + 1 AS AMax,
        (FLOOR(RAND(CHECKSUM(NEWID())) * 31) * 10) + 1690 AS BMin,
        (FLOOR(RAND(CHECKSUM(NEWID())) * 31) * 10) + 1690 AS BMax,
        FLOOR(RAND(CHECKSUM(NEWID())) * 51) * 1000 AS CMin,
        FLOOR(RAND(CHECKSUM(NEWID())) * 51) * 1000 AS CMax,
        FLOOR(RAND(CHECKSUM(NEWID())) * 3) + 1 AS DMin,
        FLOOR(RAND(CHECKSUM(NEWID())) * 3) + 1 AS DMax,
        FLOOR(RAND(CHECKSUM(NEWID())) * 5) + 1 AS EMin,
        FLOOR(RAND(CHECKSUM(NEWID())) * 5) + 1 AS EMax,
        FLOOR(RAND(CHECKSUM(NEWID())) * 4) + 1 AS FMin,
        FLOOR(RAND(CHECKSUM(NEWID())) * 4) + 1 AS FMax
    FROM
        (SELECT TOP 400000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM sys.objects AS o1 CROSS JOIN sys.objects AS o2 CROSS JOIN sys.objects AS o3) AS Numbers
) AS RandomValues;


-- Create an index that covers every column
CREATE CLUSTERED INDEX idx_TempRules_AllColumns ON #TempRule (AMin, AMax, BMin, BMax, CMin, CMax, DMin, DMax, EMin, EMax, FMin, FMax);


--TEST DATA SET UP 'Set of Values'
CREATE TABLE #TestSet (
    A INT,
    B INT,    
    C INT,
    D INT,
    E INT,       
    F INT
);

INSERT INTO #TestSet (A, B, C, D, E, F)
SELECT
    FLOOR(RAND(CHECKSUM(NEWID())) * 100000) + 1 AS A,
    (FLOOR(RAND(CHECKSUM(NEWID())) * 31) * 10) + 1690 AS B,
    FLOOR(RAND(CHECKSUM(NEWID())) * 51) * 1000 AS C,
    FLOOR(RAND(CHECKSUM(NEWID())) * 3) + 1 AS D,
    FLOOR(RAND(CHECKSUM(NEWID())) * 5) + 1 AS E,
    FLOOR(RAND(CHECKSUM(NEWID())) * 4) + 1 AS F
FROM
    (SELECT TOP 15000000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM master.dbo.spt_values AS a CROSS JOIN master.dbo.spt_values AS b) AS Numbers;

SELECT COUNT(*) FROM #TestSet
CREATE CLUSTERED INDEX idx_TestSet ON #TestSet (A, B, C, D, E, F);

CREATE TABLE #TestSetChunk (
    A INT,
    B INT,    
    C INT,
    D INT,
    E INT,       
    F INT
);
CREATE CLUSTERED INDEX idx_TestSetChunk ON #TestSetChunk (A, B, C, D, E, F);



-- Create the results temp table
CREATE TABLE #Results (
    A INT,
    B INT,
    C INT,
    D INT,
    E INT,
    F INT
);



-- Variables to control the loop
DECLARE @BatchSize INT = 1000000;
DECLARE @Offset INT = 0;
DECLARE @TotalRows INT;
DECLARE @ProcessedRows INT = 0;

-- Get the total number of rows in #TestSet
SELECT @TotalRows = COUNT(*) FROM #TestSet;

-- Loop to process 1 million records at a time
WHILE @Offset < @TotalRows
BEGIN
    
    DELETE FROM #TestSetChunk;  
    INSERT INTO #TestSetChunk (A, B, C, D, E, F)
    SELECT A, B, C, D, E, F
    FROM (
        SELECT A, B, C, D, E, F, 
               ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RowNum
        FROM #TestSet
    ) AS n
    WHERE n.RowNum BETWEEN @Offset + 1 AND @Offset + @BatchSize;
    
    
    ---THIS IS SLOW running part that I am trying to optimize 
    INSERT INTO #Results WITH (TABLOCK) (A, B, C, D, E, F)
    SELECT n.A, n.B, n.C, n.D, n.E, n.F
    FROM #TestSetChunk n WITH (INDEX(idx_TestSetChunk)) 
    WHERE NOT EXISTS (
        SELECT 1
        FROM #TempRule t WITH (INDEX(idx_TempRules_AllColumns)) 
        WHERE n.A >= t.AMin AND n.A <= t.AMax
          AND n.B >= t.BMin AND n.B <= t.BMax
          AND n.C >= t.CMin AND n.C <= t.CMax
          AND n.D >= t.DMin AND n.D <= t.DMax
          AND n.E >= t.EMin AND n.E <= t.EMax
          AND n.F >= t.FMin AND n.F <= t.FMax
    )
    ORDER BY n.A;
   

    -- Increment the offset
    SET @Offset = @Offset + @BatchSize;
    SET @ProcessedRows = @ProcessedRows + @BatchSize;

    -- Print progress
    PRINT 'Processed ' + CAST(@ProcessedRows AS VARCHAR) + ' rows out of ' + CAST(@TotalRows AS VARCHAR);
    

   END



-- Select results (Need to do more.  Just a simplification)
SELECT count(*) FROM #Results;

执行计划:

|--Table Insert(OBJECT:([tempdb].[dbo].[#Results]), SET:([tempdb].[dbo].[#Results].[A] = [tempdb].[dbo].[#TestSetChunk].[A] as [n].[A],[tempdb].[dbo].[#Results].[B] = [tempdb].[dbo].[#TestSetChunk].[B] as [n].[B],[tempdb].[dbo].[#Results].[C] = [tempdb].[dbo].[#TestSetChunk].[C] as [n].[C],[tempdb].[dbo].[#Results].[D] = [tempdb].[dbo].[#TestSetChunk].[D] as [n].[D],[tempdb].[dbo].[#Results].[E] = [tempdb].[dbo].[#TestSetChunk].[E] as [n].[E],[tempdb].[dbo].[#Results].[F] = [tempdb].[dbo].[#TestSetChunk].[F] as [n].[F]))
            |--Nested Loops(Left Anti Semi Join, WHERE:([tempdb].[dbo].[#TestSetChunk].[A] as [n].[A]>=[tempdb].[dbo].[#TempRule].[AMin] as [t].[AMin] AND [tempdb].[dbo].[#TestSetChunk].[A] as [n].[A]<=[tempdb].[dbo].[#TempRule].[AMax] as [t].[AMax] AND [tempdb].[dbo].[#TestSetChunk].[B] as [n].[B]>=[tempdb].[dbo].[#TempRule].[BMin] as [t].[BMin] AND [tempdb].[dbo].[#TestSetChunk].[B] as [n].[B]<=[tempdb].[dbo].[#TempRule].[BMax] as [t].[BMax] AND [tempdb].[dbo].[#TestSetChunk].[C] as [n].[C]>=[tempdb].[dbo].[#TempRule].[CMin] as [t].[CMin] AND [tempdb].[dbo].[#TestSetChunk].[C] as [n].[C]<=[tempdb].[dbo].[#TempRule].[CMax] as [t].[CMax] AND [tempdb].[dbo].[#TestSetChunk].[D] as [n].[D]>=[tempdb].[dbo].[#TempRule].[DMin] as [t].[DMin] AND [tempdb].[dbo].[#TestSetChunk].[D] as [n].[D]<=[tempdb].[dbo].[#TempRule].[DMax] as [t].[DMax] AND [tempdb].[dbo].[#TestSetChunk].[E] as [n].[E]>=[tempdb].[dbo].[#TempRule].[EMin] as [t].[EMin] AND [tempdb].[dbo].[#TestSetChunk].[E] as [n].[E]<=[tempdb].[dbo].[#TempRule].[EMax] as [t].[EMax] AND [tempdb].[dbo].[#TestSetChunk].[F] as [n].[F]>=[tempdb].[dbo].[#TempRule].[FMin] as [t].[FMin] AND [tempdb].[dbo].[#TestSetChunk].[F] as [n].[F]<=[tempdb].[dbo].[#TempRule].[FMax] as [t].[FMax]))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#TestSetChunk] AS [n]))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#TempRule] AS [t]))

内容的提问来源于stack exchange,提问作者BSchroeder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 13:47:32