SQL Server stored procedure evaluating JSON_VALUE out of execution order

时间:2018-12-03 13:10:20

标签: sql-server tsql stored-procedures

In the below procedure, I am trying to return a value based on an input that is in JSON format. To protect against an invalid JSON input, I am using the ISJSON() function. However, SQL Server appears to be evaluating JSON_VALUE() in the 2nd statement prior to the ISJSON() validation check in the 1st statement.

CREATE TABLE dbo.Table1(
    ColKey integer NOT NULL,
    ColText nvarchar(255) NOT NULL,
    CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED ([ColKey] ASC));
GO

INSERT INTO dbo.Table1(ColKey, ColText)
VALUES (1, 'Test String');
GO

CREATE PROCEDURE [dbo].[usp_GetTextFromJSON]
    @JSON nvarchar(255),
    @TextOut nvarchar(255) OUTPUT
AS
BEGIN       
    IF ISJSON(@JSON) <> 1
        THROW 50000, 'Invalid JSON', 1; -- Alternatively: RETURN -1;

    SELECT TOP 1 @TextOut = ColText
    FROM dbo.Table1
    WHERE ColKey = JSON_VALUE(@JSON, N'$.Col1Key')

    RETURN 0;   
END;
GO

Executing the procedure with an invalid JSON string does not trigger the expected exception.

DECLARE @return_value int,
        @TextOut nvarchar(255);

EXEC    @return_value = [dbo].[usp_GetTextFromJSON]
        @JSON = N'{Col1Key:1}', -- Invalid JSON: ISJSON(N'{Col1Key:1}') = 0 
        @TextOut = @TextOut OUTPUT;

SELECT @TextOut;

returns:

Msg 13609, Level 16, State 1, Procedure dbo.usp_GetTextFromJSON, Line 10 [Batch Start Line 29]
JSON text is not properly formatted. Unexpected character 'C' is found at position 1.

Changing the execution code to the following returns successfully.

DECLARE @return_value int,
        @TextOut nvarchar(255);

EXEC    @return_value = [dbo].[usp_GetTextFromJSON]
        @JSON = N'{"Col1Key":1}', -- Valid JSON
        @TextOut = @TextOut OUTPUT;

SELECT @TextOut;

Why is SQL Server evaluating JSON_VALUE() in the 2nd statement before evaluating ISJSON in the 1st statement?

1 个答案:

答案 0 :(得分:2)

It''s a compilation issue not an execution issue. When you run the good one first the plan is generated and cached and the bad one then works as you want.

Probably it tries to evaluate JSON_VALUE(@JSON, N'$.Col1Key') against the initial parameter value so it can then use the histogram on ColKey to get row estimates.

You can try assigning the parameter to a local variable to avoid this.

CREATE PROCEDURE [dbo].[usp_GetTextFromJSON] @JSON    NVARCHAR(255),
                                             @TextOut NVARCHAR(255) OUTPUT
AS
  BEGIN
      DECLARE @vJSON NVARCHAR(255) = @JSON;

      IF ISJSON(@vJSON) <> 1
        THROW 50000, 'Invalid JSON', 1; -- Alternatively: RETURN -1;

      SELECT TOP 1 @TextOut = ColText
      FROM   dbo.Table1
      WHERE  ColKey = JSON_VALUE(@vJSON, N'$.Col1Key')

      RETURN 0;
  END;