return resultset from function

前端 未结 2 1827
醉话见心
醉话见心 2020-12-03 16:18

I need to return a resultset from function and work with this resultset just like with an ordinary table.

So I need something like following:

select          


        
相关标签:
2条回答
  • 2020-12-03 16:59

    If I understand you correctly, you want to do this:

    select * from (select * from dual)
    

    ...with the caveat that the subquery is in some way dynamic? You can do this using a PL/SQL block, using a reference cursor:

    declare
      subQuery         varchar2(1000);
      mainQuery        varchar2(1000) := 'select * from (';
    
      type myRefCursor is ref cursor;
      myResultset      myRefCursor;
    
      myField1         FIELDTYPE;
      ...
      myFieldN         FIELDTYPE;
    begin
      -- Generate this dynamically
      subQuery := 'select * from dual';
    
      -- Create main query and open cursor
      mainQuery := mainQuery || subQuery || ')';
      open myResultset for mainQuery;
    
      -- Loop through records
      loop
        fetch myResultset into myField1, ..., myFieldN;
        exit when myResultset%NOTFOUND;
    
        -- Do something with the record data
        dbms_output.put_line(myField1 || ' ... ' || myFieldN);
      end loop;
    
      close myResultset;
    end;
    /
    

    Note that rather than using fetch into with individual variables, you can populate an entire record at once, provided you can define the record's field types. That is, if you have created a custom type or your record's type matches a table you already have in your schema. For the latter, you can use:

    myRecord someTable%ROWTYPE;
    

    ...in the declaration block, then change the fetch into statement to:

    fetch myResultset into myRecord;
    

    ...and access record fields using dot notation (e.g., myRecord.some_field_name).

    You say in your comments that the dynamic SQL bit is from the results of some other query. Therefore, in my example code, you could use a regular cursor to loop over these data to create the subQuery variable in each instance... Although, incidentally, is there any reason why what you're trying to achieve can't be done with a simple join?

    0 讨论(0)
  • 2020-12-03 17:02

    If you need a result set and a ref cursor won't do with a datatype called sys.anydataset. i.e what you seem to want is a pipelined function, but of course with a regular pipelined function you need to define the output structure, which in your case isn't static.

    Enter anydataset. this type allows us to dynamically generate types on the fly (at hard parse time only) to allow us to define pipelined functions with varying outputs.

    The coding is a bit complex unfortunately.

    To start with, we define a type that will do the processing of the passed in SQL statement.

    SQL> create type dyn_pipeline as object
      2  (
      3    atype anytype,
      4
      5    static function ODCITableDescribe(rtype out anytype,
      6                                      stmt  in  varchar2)
      7      return number,
      8
      9    static function ODCITablePrepare(sctx      out dyn_pipeline,
     10                                     tf_info   in  sys.ODCITabfuncinfo,
     11                                     stmt      in  varchar2)
     12      return number,
     13
     14    static function ODCITableStart(sctx  in out dyn_pipeline,
     15                                   stmt  in     varchar2)
     16      return number,
     17
     18    member function ODCITablefetch(self  in out dyn_pipeline,
     19                                   nrows in     number,
     20                                  rws   out    anydataset)
     21      return number,
     22
     23    member function ODCITableClose(self in dyn_pipeline)
     24      return number
     25  );
     26  /
    

    Next up, we create a package spec that will be basically your querydb function call:

    SQL> create package pkg_pipeline
      2  as
      3
      4    /*
      5     * Global Types
      6     */
      7    -- Describe array.
      8    type dynamic_sql_rec is record(cursor    integer,
      9                                   column_cnt  pls_integer,
     10                                   description dbms_sql.desc_tab2,
     11                                   execute     integer);
     12    -- Meta data for the ANYTYPE.
     13    type anytype_metadata_rec is record(precision pls_integer,
     14                                        scale     pls_integer,
     15                                        length    pls_integer,
     16                                        csid      pls_integer,
     17                                        csfrm     pls_integer,
     18                                        schema    varchar2(30),
     19                                        type      anytype,
     20                                        name      varchar2(30),
     21                                        version   varchar2(30),
     22                                        attr_cnt  pls_integer,
     23                                        attr_type anytype,
     24                                        attr_name varchar2(128),
     25                                        typecode  pls_integer);
     26
     27
     28    /*
     29     * Global Variables
     30     */
     31    -- SQL descriptor.
     32    r_sql dynamic_sql_rec;
     33
     34    /*
     35     * function will run the given SQL
     36     */
     37    function querydb(p_stmt in varchar2)
     38      return anydataset pipelined using dyn_pipeline;
     39
     40  end pkg_pipeline;
     41  /
    
    Package created.
    

    the types there will just hold some info about the SQL structure itself (we will be using DBMS_SQL to describe the input SQL as it has functions to get the number of columns, data types etc out of any given SQL statement.

    The main type body is where the processing occurs:

    SQL> create type body dyn_pipeline
      2  as
      3
      4    /*
      5     * DESC step. this will be called at hard parse and will create
      6     * a physical type in the DB Schema based on the select columns.
      7     */
      8    static function ODCITableDescribe(rtype out anytype,
      9                                      stmt  in  varchar2)
     10      return number
     11    is
     12
     13      /* Variables */
     14      -- Type to hold the dbms_sql info (description)
     15      r_sql   pkg_pipeline.dynamic_sql_rec;
     16      -- Type to create (has all the columns) of the sql query.
     17      t_anyt  anytype;
     18      -- SQL query that will be made up from the 2 passed in queries.
     19      v_sql   varchar2(32767);
     20
     21    begin
     22
     23      /*
     24       * Parse the SQL and describe its format and structure.
     25       */
     26      v_sql := replace(stmt, ';', null);
     27
     28      --  open, parse and discover all info about this SQL.
     29      r_sql.cursor := dbms_sql.open_cursor;
     30      dbms_sql.parse( r_sql.cursor, v_sql, dbms_sql.native );
     31      dbms_sql.describe_columns2( r_sql.cursor, r_sql.column_cnt, r_sql.description );
     32      dbms_sql.close_cursor( r_sql.cursor );
     33
     34      -- Start to create the physical type.
     35      anytype.BeginCreate( DBMS_TYPES.TYPECODE_OBJECT, t_anyt );
     36
     37      -- Loop through each attribute and add to the type.
     38      for i in 1 .. r_sql.column_cnt
     39      loop
     40
     41        t_anyt.AddAttr(r_sql.description(i).col_name,
     42                       case
     43                         when r_sql.description(i).col_type in (1,96,11,208)
     44                         then dbms_types.typecode_varchar2
     45                         when r_sql.description(i).col_type = 2
     46                         then dbms_types.typecode_number
     47                         when r_sql.description(i).col_type in (8,112)
     48                         then dbms_types.typecode_clob
     49                         when r_sql.description(i).col_type = 12
     50                         then dbms_types.typecode_date
     51                         when r_sql.description(i).col_type = 23
     52                         then dbms_types.typecode_raw
     53                         when r_sql.description(i).col_type = 180
     54                         then dbms_types.typecode_timestamp
     55                         when r_sql.description(i).col_type = 181
     56                         then dbms_types.typecode_timestamp_tz
     57                         when r_sql.description(i).col_type = 182
     58                         then dbms_types.typecode_interval_ym
     59                         when r_sql.description(i).col_type = 183
     60                         then dbms_types.typecode_interval_ds
     61                         when r_sql.description(i).col_type = 231
     62                         then dbms_types.typecode_timestamp_ltz
     63                       end,
     64                       r_sql.description(i).col_precision,
     65                       r_sql.description(i).col_scale,
     66                       r_sql.description(i).col_max_len,
     67                       r_sql.description(i).col_charsetid,
     68                       r_sql.description(i).col_charsetform );
     69      end loop;
     70
     71      t_anyt.EndCreate;
     72
     73      -- set the output type to our built type.
     74      ANYTYPE.BeginCreate(dbms_types.TYPECODE_TABLE, rtype);
     75      rtype.SetInfo(null, null, null, null, null, t_anyt,
     76                    dbms_types.TYPECODE_OBJECT, 0);
     77      rtype.EndCreate();
     78
     79      return ODCIConst.Success;
     80
     81    end ODCITableDescribe;
     82
     83
     84    /*
     85     * PREPARE step. Initialise our type.
     86     */
     87    static function ODCITableprepare(sctx      out dyn_pipeline,
     88                                     tf_info   in  sys.ODCITabfuncinfo,
     89                                     stmt      in  varchar2)
     90      return number
     91    is
     92
     93      /* Variables */
     94      -- Meta data.
     95      r_meta   pkg_pipeline.anytype_metadata_rec;
     96
     97    begin
     98
     99      r_meta.typecode := tf_info.rettype.getattreleminfo(
    100                           1, r_meta.precision, r_meta.scale, r_meta.length,
    101                           r_meta.csid, r_meta.csfrm, r_meta.type, r_meta.name
    102                         );
    103
    104      sctx := dyn_pipeline(r_meta.type);
    105      return odciconst.success;
    106
    107    end;
    108
    109
    110    /*
    111     * START step. this is where we execute the cursor prior to fetching from it.
    112     */
    113    static function ODCITablestart(sctx  in out dyn_pipeline,
    114                                   stmt  in     varchar2)
    115      return number
    116    is
    117
    118      /* Variables */
    119      r_meta pkg_pipeline.anytype_metadata_rec;
    120      v_sql varchar2(32767);
    121    begin
    122
    123      v_sql := replace(stmt, ';', null);
    124      pkg_pipeline.r_sql.cursor := dbms_sql.open_cursor;
    125      dbms_sql.parse(pkg_pipeline.r_sql.cursor, v_sql, dbms_sql.native);
    126      dbms_sql.describe_columns2(pkg_pipeline.r_sql.cursor,
    127                                 pkg_pipeline.r_sql.column_cnt,
    128                                 pkg_pipeline.r_sql.description);
    129
    130      -- define all the columns found to let Oracle know the datatypes.
    131      for i in 1..pkg_pipeline.r_sql.column_cnt
    132      loop
    133
    134        r_meta.typecode := sctx.atype.GetAttrElemInfo(
    135                             i, r_meta.precision, r_meta.scale, r_meta.length,
    136                             r_meta.csid, r_meta.csfrm, r_meta.type, r_meta.name
    137                           );
    138
    139        case r_meta.typecode
    140          when dbms_types.typecode_varchar2
    141          then
    142            dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, '', 32767);
    143          when dbms_types.typecode_number
    144          then
    145            dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as number));
    146          when dbms_types.typecode_date
    147          then
    148            dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as date));
    149          when dbms_types.typecode_raw
    150          then
    151            dbms_sql.define_column_raw(pkg_pipeline.r_sql.cursor, i, cast(null as raw), r_meta.length);
    152          when dbms_types.typecode_timestamp
    153          then
    154            dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as timestamp));
    155          when dbms_types.typecode_timestamp_tz
    156          then
    157            dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as timestamp with time zone));
    158          when dbms_types.typecode_timestamp_ltz
    159          then
    160            dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as timestamp with local time zone));
    161          when dbms_types.typecode_interval_ym
    162          then
    163            dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as interval year to month));
    164          when dbms_types.typecode_interval_ds
    165          then
    166            dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as interval day to second));
    167          when dbms_types.typecode_clob
    168          then
    169            case pkg_pipeline.r_sql.description(i).col_type
    170              when 8
    171              then
    172                dbms_sql.define_column_long(pkg_pipeline.r_sql.cursor, i);
    173              else
    174                dbms_sql.define_column(pkg_pipeline.r_sql.cursor, i, cast(null as clob));
    175            end case;
    176        end case;
    177      end loop;
    178
    179      -- execute the SQL.
    180      pkg_pipeline.r_sql.execute := dbms_sql.execute(pkg_pipeline.r_sql.cursor);
    181
    182      return odciconst.success;
    183
    184    end ODCITablestart;
    185
    186
    187    /*
    188     * FETCH step.
    189     */
    190    member function ODCITablefetch(self   in out dyn_pipeline,
    191                                   nrows  in     number,
    192                                   rws    out    anydataset)
    193      return number
    194    is
    195
    196      /* Variables */
    197      -- Buffers to hold values.
    198      v_vc_col       varchar2(32767);
    199      v_num_col      number;
    200      v_date_col     date;
    201      v_raw_col      raw(32767);
    202      v_raw_error    number;
    203      v_raw_len      integer;
    204      v_int_ds_col   interval day to second;
    205      v_int_ym_col   interval year to month;
    206      v_ts_col       timestamp;
    207      v_tstz_col     timestamp with time zone;
    208      v_tsltz_col    timestamp with local time zone;
    209      v_clob_col     clob;
    210      v_clob_offset  integer := 0;
    211      v_clob_len     integer;
    212      -- Metadata
    213      r_meta  pkg_pipeline.anytype_metadata_rec;
    214
    215    begin
    216
    217      if dbms_sql.fetch_rows( pkg_pipeline.r_sql.cursor ) > 0
    218      then
    219
    220        -- Describe to get number and types of columns.
    221        r_meta.typecode := self.atype.getinfo(
    222                             r_meta.precision, r_meta.scale, r_meta.length,
    223                             r_meta.csid, r_meta.csfrm, r_meta.schema,
    224                             r_meta.name, r_meta.version, r_meta.attr_cnt
    225                           );
    226
    227        anydataset.begincreate(dbms_types.typecode_object, self.atype, rws);
    228        rws.addinstance();
    229        rws.piecewise();
    230
    231        -- loop through each column extracting value.
    232        for i in 1..pkg_pipeline.r_sql.column_cnt
    233        loop
    234
    235          r_meta.typecode := self.atype.getattreleminfo(
    236                               i, r_meta.precision, r_meta.scale, r_meta.length,
    237                               r_meta.csid, r_meta.csfrm, r_meta.attr_type,
    238                               r_meta.attr_name
    239                             );
    240
    241          case r_meta.typecode
    242            when dbms_types.typecode_varchar2
    243            then
    244              dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_vc_col);
    245              rws.setvarchar2(v_vc_col);
    246            when dbms_types.typecode_number
    247            then
    248              dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_num_col);
    249              rws.setnumber(v_num_col);
    250            when dbms_types.typecode_date
    251            then
    252              dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_date_col);
    253              rws.setdate(v_date_col);
    254            when dbms_types.typecode_raw
    255            then
    256              dbms_sql.column_value_raw(pkg_pipeline.r_sql.cursor, i, v_raw_col,
    257                 v_raw_error, v_raw_len);
    258              rws.setraw(v_raw_col);
    259            when dbms_types.typecode_interval_ds
    260            then
    261              dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_int_ds_col);
    262              rws.setintervalds(v_int_ds_col);
    263            when dbms_types.typecode_interval_ym
    264            then
    265              dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_int_ym_col);
    266              rws.setintervalym(v_int_ym_col);
    267            when dbms_types.typecode_timestamp
    268            then
    269              dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_ts_col);
    270              rws.settimestamp(v_ts_col);
    271            when dbms_types.typecode_timestamp_tz
    272            then
    273              dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_tstz_col);
    274              rws.settimestamptz(v_tstz_col);
    275           when dbms_types.typecode_timestamp_ltz
    276           then
    277              dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_tsltz_col);
    278              rws.settimestampltz(v_tsltz_col);
    279           when dbms_types.typecode_clob
    280           then
    281             case pkg_pipeline.r_sql.description(i).col_type
    282               when 8
    283               then
    284                 loop
    285                   dbms_sql.column_value_long(pkg_pipeline.r_sql.cursor, i, 32767, v_clob_offset,
    286                                              v_vc_col, v_clob_len);
    287                   v_clob_col := v_clob_col || v_vc_col;
    288                   v_clob_offset := v_clob_offset + 32767;
    289                   exit when v_clob_len < 32767;
    290                 end loop;
    291               else
    292                 dbms_sql.column_value(pkg_pipeline.r_sql.cursor, i, v_clob_col);
    293             end case;
    294             rws.setclob(v_clob_col);
    295          end case;
    296        end loop;
    297
    298        rws.endcreate();
    299
    300      end if;
    301
    302      return ODCIConst.Success;
    303
    304    end;
    305
    306    /*
    307     * CLOSE step. close the cursor.
    308     */
    309    member function ODCITableClose(self in dyn_pipeline)
    310      return number
    311    is
    312
    313
    314    begin
    315      dbms_sql.close_cursor( pkg_pipeline.r_sql.cursor );
    316      pkg_pipeline.r_sql := null;
    317      return odciconst.success;
    318    end ODCITableClose;
    319
    320  end;
    321  /
    
    Type body created.
    

    once this is done, you can query like:

    SQL> select * from table(pkg_pipeline.querydb('select * from dual'));
    
    D
    -
    X
    
    SQL> select * from table(pkg_pipeline.querydb('select * from v$mystat where rownum <= 2'));
    
           SID STATISTIC#      VALUE
    ---------- ---------- ----------
           230          0          1
           230          1          1
    
    0 讨论(0)
提交回复
热议问题