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?
答案 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;