【问题标题】:How do I transpose a single row to a column for conversion to JSON in Oracle Database 12.1?如何在 Oracle Database 12.1 中将单行转换为列以转换为 JSON?
【发布时间】:2019-01-23 08:59:52
【问题描述】:

情况如下:

我只有一行(来自查询)。该行的数据必须以 JSON 格式发送到服务器,其中 JSON 文件中的每个数据条目都是

"{column_name}": "{column_value}".

对于这种特殊情况,输入是

nr | a  | b    |
---|----|------|
1  | 10 | text |

输出应该是

{
  "a": 10,
  "b": "text"
}

如何在 PL/SQL 中基于一组未知的列动态生成 JSON 对象?这是否需要转置行?如果有,怎么做?

【问题讨论】:

  • 所以您需要能够接受任意查询并将其结果格式化为 JSON? 12.2 没有任何内置工具 - 你需要 12.2+。外部PL/JSON 可以在早期版本中使用,但如果这对你正在做的事情来说太过分了,你可以回退到动态 SQL 吗?
  • 我知道 12.2+ 的 JSON 功能,但我们公司的升级不会在不久的将来发生。同事们已经提出了动态 SQL,并且我们的数据库中已经存在类似的方法,但是其中的方法似乎过于繁重,无法简单地将数据转换为 JSON。问这个问题的主要原因是找到一个更简单的方法,也许使用(UN)PIVOT,或其他我不知道的SQL技巧,我不知道通过简单的在线搜索我无法直接找到,直到升级数据库。
  • 尽管如此,您也不能(取消)转置任意查询,无论如何都不会使用动态 SQL。或者,可能,使用 XML 数据透视 - 但是您会遇到同样的问题,即从 XML 中提取未知数量的键/值对。枢轴似乎不合适 - 您只有一行,并且想要一个 JSON 结果,而不是多个新行?至少在 12.2 之前,没有真正的通用结果的简单转换。如果转换更容易,我想你可以考虑使用 Java 存储过程。
  • 我对 Oracle 数据库和 SQL 还很陌生,所以请原谅我将 PIVOT 误认为是我的想法。不幸的是,使用 Java 也不是一种选择。转换为 JSON 的查询总是产生一行。无论如何感谢您的建议!

标签: sql json oracle plsql


【解决方案1】:

由于您使用的是 12cR1,因此您无法使用 12cR2 中添加的 JSON 生成功能。有可用的包 (PL/JSON),但如果您只有这一项任务,您可能会使用 dbms_sql 包自行开发。

作为一个相当简单 (!) 的示例,并假设问题中提到的单行,您可以执行以下操作:

declare
  -- for query text
  l_query varchar2(4000);
  -- for fetching query results
  l_varchar2 varchar2(4000);
  l_number number;
  l_date date;
  l_timestamp timestamp;
  -- for generated JSON string
  l_json varchar2(4000);
  -- for dbms_sql calls
  l_c pls_integer;
  l_col_cnt pls_integer;
  l_desc_t dbms_sql.desc_tab3;
  l_rc pls_integer;
begin
  -- arbitrary query
  l_query := 'select a, b, c from your_table where nr = 1';

  l_c := dbms_sql.open_cursor;
  dbms_sql.parse(l_c, l_query, dbms_sql.native);
  dbms_sql.describe_columns3(c => l_c, col_cnt => l_col_cnt, desc_t => l_desc_t);

  for i in 1..l_col_cnt loop
    case l_desc_t(i).col_type
      when 1 then
        dbms_sql.define_column(l_c, i, l_varchar2, 4000);
      when 2 then
        dbms_sql.define_column(l_c, i, l_number);
      when 12 then
        dbms_sql.define_column(l_c, i, l_date);
      when 180 then
        dbms_sql.define_column(l_c, i, l_timestamp);
      -- more types as needed
      else raise_application_error(-20001, 'Unknown data type ' || l_desc_t(i).col_type);
    end case;
  end loop;

  l_rc := dbms_sql.execute(l_c);
  l_rc := dbms_sql.fetch_rows(l_c);

  l_json := '{' || chr(10);

  for i in 1..l_col_cnt loop
    -- name
    l_json := l_json || ' "' || l_desc_t(i).col_name || '": ';
    -- value
    case l_desc_t(i).col_type
      when 1 then
        dbms_sql.column_value(l_c, i, l_varchar2);
        l_json := l_json || '"' || l_varchar2 || '"';
      when 2 then
        dbms_sql.column_value(l_c, i, l_number);
        l_json := l_json || l_number;
      when 12 then
        dbms_sql.column_value(l_c, i, l_date);
        l_json := l_json || '"' || to_char(l_date, 'YYYY-MM-DD"T"HH24:MI:SS') || '"';
      when 180 then
        dbms_sql.column_value(l_c, i, l_timestamp);
        l_json := l_json || '"' || to_char(l_timestamp, 'YYYY-MM-DD"T"HH24:MI:SS.FF3') || '"';
      -- more types as needed
      else raise_application_error(-20001, 'Unknown data type ' || l_desc_t(i).col_type);
    end case;
    if i < l_col_cnt then
      l_json := l_json || ',';
    end if;
    l_json := l_json || chr(10);
  end loop;

  l_json := l_json || '}';

  dbms_sql.close_cursor(l_c);

  -- for debugging, to see generated value
  dbms_output.put_line(l_json);
end;
/

您可能会使用类似的机制将查询传递到函数或过程并从函数或过程中取回 JSON。你可能需要一个 CLOB 来保存生成的 JSON;和错误检查等...

为了好玩,添加了一个带有日期列的示例表:

create table your_table (nr, a, b, c) as
select 1, 10, cast('text' as varchar2(30)), sysdate from dual;

得到:

{
 "A": 10,
 "B": "text",
 "C": "2019-01-23T07:14:43"
}


PL/SQL procedure successfully completed.

【讨论】:

  • 我正在寻找一种方法,它不需要在创建 JSON 时声明每一列,所以不幸的是,这种方法不是 100% 我需要的。尽管如此,该方法看起来效果很好,绝对值得一试。谢谢!
  • 列仅在查询本身中声明,动态内容仅使用查询返回的任何内容。如果合适的话,您可以在查询中使用*,尽管很少使用。 (如果您想要小写键,那么您必须使用lower(l_desct_t(i).col_name),或者使用带引号的标识符对列进行别名。)
  • 我 100% 确定我想将每一列都包含到 JSON 对象中,所以我会试一试。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-17
相关资源
最近更新 更多