0

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)

2
  • what database Oracle version do you have ? Commented Aug 25, 2021 at 13:47
  • I believe it is 19c, with OCI 12.1 Commented Aug 25, 2021 at 14:49

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.