兼容级别 100 SQL SERVER 2016 中的 OPENJSON
OPENJSON in compatibility level 100 SQL SERVER 2016
我需要在兼容级别为 100 的旧数据库中使用 OPENJSON()
的功能。服务器运行 SQL SERVER 2016。所以我想出了这个主意:创建另一个数据库 "GeneralUTILS" (lvl 130) 在同一台服务器中并从 lvl 100 DB 调用此函数:
CREATE FUNCTION [dbo].[OPENJSON_](@json NVARCHAR(MAX))
RETURNS @Results TABLE ([Key] nVARCHAR (4000) , [Value] NVARCHAR(MAX), [Type] INT)
AS
BEGIN
INSERT INTO @Results
SELECT * from OPENJSON(@json)
RETURN
END
但是我没有 WITH 子句来修改 lvl 100 数据库中的输出 table。
最重要的问题可能是为什么你需要这个...
希望我没看错,你需要什么:
(提示:这至少需要 SQL-Server 2016)
--创建两个模型-数据库
CREATE DATABASE dbOld;
GO
ALTER DATABASE dbOld SET COMPATIBILITY_LEVEL = 100; --v2008
GO
CREATE DATABASE dbForJsonIssues;
GO
ALTER DATABASE dbForJsonIssues SET COMPATIBILITY_LEVEL = 130; --v2016
GO
--现在我们将在"higher"数据库中创建一个存储过程
USE dbForJsonIssues;
GO
--Attention: replacing FROM is a very hacky way... Read the hints at the end...
--You might use parameters for the JSON-string and the JSON-path, but then you must use sp_executesql
CREATE PROCEDURE EXEC_Json_Command @Statement NVARCHAR(MAX), @TargetTable NVARCHAR(MAX)
AS
BEGIN
DECLARE @statementWithTarget NVARCHAR(MAX)=REPLACE(@Statement,'FROM',CONCAT(' INTO ',@TargetTable,' FROM'));
PRINT @statementWithTarget; --you can out-comment this line...
EXEC(@statementWithTarget);
END
GO
--现在我们进入"lower"数据库
USE dbOld;
GO
--A synonym is not necessary, but allows for easier code
CREATE SYNONYM dbo.ExecJson FOR dbForJsonIssues.dbo.EXEC_Json_Command;
GO
--这是使用方法
DECLARE @json NVARCHAR(MAX)=N'[{"someObject":[{"attr1":"11", "attr2":"12"},{"attr1":"21", "attr2":"22"}]}]';
DECLARE @Statement NVARCHAR(MAX)=CONCAT(N'SELECT * FROM OPENJSON(N''',@json,N''',''$[0].someObject'') WITH(attr1 INT,attr2 INT)');
--the target table will be created "on the fly"
--You can use ##SomeTarget too, but be careful with concurrencies in both approaches...
EXEC ExecJson @Statement=@Statement,@TargetTable='dbOld.dbo.SomeTarget';
SELECT * FROM SomeTarget;
--We can drop this table after dealing with the result
DROP TABLE SomeTarget;
GO
--清理(注意真实数据!)
USE master;
GO
DROP DATABASE dbOld;
DROP DATABASE dbForJsonIssues;
最重要的概念:
我们不能直接在数据库中使用 JSON 语句,但我们可以在字符串基础上创建一个语句,将其传递给存储过程并使用 EXEC()
来执行它。
使用SELECT * INTO SomeDb.SomeSchema.SomeTargetTable FROM ...
将创建具有合适结构的table。确保使用数据库中不存在的 table。
并不是真的需要将目标 table 作为参数传递,您可以自己将其放在语句中。替换存储过程中的 FROM
是一种非常精明的方法,如果在其他地方发现 from
可能会导致麻烦。
您可能会针对各种需要使用类似的过程...
是的。这绝不会通过我们办公室的烟幕。不管怎样,有人让我做类似的事情,但用例仅用于解析 json 数组。由于 Json_Query 和 Json_Value 可用,我把它拼凑在一起只是为了给他们一些可以使用的东西。我的同事喜欢这个结果。改完后发现他比我帅多了
Declare @Fields NVarchar(2000) = 'Name,Coolness'
Declare @Delimiter As Varchar(10) = ',';
Declare @Xml As Xml = Cast(('<V>' + Replace(@Fields, @delimiter, '</V><V>') + '</V>' ) As Xml);
Declare @Json Nvarchar(4000) = N'{"Examples":[{"Name": "Chris","Coolness": "10"},{"Name": "Jay","Coolness": "1"}]}';
Exec ('Begin Try Drop Table #JsonTemp End Try Begin Catch End Catch');
Create Table #JsonTemp (JsonNode Nvarchar(1000));
Declare @Max INTEGER = 100;
Declare @Index INTEGER = 0;
While @Index < @Max
Begin
Declare @Affected Integer = 0;
Declare @Select Nvarchar(200) = '''' + 'lax$.Examples[' + Convert(Nvarchar, @Index) + ']' + '''';
Declare @Statement Nvarchar(2000)= 'Select Json_Query(' + '''' + @Json + '''' + ', ' + @Select + ') Where Json_Query(' + '''' + @Json + '''' + ', ' + @Select + ') Is Not Null';
Insert Into #JsonTemp (JsonNode) Exec sp_executesql @Statement;
Set @Affected = @@RowCount;
If (@Affected = 0) Begin Break End
Set @Index = @Index + 1;
End
Declare @Table Table(Field NVarchar(200));
Declare @Selector NVarchar(500) = 'Json_Value(' + '''' + '{"Node":' + '''' + ' + ' + 'JsonNode' + ' + ' + '''' + '}' + '''' + ', ' + '''' + '$.Node.@Field' + '''' + ')';
Insert Into @Table(Field)
Select N.value('.', 'Varchar(10)') As Field
From @XML.nodes('V') As A(N);
Declare @Selectors Varchar(8000);
Select @Selectors = Coalesce(@Selectors + ', ', '') + Replace(@Selector, '@Field', Field) + ' As ' + Field
From @Table
Exec ('Select ' + @Selectors + ' From #JsonTemp');
我需要在兼容级别为 100 的旧数据库中使用 OPENJSON()
的功能。服务器运行 SQL SERVER 2016。所以我想出了这个主意:创建另一个数据库 "GeneralUTILS" (lvl 130) 在同一台服务器中并从 lvl 100 DB 调用此函数:
CREATE FUNCTION [dbo].[OPENJSON_](@json NVARCHAR(MAX))
RETURNS @Results TABLE ([Key] nVARCHAR (4000) , [Value] NVARCHAR(MAX), [Type] INT)
AS
BEGIN
INSERT INTO @Results
SELECT * from OPENJSON(@json)
RETURN
END
但是我没有 WITH 子句来修改 lvl 100 数据库中的输出 table。
最重要的问题可能是为什么你需要这个...
希望我没看错,你需要什么:
(提示:这至少需要 SQL-Server 2016)
--创建两个模型-数据库
CREATE DATABASE dbOld;
GO
ALTER DATABASE dbOld SET COMPATIBILITY_LEVEL = 100; --v2008
GO
CREATE DATABASE dbForJsonIssues;
GO
ALTER DATABASE dbForJsonIssues SET COMPATIBILITY_LEVEL = 130; --v2016
GO
--现在我们将在"higher"数据库中创建一个存储过程
USE dbForJsonIssues;
GO
--Attention: replacing FROM is a very hacky way... Read the hints at the end...
--You might use parameters for the JSON-string and the JSON-path, but then you must use sp_executesql
CREATE PROCEDURE EXEC_Json_Command @Statement NVARCHAR(MAX), @TargetTable NVARCHAR(MAX)
AS
BEGIN
DECLARE @statementWithTarget NVARCHAR(MAX)=REPLACE(@Statement,'FROM',CONCAT(' INTO ',@TargetTable,' FROM'));
PRINT @statementWithTarget; --you can out-comment this line...
EXEC(@statementWithTarget);
END
GO
--现在我们进入"lower"数据库
USE dbOld;
GO
--A synonym is not necessary, but allows for easier code
CREATE SYNONYM dbo.ExecJson FOR dbForJsonIssues.dbo.EXEC_Json_Command;
GO
--这是使用方法
DECLARE @json NVARCHAR(MAX)=N'[{"someObject":[{"attr1":"11", "attr2":"12"},{"attr1":"21", "attr2":"22"}]}]';
DECLARE @Statement NVARCHAR(MAX)=CONCAT(N'SELECT * FROM OPENJSON(N''',@json,N''',''$[0].someObject'') WITH(attr1 INT,attr2 INT)');
--the target table will be created "on the fly"
--You can use ##SomeTarget too, but be careful with concurrencies in both approaches...
EXEC ExecJson @Statement=@Statement,@TargetTable='dbOld.dbo.SomeTarget';
SELECT * FROM SomeTarget;
--We can drop this table after dealing with the result
DROP TABLE SomeTarget;
GO
--清理(注意真实数据!)
USE master;
GO
DROP DATABASE dbOld;
DROP DATABASE dbForJsonIssues;
最重要的概念:
我们不能直接在数据库中使用 JSON 语句,但我们可以在字符串基础上创建一个语句,将其传递给存储过程并使用 EXEC()
来执行它。
使用SELECT * INTO SomeDb.SomeSchema.SomeTargetTable FROM ...
将创建具有合适结构的table。确保使用数据库中不存在的 table。
并不是真的需要将目标 table 作为参数传递,您可以自己将其放在语句中。替换存储过程中的 FROM
是一种非常精明的方法,如果在其他地方发现 from
可能会导致麻烦。
您可能会针对各种需要使用类似的过程...
是的。这绝不会通过我们办公室的烟幕。不管怎样,有人让我做类似的事情,但用例仅用于解析 json 数组。由于 Json_Query 和 Json_Value 可用,我把它拼凑在一起只是为了给他们一些可以使用的东西。我的同事喜欢这个结果。改完后发现他比我帅多了
Declare @Fields NVarchar(2000) = 'Name,Coolness'
Declare @Delimiter As Varchar(10) = ',';
Declare @Xml As Xml = Cast(('<V>' + Replace(@Fields, @delimiter, '</V><V>') + '</V>' ) As Xml);
Declare @Json Nvarchar(4000) = N'{"Examples":[{"Name": "Chris","Coolness": "10"},{"Name": "Jay","Coolness": "1"}]}';
Exec ('Begin Try Drop Table #JsonTemp End Try Begin Catch End Catch');
Create Table #JsonTemp (JsonNode Nvarchar(1000));
Declare @Max INTEGER = 100;
Declare @Index INTEGER = 0;
While @Index < @Max
Begin
Declare @Affected Integer = 0;
Declare @Select Nvarchar(200) = '''' + 'lax$.Examples[' + Convert(Nvarchar, @Index) + ']' + '''';
Declare @Statement Nvarchar(2000)= 'Select Json_Query(' + '''' + @Json + '''' + ', ' + @Select + ') Where Json_Query(' + '''' + @Json + '''' + ', ' + @Select + ') Is Not Null';
Insert Into #JsonTemp (JsonNode) Exec sp_executesql @Statement;
Set @Affected = @@RowCount;
If (@Affected = 0) Begin Break End
Set @Index = @Index + 1;
End
Declare @Table Table(Field NVarchar(200));
Declare @Selector NVarchar(500) = 'Json_Value(' + '''' + '{"Node":' + '''' + ' + ' + 'JsonNode' + ' + ' + '''' + '}' + '''' + ', ' + '''' + '$.Node.@Field' + '''' + ')';
Insert Into @Table(Field)
Select N.value('.', 'Varchar(10)') As Field
From @XML.nodes('V') As A(N);
Declare @Selectors Varchar(8000);
Select @Selectors = Coalesce(@Selectors + ', ', '') + Replace(@Selector, '@Field', Field) + ' As ' + Field
From @Table
Exec ('Select ' + @Selectors + ' From #JsonTemp');