能否与JSON_TABLE一起执行immediate?

问题描述 投票:0回答:1

我需要在Oracle 12c v12.1.0.2中把JSON转换成数据表(键值列)。

例如,有一个JSON字符串,如

{"ID": 10, "Description": "TestJSON", "status":"New"}

我需要将其转换为.NET语言。

Column1          Column2
------------------------------------
ID                  10
Description         TestJSON
status              New

现在,我的JSON字符串可以改变属性的数量,因此我需要保持动态的转换。

我试着使用execute immediate :

set serveroutput on;
declare
sqlsmt VARCHAR2(200);
t3 varchar2(50);
begin
sqlsmt := 'SELECT * '||
'FROM  json_table( ( select jsonstr from mytable where ID= 10) , ''$[*]'' '||
                'COLUMNS (  :t1 PATH ''$.''|| '':t2'' ))';
execute immediate sqlsmt into t3 using 'desc' , '$.Description' ;
DBMS_OUTPUT.PUT_LINE( 'Output Variable: ' || t3);
END;

然而,我得到以下错误。

ORA-00904: : invalid identifier
ORA-06512: at line 8
00904. 00000 -  "%s: invalid identifier"

请帮助我。我使用的是Oracle 12c V1。但我真的需要从JSON中动态提取列。

oracle oracle12c
1个回答
1
投票

有几件事可以帮助动态SQL(假设你真的需要使用它)。第一个是使用 dbms_output 来显示生成的语句,然后再尝试执行它;所以在你的案例中。

...
dbms_output.put_line(sqlsmt);
execute immediate sqlsmt into t3;
--using 'descr' , '$.Description' ;
DBMS_OUTPUT.PUT_LINE( 'Output Variable: ' || t3);
END;
/

与你的代码显示。

SELECT * FROM  json_table( ( select jsonstr from mytable where ID= 10) , '$[*]' COLUMNS (  :t1 PATH '$.'|| ':t2' ))

最明显的问题是 '$.'|| ':t2',其中 :t2 不应该用引号,这不会导致错误,但会阻止它被绑定到你的变量上,因为它是一个文字值。你也有 $. 部分在该位和你的变量值中,但又没有到那个程度。

与所有的动态SQL一样,你只能为变量提供在 using 子句。你试图将列名作为绑定变量来传递,这是不允许的;所以它试图用 :t1 作为输出列名,而不是 desc;和 :t1 不是一个有效的名字。(也不是 desc 因为那是一个保留字--但无论哪一个都会得到同样的错误)。) 所以,你必须将列名连接起来,而不是绑定它。

看起来你可以使用 :t2 但是你也不能这样做,不是作为一个动态的SQL限制,而是作为一个SQLJSON限制--如果你走到那一步,用一个有效的变量值,你仍然会得到 "ORA-40454: path expression not a literal"。你也必须将路径连入语句中。

最后是 $[*] 不允许你匹配 Description...这就引出了关于动态SQL的第二个提示;先让静态查询正常工作,然后再使其动态化。

所以把这些放在一起,你可以这样做:

declare
  sqlsmt varchar2(200);
  t1 varchar2(30) := 'descr';
  t2 varchar2(30) := 'Description';
  t3 varchar2(50);
begin
  sqlsmt := 'SELECT * '||
    'FROM  json_table( ( select jsonstr from mytable where ID= 10) , ''$'' '||
    'COLUMNS ( ' || t1 || ' PATH ''$.' || t2 || '''))';
  dbms_output.put_line(sqlsmt);
  execute immediate sqlsmt into t3;
  dbms_output.put_line( 'Output Variable: ' || t3);
end;
/

用你的示例数据输出。

SELECT * FROM  json_table( ( select jsonstr from mytable where ID= 10) , '$' COLUMNS ( descr PATH '$.Description'))
Output Variable: TestJSON

这是一个有点奇怪的事情,唯一的东西 你可以传递作为一个变量,10,是硬编码。但我明白这是一个实验。

你也可以把语句写成。

select j.*
from mytable t
cross join json_table ( t.jsonstr, '$' columns ( descr path '$.Description' )) j
where t.id = 10;

你可以把它写成动态的

declare
  sqlsmt varchar2(200);
  id number := 10;
  t1 varchar2(30) := 'descr';
  t2 varchar2(30) := 'Description';
  t3 varchar2(50);
begin
  sqlsmt := 'select j.*'
    || ' from mytable t'
    || q'^ cross join json_table ( t.jsonstr, '$' columns ( ^'
    || t1
    || q'^ path '$.^'
    || t2
    || q'^' )) j^'
    || ' where t.id = :id';
  dbms_output.put_line(sqlsmt);
  execute immediate sqlsmt into t3 using id;
  dbms_output.put_line( 'Output Variable: ' || t3);
end;
/

我已经用了 另类引用机制 以避免在语句中使用双引号,但这是可选的。与输出的数据相同。

select j.* from mytable t cross join json_table ( t.jsonstr, '$' columns ( descr path '$.Description' )) j where t.id = :id
Output Variable: TestJSON

db<>fiddle

© www.soinside.com 2019 - 2024. All rights reserved.