您所拥有的存在多个问题。我将首先介绍我的每个提示,从提示 1 开始:
不要使用EXEC ({SQL Variable}) 语法,你不能将你的参数传递给动态语句。使用sys.sp_executesql。
这个和锡上写的差不多。您遇到的第一个问题是此错误:
必须声明表变量“@json”。
您收到此错误的原因是@json 在动态语句中没有上下文;变量仅在其定义的语句内具有范围;它不能被内部或外部范围访问。那么如何在动态语句中使用该变量?好吧,您将它作为参数传递给所述动态语句。
但是,有一个问题,您使用了EXEC (@stm),这意味着不可能将@json 作为参数传递。因此,您需要使用 sys.sp_executesql,在编写动态语句时应该始终使用它:
EXEC sys.sp_executesql @stm, N'@json nvarchar(MAX)', @json;
如果你确实使用它,你会得到另一个错误:
过程需要“ntext/nchar/nvarchar”类型的参数“@statement”
这是因为您还错误地将@stm 定义为varchar(MAX);它必须是nvarchar(MAX)。如果你修复它,你会得到一个新的错误:
'$.' 附近的语法不正确。
提示 2:
调试动态 SQL 的最简单方法是 PRINT/SELECT 您的语句,对其进行调试,然后将解决方案传播到创建动态语句的 SQL。如果您查看PRINT,您会发现单引号存在明显错误。
我的意思是字面意思。那么让我们看看你的陈述:
SELECT [ProductId],[ProductName],[ProductDescription],[DatePurchased] INTO #TestData FROM OPENJSON((SELECT * FROM @json),'$.records') WITH ([ProductId] varchar(max) '$.ProductId,[ProductName] varchar(max) '$.ProductName,[ProductDescription] varchar(max) '$.ProductDescription,[DatePurchased] varchar(max) '$.DatePurchased)
嗯,这很难看……让我们现在跳到技巧 3,然后回到技巧 2:
在编写动态 SQL 时,格式化您创建的语句也很重要。众所周知,长的单行代码很难调试。
如您所见,前面的 SQL 语句在阅读上非常不同。我们真的希望它是可读的,像这样:
SELECT [ProductId],
[ProductName],
[ProductDescription],
[DatePurchased]
INTO #TestData
FROM OPENJSON((SELECT *
FROM @json),'$.records')
WITH ([ProductId] varchar(max) '$.ProductId,
[ProductName] varchar(max) '$.ProductName,
[ProductDescription] varchar(max) '$.ProductDescription,
[DatePurchased] varchar(max) '$.DatePurchased)
现在,上面的问题已经很清楚了,但是让我们首先完整地解决技巧 3,并让您的语句在编写时具有良好的格式。因此,我还将把你古怪的SET ~{Variable} = @{Variable} FROM dataset 语法装箱;它是无证的,不应该被信任工作。我将改为使用字符串聚合。我假设您在这里使用的是最新版本的 SQL Server;否则你需要使用“旧的”FOR XML PATH 方法。
DECLARE @stm nvarchar(max),
@CRLF nchar(2) = NCHAR(13) + NCHAR(10);
DECLARE @SelectDelim nvarchar(15) = N',' + @CRLF + N' ',
@WithDelim nvarchar(15) = N',' + @CRLF + N' ';
-- Columns preparation
SELECT @stm = N'SELECT ' +
STRING_AGG(QUOTENAME([key]),@SelectDelim) + @CRLF +
N'INTO #TestData' + @CRLF + --This INTO is actually completely pointless, but we'll come onto this later
N'FROM OPENJSON((SELECT *' + @CRLF +
N' FROM @json),''$.records'')' + @CRLF +
N' WITH (' +
STRING_AGG(QUOTENAME([key]) + N' nvarchar(MAX)', @WithDelim) + N');' --Doing this fixing the issue in point 2
FROM OPENJSON(@json, '$[0]');
PRINT @stm;
在此我实际上解决了您的报价问题,如果我们查看打印的声明,我们现在得到以下内容:
SELECT [ProductId],
[ProductName],
[ProductDescription],
[DatePurchased]
INTO #TestData
FROM OPENJSON((SELECT *
FROM @json),'$.records')
WITH ([ProductId] nvarchar(MAX),
[ProductName] nvarchar(MAX),
[ProductDescription] nvarchar(MAX),
[DatePurchased] nvarchar(MAX));
看起来不错不是吗!现在让我们将该调用添加到sys.sp_executesql:
必须声明表变量“@json”。
哦,那是什么?嗯,这就是这些行的原因:
FROM OPENJSON((SELECT *
FROM @json),'$.records')
@json 不是表变量,而是标量变量。所以让我们修复它并将其更改为以下内容:
FROM OPENJSON(@json,'$.records')
而且,它运行了!是的!现在让我们尝试在动态语句的外部获取该数据:
SELECT *
FROM #TestData;
对象名称“#TestData”无效。
等等,什么?!好吧,我们回到我们的朋友“范围”。当动态 SQL 语句运行并且我们回到外部范围时,临时表已被删除,因此出现上述错误。因此,让我们将 INTO 注释掉(因为我不知道您对此有何打算)。
但是现在我们没有得到任何数据?好吧,那是因为你有OPENJSON(@json,''$.records'') 它应该只是OPENJSON(@json)。所以,让我们最终解决这个问题,然后一切正常!
完整的工作声明:
--- JSON document ---
DECLARE @json nvarchar(max) = N'[
{
"ProductId":1,
"ProductName":"Food",
"ProductDescription":"Apple",
"DatePurchased":"1995-05-01T00:00:00"
},
{
"ProductId":2,
"ProductName":"Electronics",
"ProductDescription":"TV",
"DatePurchased":"2018-09-17T00:00:00"
}
]';
DECLARE @stm nvarchar(max),
@CRLF nchar(2) = NCHAR(13) + NCHAR(10);
DECLARE @SelectDelim nvarchar(15) = N',' + @CRLF + N' ',
@WithDelim nvarchar(15) = N',' + @CRLF + N' ';
-- Columns preparation
SELECT @stm = N'SELECT ' +
STRING_AGG(QUOTENAME([key]),@SelectDelim) + @CRLF +
N'--INTO #TestData' + @CRLF + --This INTO is actually completely pointless, but we'll come onto this later
N'FROM OPENJSON(@json)' + @CRLF +
N' WITH (' +
STRING_AGG(QUOTENAME([key]) + N' nvarchar(MAX)', @WithDelim) + N');' --Doing this fixing the issue in point 2
FROM OPENJSON(@json, '$[0]');
PRINT @stm;
EXEC sys.sp_executesql @stm, N'@json nvarchar(MAX)', @json;
动态 SQL 运行:
SELECT [ProductId],
[ProductName],
[ProductDescription],
[DatePurchased]
--INTO #TestData
FROM OPENJSON(@json)
WITH ([ProductId] nvarchar(MAX),
[ProductName] nvarchar(MAX),
[ProductDescription] nvarchar(MAX),
[DatePurchased] nvarchar(MAX));