可以立即执行与 JSON_TABLE 一起使用

Can execute immediate be used with JSON_TABLE

我需要将 JSON 转换为 Oracle 12c v12.1.0.2

中的数据 table(键值列)

所以例如有一个 JSON 字符串像

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

我需要将其转换为:

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

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

我尝试使用立即执行:

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.

动态拉取列

有几件事可以帮助动态 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 限制,而是作为 SQL/JSON 限制 - 如果你做到这一点,使用有效的变量值,你仍然会得到 "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;
/

我已经使用 the alternative quoting mechanism 来避免在语句中将引号加倍,但这是可选的。使用相同的数据输出:

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