AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 316876
Accepted
SEarle1986
SEarle1986
Asked: 2022-09-15 03:22:40 +0800 CST2022-09-15 03:22:40 +0800 CST 2022-09-15 03:22:40 +0800 CST

查询存储计划强制失败,失败原因 NO_PLAN [重复]

  • 772
这个问题在这里已经有了答案:
查询存储计划强制失败,NO_PLAN 取决于过滤器运算符在计划中的位置 1 个答案
5 天前关闭。

我在查询存储中有一个查询,我已经为此制定了一个计划。

我可以确认该计划是被迫的

SELECT * FROM sys.query_store_plan WHERE is_forced_plan = 1

并且计划显示在结果中

但是,如果我查看该last_force_failure_reason_desc列,我会看到NO_PLAN

一些谷歌搜索把我带到了以下文章:

肯德拉小

Deepthi Goguri

这两者都表明更改计划使用的索引是导致NO_PLAN失败的原因。

我在第二篇文章中设置了扩展事件会话:

CREATE EVENT SESSION [Querystoreforcedplanfailures] ON SERVER 

ADD EVENT qds.query_store_plan_forcing_failed
ADD TARGET package0.event_file(SET filename=N'C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Backup\qserror.xel'),
ADD TARGET package0.ring_buffer
WITH (STARTUP_STATE=OFF)
GO

我可以看到相关计划的事件,其中包含以下文本:

查询处理器无法生成查询计划,因为 USE PLAN 提示包含无法验证为合法查询的计划。删除或替换 USE PLAN 提示。为获得成功执行计划的最大可能性,请验证 USE PLAN 提示中提供的计划是 SQL Server 为同一查询自动生成的计划

该查询作为数据仓库构建的一部分每晚运行,其中 DDL 命令很常见,因此我决定设置一个数据库审计规范来捕获SCHEMA_OBJECT_CHANGE_GROUP操作类型,以查看是否有任何索引被更改

USE [master]
GO

CREATE SERVER AUDIT [PlanForceAlters]
TO FILE 
(   FILEPATH = N'P:\Audit\'
    ,MAXSIZE = 0 MB
    ,MAX_ROLLOVER_FILES = 2147483647
    ,RESERVE_DISK_SPACE = OFF
) WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE, AUDIT_GUID = 'd2b6b090-395f-42f9-a8bb-c0ba742ce30e')
ALTER SERVER AUDIT [PlanForceAlters] WITH (STATE = ON)
GO


USE [MyDatabase]
GO

CREATE DATABASE AUDIT SPECIFICATION [PlanForceAlters]
FOR SERVER AUDIT [PlanForceAlters]
ADD (SCHEMA_OBJECT_CHANGE_GROUP)
WITH (STATE = ON)
GO

当我如下询问结果时:

SELECT  o.name,
        a.statement
FROM    sys.fn_get_audit_file ('P:\Audit\PlanForce*',default,default) a
        JOIN sys.objects o
            ON o.object_id = a.object_id
WHERE   o.name IN ('MyTableA','MyTable')

我可以看到 IN 子句中表的所有更改(哪些表是从查询中选择的表,我试图强制执行谁的计划)

我所能看到的只是外键删除和重新创建,这对于我们的数据仓库来说是相当正常的。外键使用与删除时相同的名称重新创建。事件顺序是

  1. 存在约束(不可信)
  2. 约束被删除
  3. 查询运行
  4. 重新创建约束(同名和 NOCHECK)

强制执行的计划是查询处理器在上面第 3 点生成的计划,因此事件的顺序每晚都相同,我会认为约束的变化是无关紧要的吗?

我更进一步,计算出被删除/重新创建的外键在哪些列上:

SELECT  o.name,
        a.statement,
        c.name
