【问题标题】:Can execute immediate be used with JSON_TABLE可以与 JSON_TABLE 一起使用立即执行
【发布时间】:2020-06-17 13:54:50
【问题描述】:

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

例如,有一个 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 - JSON 字段名称和/或输出列是否真的会在运行时提供?
  • JSON 输入的键值对数量可能会有所不同。所以要求是保持 JSON 动态。
  • JSON 是一个变量;问题是desc(这是非法的)和Description 在你运行时是否会有所不同?
  • 好的,所以我们正在尝试在我们的应用程序中实现可配置的表单。明天说一个新列被添加到表单中,我需要从 JSON 中获取新列的值。我们在数据库表中配置了与 JSON 中的字段名称匹配的列名称。我想运行一个循环将这些字段名称传递给 JSON 并获取它的值。这样,如果将任何新列添加到我的表单字段主控中,我们将能够从 JSON 中动态获取其值。我希望我能解释一下

标签: oracle oracle12c


【解决方案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 限制,而是作为 SQL/JSON 限制 - 如果你走到那一步,使用有效的变量值,你仍然会得到“ORA-40454: path expression not a文字”。您还必须将路径连接到语句中。

最后$[*] 不允许你匹配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

【讨论】:

  • 非常感谢您的解决方案!我非常感谢您的详细回复。该查询确实对我有用。我将尝试在我的存储过程中循环实现它。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2021-03-09
  • 1970-01-01
  • 1970-01-01
  • 2012-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-07
相关资源
最近更新 更多