【问题标题】:Return results of a sql query as JSON in oracle 12c在 oracle 12c 中以 JSON 形式返回 sql 查询的结果
【发布时间】:2014-06-26 23:31:27
【问题描述】:

背景

我需要从 Oracle 获取几千行并将它们转换为 JSON 以在 SlickGrid 中使用。 目前我正在获取 PHP 中的行,使用 iconv 将其从 ISO 转换为 UTF-8,并使用 json_encode 导出为 json。整个操作在 DB 端大约需要 1 秒,生成 JSON 需要 5 秒。太长了。

问题

我读到 Oracle 12c 支持 JSON,但我找不到我需要的确切内容。

有没有办法以json格式返回标准sql查询的结果?

据说我想发出类似这样的查询:

SELECT * from table AS JSON

并收到一个类似这样的有效 json:

[{"col1": "value1", "col2": 2}, {"col1": "valueOfRow2", "col2": 3}]

重要的是我需要为我转义 unicode 序列,因为我在客户端使用 ISO-8859-2 字符集,而 JSON 必须是 UTF-8 或转义序列。

【问题讨论】:

    标签: json oracle oracle12c


    【解决方案1】:

    Oracle 12c 对 JSON 的支持是一种存储 JSON 对象、查询它们并从中选择的能力。

    您有表格格式,只需将数据显示为 JSON。所以你可以简单地将行连接成 {'col1': 'rowN1', 'col2': 'rowN2'} 并将其余的放在客户端。 或者您可以使用 LISTAGG 来获取整个文档。例子: http://technology.amis.nl/2011/06/14/creating-json-document-straight-from-sql-query-using-listagg-and-with-clause/

    请注意 SQL VARCHAR2 4000 个字符的限制。

    您也可以查看http://database-geek.com/2009/03/25/json-in-and-out-of-oracle-json-data-type/ 但我不认为,oracle 对象类型会提高您的性能。

    另一种方法是使用 XMLType 导出 XML。然后将 XML 转换为 JSON。 XMLType 会处理特殊字符,API 相当稳定(您无需为 Oracle 14 重写程序)。

    【讨论】:

    • 感谢您的建议。不幸的是,我肯定会超出 4000 个字符的限制,因为我希望获取 5000 - 20000 行。如果使用手动聚合,我将不得不担心正确转义字符串和所有其他问题。
    • 在 Oracle 12cR1 中,表列、SQL 语句和 PL/SQL 代码的最大 VARCHAR2 大小已提高到 32k。在 Oracle 11g 中,您只能在 PL/SQL 代码中使用 VARCHAR2(32000);表列和 SQL 代码仅限于 VARCHAR4(4000)。
    • 关于 4000 限制的好点,但请注意,它是 4000 字节限制与字符。 (asktom.oracle.com/pls/apex/…)
    • @SWilk 您可以使用 xmlagg 而不是 listagg 来绕过 4000 个字符的限制,它将您的数据聚合到一个 clob 中,然后您可以调用 DBMS_XMLGEN.CONVERT() 来取消特殊字符的转义。
    【解决方案2】:

    您可以使用 xmltype 将 SQL 的结果转换为 XML 和 JSON。有关适用于 Oracle 9 版本的解决方案,请参阅以下文章。您还可以下载包 itstar_xml_util:

    http://stefan-armbruster.com/index.php/12-it/pl-sql/12-oracle-xml-and-json-goodies

    emp 表的简单示例:

    declare
      l_sql_string varchar2(2000);
      l_xml        xmltype;
      l_json       xmltype;
    begin
      l_sql_string := 'select a.empno, a.ename, a.job from emp a';
    
      -- Create the XML from SQL
      l_xml := itstar_xml_util.sql2xml(l_sql_string);
    
      -- Display the XML
      dbms_output.put_line(l_xml.getclobval());
    
      l_json := itstar_xml_util.xml2json(l_xml);
      -- Display the JSON
      dbms_output.put_line(l_json.getclobval());  
    end;
    

    结果如下:

    {"ROWSET": [
        {
          "EMPNO": 7839,
          "ENAME": "KING",
          "JOB": "PRESIDENT"
        },
        {
          "EMPNO": 7698,
          "ENAME": "BLAKE",
          "JOB": "MANAGER"
        },
    [...]
        {
          "EMPNO": 7934,
          "ENAME": "MILLER",
          "JOB": "CLERK"
        }
      ]}
    

    【讨论】:

    【解决方案3】:

    Oracle 12c 版本 12.1.0.2(截至 11.11.2014 的最新版本)添加了 JSON 支持: https://docs.oracle.com/database/121/NEWFT/chapter12102.htm#BGBGADCC

    它从 10 月 17 日开始提供。 https://blogs.oracle.com/db/entry/oracle_database_12c_release_1

    如果您无法修补/使用该版本,可以使用 Lewis Cunningham 和 Jonas Krogsboell 编写的出色软件包:PL/JSON * http://pljson.sourceforge.net/

    这是一个很棒的包(我已经在许多数据库安装中使用过它)。

    包含的示例很好,涵盖了大多数场景。

    declare 
      ret json;
    begin
      ret := json_dyn.executeObject('select * from tab');
      ret.print;
    end;
    /
    

    【讨论】:

    • 12.1 没有任何从关系数据生成 JSON 的功能。它只能解析现有的 JSON 数据。为此,您需要 Oracle 12.2
    【解决方案4】:

    12cR2(在 Oracle 云中可用)本机支持此功能。

    SQL> select JSON_ARRAY(EMPLOYEE_ID, FIRST_NAME,LAST_NAME) from HR.EMPLOYEES;
    
    JSON_ARRAY(EMPLOYEE_ID,FIRST_NAME,LAST_NAME)
    --------------------------------------------------------------------------------
    [100,"Steven","King"]
    [101,"Neena","Kochhar"]
    

    SQL> select JSON_OBJECT('ID' is EMPLOYEE_ID , 'FirstName' is FIRST_NAME,'LastName' is LAST_NAME) from HR.EMPLOYEES;
    
    JSON_OBJECT('ID'ISEMPLOYEE_ID,'FIRSTNAME'ISFIRST_NAME,'LASTNAME'ISLAST_NAME)
    ----------------------------------------------------------------------------
    {"ID":100,"FirstName":"Steven","LastName":"King"}
    {"ID":101,"FirstName":"Neena","LastName":"Kochhar"}
    

    【讨论】:

    • 它现在随处可用 - 在云端、客户的 Oracle 云和本地。
    • 子数组怎么做?
    【解决方案5】:

    我没有看到 Python 解决方案(以防您需要转储 JSON)。

    我为中等大小的提取写了json-ora-extract(因为数据集必须适合可用内存)。

    它使用wx_Oraclejson Python 模块从Oracle 数据库(任何版本)读取数据并将其转储到*.json 文件中。

    还有一个选项可以创建压缩的*.gz 文件。

    【讨论】:

    • 回购离线:/
    【解决方案6】:

    要添加到 oracle 12.2 中的答案,您可以像这样根据需要创建 json。

    SELECT JSON_ARRAY(
    JSON_OBJECT (
             KEY 'number' VALUE s.number,
             KEY 'name' VALUE s.sname,
             KEY 'location' VALUE s.loc
              )
           ) AS student_det
    FROM   student s;
    

    【讨论】:

      【解决方案7】:

      试试这个:

      :) 生活是幸福的

      with data as
        ( select 
          xmlelement(e,regexp_replace('{"name":"'||colname||'"}', '[[:cntrl:]]', ''),',') col1
          from tblname
        )
        select
              rtrim(replace(replace(replace(xmlagg(col1).getclobval(),'&'||'quot;','"'),'<E>',''),'</E>',''),',')
              as very_long_json
        from data;
      

      【讨论】:

        【解决方案8】:

        从 Oracle 19c 开始,为表中的一行构造 JSON 表示的语法is simplified

        例如:要将hr.employees 的所有行转换为单独的 json,请使用

        SELECT JSON_OBJECT(*) FROM hr.employees ;
        
        {
        "EMPLOYEE_ID" : 100,
        "FIRST_NAME" : "Steven",
        "LAST_NAME" : "King",
        "EMAIL" : "SKING",
        "PHONE_NUMBER" : "515.123.4567",
        "HIRE_DATE" : "2003-06-17T00:00:00",
        "JOB_ID" : "AD_PRES",
        "SALARY" : 24000,
        "COMMISSION_PCT" : null,
        "MANAGER_ID" : null,
        "DEPARTMENT_ID" : 90
        }                       --row 1
        {
        "EMPLOYEE_ID" : 101,
        "FIRST_NAME" : "Neena",
        "LAST_NAME" : "Kochhar",
        "EMAIL" : "NKOCHHAR",
        "PHONE_NUMBER" : "515.123.4568",
        "HIRE_DATE" : "2005-09-21T00:00:00",
        "JOB_ID" : "AD_VP",
        "SALARY" : 17000,
        "COMMISSION_PCT" : null,
        "MANAGER_ID" : 100,
        "DEPARTMENT_ID" : 90
        }                       --row 2
         ...
        

        LIVE SQL example

        【讨论】:

        • 如何只选择选择性列,而不是所有列?
        【解决方案9】:

        12.2 版包含直接从 SQL 查询生成 JSON 文档的新功能。实现目标最简单的方法是使用函数:JSON_OBJECTJSON_ARRAYAGG

        create table tab as
            select level col1, 'value '||level col2 from dual connect by level <= 2
        / 
        
        select max (rownum) rn, json_arrayagg (
            json_object (
                key 'col1' value col1,
                key 'col2' value col2
            ) format json returning clob 
        ) as json_doc
        from tab;
        

        结果:

                RN JSON_DOC                                                                        
        ---------- ---------------------------------------------------------
                 2 [{"col1":1,"col2":"value 1"},{"col1":2,"col2":"value 2"}] 
        

        用大量数据进行测试:

        select rn, length (json_doc) json_size, json_doc from (
            <query mentoined above here>
            cross join (select dummy from dual connect by level <= 1e5) 
            );
        
                RN  JSON_SIZE JSON_DOC                                                                        
        ---------- ---------- ---------------------------------------------------------
            200000    5600001 [{"col1":1,"col2":"value 1"},{"col1":2,"col2":"value 2"},
        

        在慢速测试机器上大约需要 1 秒。创建 5,6M JSON。


        在 19c 版中,函数 JSON_OBJECT is simplified 的语法。
        上面的查询现在看起来像这样:

        select json_arrayagg (  
            json_object (*) returning clob   
        ) as json_doc  
        from tab;
        

        开启Live SQL

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-12-10
          • 2021-03-06
          • 2019-01-16
          • 1970-01-01
          • 1970-01-01
          • 2017-05-18
          • 1970-01-01
          • 2021-12-12
          相关资源
          最近更新 更多