FROM    sys.fn_get_audit_file ('P:\Audit\PlanForce*',default,default) a
        JOIN sys.objects o
            ON o.object_id = a.object_id
        LEFT JOIN sys.foreign_keys fk
            ON  statement LIKE '%ADD CONSTRAINT%' + fk.name + '%' OR
                statement LIKE '%DROP CONSTRAINT%' + fk.name + '%' OR
                statement LIKE '%ADD  CONSTRAINT%' + fk.name + '%' OR
                statement LIKE '%DROP  CONSTRAINT%' + fk.name + '%'
        LEFT JOIN sys.foreign_key_columns fkc
            ON fk.object_id = fkc.constraint_object_id
        LEFT JOIN sys.all_columns c
            ON c.column_id = fkc.parent_column_id AND
                c.object_id = fkc.parent_object_id
WHERE   o.name IN ('MyTableA','MyTableB')

并且这些都不是 query_plan 中使用的任何非聚集索引中的列

我试图在 AdventureWorks2016 数据库上重新创建一个示例,在该示例中我强制执行一个计划,该计划对具有可信外键的列执行 NCI 搜索,然后观察优化器仍然使用该计划,尽管外键被删除并且在重新创建不受信任时仍然使用:

/* create our stored proc */
CREATE OR ALTER PROCEDURE sp_SalesbyProduct
    @ProductID INT
AS
SELECT 
  SalesOrderID, 
  OrderQty,
  UnitPrice
FROM Sales.SalesOrderDetail
WHERE ProductID = @ProductID
GO

/* create an index to support the query */
CREATE INDEX IX_SalesOrderDetail_ProductID ON Sales.SalesOrderDetail
(
    ProductId
) 
WITH 
(
    DROP_EXISTING = ON
)

/* add a trusted foreign key on the column IX_SalesOrderDetail_ProductID is on */
ALTER TABLE Sales.SalesOrderDetail ADD CONSTRAINT FK_MyKey FOREIGN KEY (ProductID) REFERENCES Production.Product(ProductId)

/* run the proc and ensure differing plans */
DBCC FREEPROCCACHE
GO
EXEC sp_SalesbyProduct @ProductID = 710 /* seek on IX_SalesOrderDetail_ProductID with key lookup */
GO
DBCC FREEPROCCACHE
GO
EXEC sp_SalesbyProduct @ProductID = 870 /* CI Scan*/
GO

在此处输入图像描述

/* force the seek / lookup plan */
EXEC sp_query_store_force_plan 222, 224;

/* verify the plan is forced */
SELECT  *
FROM    sys.query_store_plan
WHERE   is_forced_plan = 1

在此处输入图像描述

/* run the queries again and ensure both use the seek / lookup plan */

DBCC FREEPROCCACHE
GO
EXEC sp_SalesbyProduct @ProductID = 710
GO
DBCC FREEPROCCACHE
GO
EXEC sp_SalesbyProduct @ProductID = 870
GO

在此处输入图像描述

/* drop the constraint on the column in the index */
ALTER TABLE Sales.SalesOrderDetail DROP CONSTRAINT FK_MyKey 

/* is the plan still forced? */

DBCC FREEPROCCACHE
GO
EXEC sp_SalesbyProduct @ProductID = 710
GO
DBCC FREEPROCCACHE
GO
EXEC sp_SalesbyProduct @ProductID = 870
GO

在此处输入图像描述

是的!

/* re-add the FK but make it untrusted */
ALTER TABLE Sales.SalesOrderDetail WITH NOCHECK ADD CONSTRAINT FK_MyKey FOREIGN KEY (ProductID) REFERENCES Production.Product(ProductId)

/* is the plan still forced? */

DBCC FREEPROCCACHE
GO
EXEC sp_SalesbyProduct @ProductID = 710
GO
DBCC FREEPROCCACHE
GO
EXEC sp_SalesbyProduct @ProductID = 870
GO

在此处输入图像描述

是的!

是什么导致了NO_PLAN错误?这与删除/创建外键约束有关吗?

sql-server sql-server-2016
  • 2 2 个回答
  • 91 Views

