简体   繁体   中英

Dynamically select columns for json_table

I have this query:

DECLARE
     rc sys_refcursor;         
     j_keys varchar2(2000);
     query_s varchar2(20000);

BEGIN

 j_keys := '(
        SELECT 
                listagg(distinct k.COLUMN_VALUE || '' varchar(256) PATH ''$.'' ||  k.COLUMN_VALUE, '', '') as j_cols
        FROM   (select json_response as json_value from SOME_TABLE where param=''some_param'') t
               CROSS APPLY JSON_TABLE(
                 t.json_value,
                 ''$[*]''
                 COLUMNS (
                   idx FOR ORDINALITY,
                   json_obj VARCHAR2(4000) FORMAT JSON PATH ''$''
                 )
               ) jt
               CROSS APPLY get_keys( jt.json_obj ) k
               )';
                 
  query_s := 'SELECT * FROM json_table((select json_response from SOME_TABLE where param=''some_param''), ''$[*]''
                 COLUMNS 
                        ' || j_keys || ')';
                        
                        
  open rc for query_s;
              
dbms_sql.return_result(rc);

END;

It's a nasty query, meant to test the possibility of dynamically selecting columns for the json_table (and then parse any json-string in the selected clob - named json_response in SOME_TABLE)

Not entirely sure my syntax is set correct, but currently it complains about:

ORA-00904: invalid identifier

on line 22 (the "open rc for '...' line)

You want to run the first query rather than creating a string literal containing the text of the query and then put the output from the first query into the second query string:

DECLARE
  rc sys_refcursor;         
  j_keys varchar2(2000);
  query_s varchar2(20000);
BEGIN
  SELECT listagg(
           k.COLUMN_VALUE || ' varchar(256) PATH ''$.' ||  k.COLUMN_VALUE || '''',
           ','
         )
  INTO   j_keys
  FROM   ( SELECT JSON_QUERY( json_value, '$[1]' RETURNING CLOB) AS json_obj
           FROM   table_name
         )t
         CROSS APPLY get_keys( t.json_obj ) k;

  query_s := 'SELECT jt.*
              FROM   table_name t
                     CROSS APPLY JSON_TABLE(
                       t.json_value,
                       ''$[*]''
                       COLUMNS 
                        ' || j_keys || ') jt';
                        
  open rc for query_s;

  DECLARE
    col1 VARCHAR2(50);
    col2 VARCHAR2(50);
    col3 VARCHAR2(50);
  BEGIN
    LOOP
      FETCH rc INTO col1, col2, col3;
      EXIT WHEN rc%NOTFOUND;
      DBMS_OUTPUT.PUT_LINE( col1 || ', ' || col2 || ', ' || col3 );
    END LOOP;
  END;

  -- or

  -- dbms_sql.return_result(rc);
END;
/

Which, given this setup:

CREATE TABLE table_name (
  id         NUMBER
             GENERATED ALWAYS AS IDENTITY
             PRIMARY KEY,
  json_value CLOB
             CHECK( json_value IS JSON )
);

INSERT INTO table_name ( json_value ) VALUES (
'[{"column1":"value1","column2":"value2","column3":"value3"},
{"column1":"value4","column2":"value5","column3":"value6"},
{"column3":"value9","column1":"value7","column2":"value8"}]'
);

CREATE FUNCTION get_keys(
  value IN CLOB
) RETURN SYS.ODCIVARCHAR2LIST PIPELINED
IS
  js   JSON_OBJECT_T := JSON_OBJECT_T( value );
  keys JSON_KEY_LIST;
BEGIN
  keys := js.get_keys();
  FOR i in 1 .. keys.COUNT LOOP
    PIPE ROW ( keys(i) );
  END LOOP;
END;
/

CREATE FUNCTION get_value(
  value IN CLOB,
  path  IN VARCHAR2
) RETURN VARCHAR2
IS
  js JSON_OBJECT_T := JSON_OBJECT_T( value );
BEGIN
  RETURN js.get_string( path );
END;
/

Outputs:

 value1, value2, value3 value4, value5, value6 value7, value8, value9

db<>fiddle here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM