web-dev-qa-db-ja.com

SQLの再起動後、CTEによりSQL Server 2019で短期間無効なオブジェクト名エラーが発生する

SQL Server 2016データベースには、しばらくの間本番環境で使用されているSQLコードがあります。しかし、SQL Serverの再起動後、最初の1時間程度(おそらくSQL Serverのアクティビティのレベルに応じて5〜10分から1時間以上)、SQL Server 2019データベースでエラーが発生します。エラーは、CTE(Common Table Expression)の「無効なオブジェクト名」です。

SQL Server 2016にいくつかのデータベースがある実稼働環境があります。これで、SQL Server 2019を使用して新しい開発/テスト環境をセットアップしました(Windows Server 2016マシンで、24 GBのRAMおよびSQL Server 2019でテストできるように、4つのCPUコア)。このテストサーバー上のデータベースは、運用バックアップから運用データベースの復元されたコピーです。テスト環境のすべてのデータベースの互換性レベルは150に設定されています(SQL Server 2019)。 。

毎朝早く、CTEを使用するいくつかの関数でいくつかの問題が発生し始めました。関数は次のようなエラーを発生させます。

_SqlException (0x80131904): Invalid object name 'CTEuniqueName'.]

Msg 208, Level 16, State 1, Procedure ufn_FunctionName, Line 28 [Batch Start Line 0]
Invalid object name 'CTEuniqueName'.
_

エラーは短時間後に発生しなくなり、翌朝まで再び発生しませんでした。

エラーは、次々に呼び出されたストアード・プロシージャーのペアで発生していました。どちらも同じ(ユーザー定義SQL)関数を呼び出しました。いくつかのテストで、関数を呼び出してから、関数からコードのブロックを実行するだけで同じエラーが発生する場合があることを学びました。

また、SQL Serverインスタンスを再起動し、関数またはコードブロックを呼び出すことで、常にエラーが発生する可能性があることも発見しました。これがおそらく、早朝にのみ失敗した理由でもあります。その前の数時間はSQL Serverインスタンスでアクティビティがなかったため、アイドルモードになったか、プロセスをシャットダウンしていました。

関数またはコードを繰り返し呼び出した後、ある時点でエラーが発生せずに成功し、その後、SQL Serverインスタンスを再起動するまで機能し続けているように見えました。

この関数を含むデータベースを「SQL Server 2016」互換モードに変更すると、SQL Serverインスタンスを再起動した直後でも、関数は常に成功します。したがって、SQL Server 2019に固有の問題のようです。

関数のコードは次のようになり(名前は匿名化されています)、関数には次のような2つの独立した "IF"ブロックが含まれています。ただし、次のコードブロックだけでエラーが発生する可能性があります。このコードの2つのビューは、同じSQL Serverインスタンス内の他のデータベースへのビューです。

_DECLARE @MyID INT = 150589;
DECLARE @MyType VARCHAR(25) = 'Test';
DECLARE @ExpirationDate DATE;
BEGIN
 IF @MyType = 'Test'
     BEGIN
         DECLARE @Test1 INT;
         WITH CTEuniqueName(PersonID, DateComplete)
              AS (SELECT T1.PersonID, MAX(T2.DateComplete) AS DateComplete
                  FROM dbo.TABLE1 AS T1 WITH(NOLOCK)
                       LEFT JOIN dbo.VIEW2 AS T2 WITH(NOLOCK) ON T2.ID = T1.ID
                  WHERE T1.ID = @MyID
                        AND T2.SecondID IN(SELECT SecondID
                                        FROM dbo.TABLE3 WITH(NOLOCK)
                                        WHERE Name = 'TEST')
                  GROUP BY T1.PersonID)
              SELECT @ExpirationDate = CASE
                                           WHEN V3.TimeFrame > 0 THEN DATEADD(DAY, TimeFrame, CONVERT(DATE, CTE1.DateComplete))
                                           ELSE NULL
                                       END
              FROM CTEuniqueName AS CTE1
                   INNER JOIN dbo.VIEW2 AS V2 WITH(NOLOCK) ON V2.ID = CTE1.ID
                                                               AND V2.DateComplete = CTE1.DateComplete
                   INNER JOIN dbo.VIEW3 AS V3 WITH(NOLOCK) ON V3.QuizID = V2.QuizID
              WHERE TS.SecondID IN(SELECT SecondID
                                 FROM dbo.TABLE3 WITH(NOLOCK)
                                 WHERE Name = 'TEST');
     END;
END;
_

また、起動後にエラーが発生している期間に、2つのストアドプロシージャ(上記の関数を呼び出し、両方のプロシージャが結果セットを返す)をSSMSウィンドウで実行すると、プロシージャはそれぞれの結果セットを正常に返しますSSMSウィンドウで、次にSSMSがメッセージパネルに切り替わり、「無効なオブジェクト名」エラーが表示されます。

これがSQL Server 2019でのみ断続的に失敗する理由を知っている人はいますか(SQL Serverの起動後またはアイドル時間の後のようです)。または、このような問題のトラブルシューティング方法を誰かが提案できますか? SQLエラーログを確認してみました。また、関数呼び出しを含めてプロファイラーを実行してみました-これらのいずれも手掛かりを提供していません。