2 个回答

  • Voted
  1. Best Answer
    Paul White
    2022-09-15T07:01:37+08:002022-09-15T07:01:37+08:00

    删除和重新创建完全相同的外键约束不会阻止 QDS 计划强制。

    确切地说,那里的词包括 中的is_not_trusted状态sys.foreign_keys。如果约束不受信任,则查询优化器不会基于外键关系应用简化。

    尝试使用不可信外键强制基于可信外键的计划可能会产生NO_PLAN失败原因。

    同样,如果应用简化来更改计划形状,则在外键不受信任时尝试强制生成计划可能会在外键受信任时失败。

    在您的情况下这应该不太可能,因为您说您删除并重新创建外键,并且WITH CHECK是新约束的默认设置。不过,这是您应该验证的。

    也有可能您正在使用创建外键NOCHECK,然后将其更改为CHECK状态。除非您也指定,否则这不会使约束受信任WITH CHECK。

    强调一点:当受信任的外键约束启用的简化改变了优化器考虑的计划空间时,就会出现这个问题。

    AdventureWorks示例:

    -- Force the plan for this query
    SELECT COUNT_BIG(*) 
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID;
    
    -- Set the FK to not trusted
    ALTER TABLE Production.TransactionHistory
        NOCHECK CONSTRAINT FK_TransactionHistory_Product_ProductID;
    
    -- NO_PLAN
    SELECT COUNT_BIG(*) 
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID;
    
    -- Make it trusted again
    ALTER TABLE Production.TransactionHistory
        WITH CHECK
            CHECK CONSTRAINT FK_TransactionHistory_Product_ProductID;
    
    -- Plan forced successfully
    SELECT COUNT_BIG(*) 
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID;
    

    使用受信任的外键,计划是:

    可信外键计划

    当约束不受信任时:

    不受信任的外键计划

    除此之外,您应该首先验证 QDS 中的计划是否可以强制执行此查询。一项测试是USE HINT手动使用计划 xml。这不是 100% 准确的测试,因为这两种机制完全不同,但它可能会有所帮助。

    Not all plans stored in QDS are capable of being forced. For complex queries, the optimizer may not be able to find the desired shape, even with the guide. In theory, this should result in a TIME_OUT forcing failure reason, but it doesn't always. You should verify that the plan is ever successfully forced before looking further for reasons it failed.

    • 2
  2. SEarle1986
    2022-09-23T07:25:47+08:002022-09-23T07:25:47+08:00

    The problem turned out not to be anything to do with constraints but is something to do with the query itself.

    A cut down, anonymized version of the query is below:

    SELECT  1
            -- some text so I can pick it out of query_store_sql_text more easily
            FROM    Object1 WITH (NOLOCK)
            INNER JOIN Object2 Object2 WITH (NOLOCK) ON ? = Object2.Column1
                AND ? = Object2.Column2
                AND Object1.Column3 = Object2.Column4
            INNER JOIN Object3 Object3 WITH (NOLOCK) ON Object1.Column5 = Object3.Column5
                AND Object1.Column6 >= Object3.Column7
                AND Object3.Column8 IS NULL
            INNER JOIN Object4 Object5 WITH (NOLOCK) ON ? = Object5.Column9
            LEFT JOIN Object4 WITH (NOLOCK) ON Object4.Column9 = ?
            LEFT JOIN Object6 ON Object6.Column10 = CAST(Object5.Column4 + ? AS VARCHAR(255))
                AND Object6.Column11 = Object1.Column12
                AND Object6.Column13 = Object3.Column13
                AND Object6.Column5 = Object3.Column5
                AND Object6.Column14 = ?
    WHERE   (Object4.Column4 = ? OR Object4.Column4 IS NULL)
    

    the plan is here

    I

    • Run the query
    • get the query_id and plan_id from the query store dmvs
    • force the query to the plan
    • run the query again
    • the plan shows as NO_PLAN in sys.query_store_plan.last_force_failure_reason_desc

    我已经设法将其追踪到 WHERE 子句(where 子句在实际查询中要大得多,但是注释掉一个位会导致这个谓词成为问题)

    我仍然无法解释为什么,但我现在在我的环境中有一个可重现的例子。我只需要看看我是否可以在 AdventureWorks 中复制它。

    我将 Paul Whites 的答案标记为公认的答案,因为原始的约束理论有问题,这是正确的

    • 0

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve