如何优化大型数据集的 SQL 查询

问题描述 投票:0回答:1

我有以下 SQL 查询,它完全满足我的需要。

WITH Subquery AS 
(
    SELECT 
        t1.ID
    FROM 
        [SEO].[dbo].[tModelPOEF] t1
    RIGHT JOIN 
        [SEO].[dbo].[tHistory] t2 ON ((t1.N1 = t2.N1 
                                       OR t1.N2 = t2.N1 
                                       OR t1.N3 = t2.N1 
                                       OR t1.N4 = t2.N1 
                                       OR t1.N5 = t2.N1 
                                       OR t1.N6 = t2.N1))
    WHERE 
        t1.N1 IS NOT NULL
)
UPDATE [SEO].[dbo].[tModelPOEF]
SET Filter = CASE 
                 WHEN Subquery.ID IS NOT NULL 
                     THEN 1 
                     ELSE 0 
             END
FROM [SEO].[dbo].[tModelPOEF]
LEFT JOIN Subquery ON tModelPOEF.ID = Subquery.ID

当两个数据集较小时(即 tModelPOEF < 10 million rows and tHistory 4000 rows).

),查询足够快

但是,在某些情况下,

tModelPOEF
表可以容纳超过 5000 万行。当发生这种情况时,查询执行时会变得非常慢。

我想知道是否有一种方法可以优化查询以获得更快的性能

sql sql-server
1个回答
0
投票

连接条件中的

OR
条件是性能杀手,导致在嵌套循环中重复表扫描以查找所有匹配项。

修复方法是重构查询,以便它可以对要匹配的 6 列中的每一列执行单独的索引查找。通常,这可以通过 UNION 来组合各个子查询的结果来完成,可能会包含在

OUTER APPLY
或 CTE 中。

UPDATE m
SET Filter = ISNULL(h.Filter, 0)
FROM dbo.tModelPOEF m
OUTER APPLY (
    SELECT 1 AS Filter FROM dbo.tHistory h WHERE h.N1 = m.N1
    UNION
    SELECT 1 AS Filter FROM dbo.tHistory h WHERE h.N1 = m.N2
    UNION
    SELECT 1 AS Filter FROM dbo.tHistory h WHERE h.N1 = m.N3
    UNION
    SELECT 1 AS Filter FROM dbo.tHistory h WHERE h.N1 = m.N4
    UNION
    SELECT 1 AS Filter FROM dbo.tHistory h WHERE h.N1 = m.N5
    UNION
    SELECT 1 AS Filter FROM dbo.tHistory h WHERE h.N1 = m.N6
) h

但是,由于我们只是检查是否存在,因此也可以通过一系列在 case 表达式中

EXISTS()
组合在一起的
OR
测试来完成同样的任务。

UPDATE m
SET Filter = CASE
   WHEN EXISTS (SELECT * FROM dbo.tHistory h WHERE h.N1 = m.N1)
   OR EXISTS (SELECT * FROM dbo.tHistory h WHERE h.N1 = m.N2)
   OR EXISTS (SELECT * FROM dbo.tHistory h WHERE h.N1 = m.N3)
   OR EXISTS (SELECT * FROM dbo.tHistory h WHERE h.N1 = m.N4)
   OR EXISTS (SELECT * FROM dbo.tHistory h WHERE h.N1 = m.N5)
   OR EXISTS (SELECT * FROM dbo.tHistory h WHERE h.N1 = m.N6)
   THEN 1 ELSE 0 END
FROM dbo.tModelPOEF m

以上两者都将执行六个独立的索引查找,而不是表扫描来检查匹配项。但是,要使其发挥作用,您需要有一个适当的索引来支持这些索引查找。

CREATE INDEX IX_tHistory_N1 ON tHistory(N1)

请参阅 this db<>fiddle 的演示,该演示显示了原始查询的执行计划以及针对一些伪造的测试数据的上述每个计划。

© www.soinside.com 2019 - 2024. All rights reserved.