追加情報:

  • DBCC FREESYSTEMCACHE(N'ALL');を実行すると、同じエラーが一時的に再び発生します(サーバーの再起動と同じ方法で)
  • 関数は(CTEにより)インライン化できないため、スカラーUDFインライン化が問題の原因であるとは思われません
  • これが発生しても、SQL Serverエラーログにエラーはありません
  • 同じインスタンスで新しく作成されたデータベースを使用して問題を再現しようとしましたが、関数に必要なテーブルのみが含まれていますが、関数は常に新しいデータベースで正常に完了します
7
PhilipD

最初は、インライン化を無効にすることで問題が解決したと述べたため、これはSQL Server 2019で追加された新しい Scalar UDF Inlining 機能に関連するバグのようです。詳細な検査では、関数定義にCTEが存在するため、関数をインライン化できません。

これが問題を再現するための私の(失敗した)試みです:

USE [master];
GO
DROP DATABASE IF EXISTS [256861OtherDatabase];
GO
CREATE DATABASE [256861OtherDatabase];
GO
USE [256861OtherDatabase];
GO

CREATE TABLE dbo.TableForView2
(
    ID int IDENTITY(1,1) NOT NULL,
    DateComplete datetime NOT NULL,
    SecondID int NOT NULL,
    QuizID int NOT NULL,
    CONSTRAINT PK_TableForView2 PRIMARY KEY (Id)
);
GO

CREATE TABLE dbo.TableForView3
(
    ID int IDENTITY(1,1) NOT NULL,
    Timeframe int NOT NULL,
    QuizID int NOT NULL,
    CONSTRAINT PK_TableForView3 PRIMARY KEY (Id)
);
GO

USE [master];
GO
DROP DATABASE IF EXISTS [256861];
GO
CREATE DATABASE [256861];
GO
USE [256861];
GO

CREATE TABLE dbo.TABLE1
(
    ID int IDENTITY(1,1) NOT NULL,
    PersonID int NOT NULL,
    CONSTRAINT PK_TABLE1 PRIMARY KEY (Id)
);
GO

CREATE VIEW dbo.View2
AS
SELECT
    ID,
    DateComplete,
    SecondID,
    QuizID
FROM [256861OtherDatabase].dbo.TableForView2 d
GO

CREATE TABLE dbo.TABLE3
(
    ID int IDENTITY(1,1) NOT NULL,
    SecondID int NOT NULL,
    [Name] varchar(50) NOT NULL,
    CONSTRAINT PK_TABLE3 PRIMARY KEY (Id)
);
GO

CREATE VIEW dbo.View3
AS
SELECT
    ID,
    Timeframe,
    QuizID
FROM [256861OtherDatabase].dbo.TableForView3 d
GO

CREATE FUNCTION dbo.TestFunction 
(
    @MyID INT = 150589, 
    @MyType VARCHAR(25) = 'Test'
)
RETURNS date
AS
BEGIN;
DECLARE @ExpirationDate DATE;
 IF @MyType = 'Test'
     BEGIN
         DECLARE @Test1 INT;
         WITH CTEuniqueName(ID, DateComplete)
              AS (SELECT T1.PersonID, MAX(T2.DateComplete) AS DateComplete
                  FROM dbo.TABLE1 AS T1 WITH(NOLOCK)
                       LEFT JOIN dbo.VIEW2 AS T2 WITH(NOLOCK) ON T2.ID = T1.ID
                  WHERE T1.ID = @MyID
                        AND T2.SecondID IN(SELECT SecondID
                                        FROM dbo.TABLE3 WITH(NOLOCK)
                                        WHERE Name = 'TEST')
                  GROUP BY T1.PersonID)
              SELECT @ExpirationDate = CASE
                                           WHEN V3.TimeFrame > 0 THEN DATEADD(DAY, TimeFrame, CONVERT(DATE, CTE1.DateComplete))
                                           ELSE NULL
                                       END
              FROM CTEuniqueName AS CTE1
                   INNER JOIN dbo.VIEW2 AS V2 WITH(NOLOCK) ON V2.ID = CTE1.ID
                                                               AND V2.DateComplete = CTE1.DateComplete
                   INNER JOIN dbo.VIEW3 AS V3 WITH(NOLOCK) ON V3.QuizID = V2.QuizID
              WHERE V2.SecondID IN(SELECT SecondID
                                 FROM dbo.TABLE3 WITH(NOLOCK)
                                 WHERE Name = 'TEST');
     END;
     RETURN @ExpirationDate;
END;
GO

関数がis_inlineable = 0sys.sql_modulesとしてマークされていることに気付いたので、次の拡張イベントセッションを実行して関数作成コードを再度実行しました。

CREATE EVENT SESSION [inlining] ON SERVER 
ADD EVENT sqlserver.tsql_scalar_udf_not_inlineable
ADD TARGET package0.event_file(SET filename=N'inlining')
WITH (STARTUP_STATE=OFF)
GO

これは、blocked_reasonが "CTE"の1つのイベントを生成しました。これは インライン化可能なスカラーUDFの要件 (この質問が最初に投稿された後にCTEを含むように更新されました)と一致します。

これはまだ私にはバグのある行動のようです。私が実際に持っている唯一の提案は、CTEを回避するために関数を書き直すことです。これは問題を回避する可能性があり、関数をインライン化することもできます。

3
Josh Darnell

同じ問題がありました。クエリが初めて(またはプランを初めて実行)失敗します。クエリを変更すると、このエラーが再び発生します。

私の解決策は、編集して( ";"の追加などのマイナーなコード変更)、スカラー関数を再コンパイルすることでした。再コンパイル後、エラーは発生しませんでした。

0
Hugo de Vreugd