使用'except'进行查询优化

Posted

技术标签:

【中文标题】使用\'except\'进行查询优化【英文标题】:Query Optimization using 'except'使用'except'进行查询优化 【发布时间】:2021-10-20 17:28:44 【问题描述】:

我最近一直在进行一些性能优化,但对下面的查询有点卡住了。分解一下,各个步骤似乎不需要很长时间,但是当我将查询作为一个整体运行时,大约需要 30 分钟才能完成。

TABLE 有大约 100k 行,而 VIEW 有大约 400k 行,所以它们不是特别大。我不确定我是否只是没有准确理解 EXCEPT 逻辑,这是否可能是罪魁祸首?除了 EXCEPT 之外,还有其他选择吗?

编辑 - 视图本身有大约 4 个连接和一个 UNION,所以它确实有一些逻辑。

CREATE TABLE [SCHEMA].[TABLE](
    ColumnA [int] IDENTITY(1,1) NOT NULL,
    ColumnB [tinyint] NOT NULL,
    ColumnC [tinyint] NOT NULL,
    ColumnD [int] NULL,
    ColumnE [nvarchar](50) NOT NULL,
    ColumnF [int] NOT NULL,
    ColumnG [nvarchar](250) NULL,
    ColumnH [nvarchar](250) NULL,
    ColumnI [nvarchar](250) NULL,
    ColumnJ [nvarchar](50) NULL,
    columnK [nvarchar](400) NULL,
    ColumnL [nvarchar](2) NULL,
    ColumnM [nvarchar](250) NULL,
    ColumnN [nvarchar](3) NULL,
----
       DELETE FROM [DB].[SCHEMA].[TABLE] WHERE ColumnB NOT IN (4,6) 
        AND  ColumnG not in
      (SELECT ColumnG 
       FROM 
       (
         SELECT ColumnG,ColumH,ColumnI FROM [DB].[SCHEMA].[TABLE] EXCEPT 

         SELECT ColumnG,ColumnH,ColumnI FROM [DB].[SCHEMA].[VIEW]
         WHERE VIEW.ColumnB='Active' and year(LastChgDateTime) = 9999
       ) AAA )

感谢您的帮助!

【问题讨论】:

year(LastChgDateTime) 不会高效,因为它不是 SARGable。使用LastChgDateTime >= '99990101 问题可能是视图中的基础查询。视图是否复杂? 抱歉,刚刚添加了关于视图的内容。它有大约 4 个连接和一个 UNION。不确定您是否会将其标记为复杂。 4 个连接和一个 UNION 肯定很复杂,您是否检查过您的 UNION 是否真的需要删除重复项?那是一种昂贵的 DISTINCT SORT,10 次中有 9 次根本不可能是骗子,他们应该使用 UNION ALL 而不必执行这些排序。 9999 的假日期/年份往往迟早会导致问题。 【参考方案1】:

不知道您的架构和索引,很难说。我们还没有看到查询计划。而且您还没有提供视图定义,所以我们不知道这涉及到什么。

但首先,您可以通过以下方式简化此查询

注意在LastChgDateTime 上使用 sarge-able 谓词

DELETE FROM [DB].[SCHEMA].[TABLE]
WHERE ColumnB NOT IN (4,6) 
  AND NOT EXISTS (
         SELECT ColumnG,ColumH,ColumnI
           FROM [DB].[SCHEMA].[TABLE] AAA
           WHERE AAA.ColumnG = [TABLE].ColumnG
         EXCEPT 
         SELECT ColumnG,ColumnH,ColumnI
           FROM [DB].[SCHEMA].[VIEW]
           WHERE [VIEW].ColumnB = 'Active' and LastChgDateTime >= '9999-01-01'
       );

对于上述情况,以下索引是有意义的

视图需要在基表上建立索引

[TABLE] (ColumnG, ColumH, ColumnI) INCLUDE (ColumnB)

[VIEW] (ColumnB, ColumnG, ColumH, ColumnI, LastChgDateTime)

我们可以通过使用带有窗口函数的可更新 CTE 来进一步优化。

我不完全确定您要实现的逻辑,但它似乎是这样的。

WITH cte AS (
    SELECT
      t.*,
      IsGNotInView = COUNT(v.IsNotInView) OVER (PARTITION BY t.ColumnG)
    FROM [DB].[SCHEMA].[TABLE] t
    CROSS APPLY (
       SELECT
        CASE WHEN NOT EXISTS (SELECT 1
            FROM [DB].[SCHEMA].[VIEW] v
            WHERE v.ColumnB = 'Active'
              AND v.LastChgDateTime >= '9999-01-01'
              AND v.ColumnG = t.ColumnG
              AND v.ColumnH = t.ColumnH
              AND t.ColumnI = v.ColumnI
            )
        THEN 1 END
    ) v(IsNotInView)
)
DELETE FROM cte
WHERE ColumnB NOT IN (4,6) 
  AND IsGNotInView = 0;

【讨论】:

以上是关于使用'except'进行查询优化的主要内容,如果未能解决你的问题,请参考以下文章

使用内部查询进行查询优化

mysql 学习 - 掌握单表查询优化

SQL优化----百万数据查询优化

使用 IN 子句和子查询进行极端查询优化

记一次数据库查询超时优化问题

mysql中怎样对大批量级的数据查询进行优化