暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle 具有行列表的枢轴

ASKTOM 2019-09-24
262

问题描述

我们有一个包含db_name和用户名的表。在输出中,我们需要每个DB的用户列表,即列数将等于不同的db_name。

示例输出格式:
DB1 DB2
用户1用户4
用户2用户5
用户3

数据库版本:11g


以下是供参考的示例脚本:

CREATE TABLE app_users (
    username   VARCHAR2(40),
    db_name    VARCHAR2(40)
);

insert all
into app_users values ('USER1','DB1')
into app_users values ('USER2','DB1')
into app_users values ('USER3','DB1')
into app_users values ('USER4','DB2')
into app_users values ('USER5','DB2')
select * from dual;
复制


请帮忙。

专家解答

我们目前没有动态枢轴,但是AMIS的好人不久前写了一个不错的动态枢轴函数,详细信息如下:

https://technology.amis.nl/2006/05/24/dynamic-sql-pivoting-stealing-antons-thunder/

它可以用来实现你想要的。创建后,您只需要最后选择 :-)

SQL> CREATE OR REPLACE
  2  type PivotImpl as object
  3  (
  4    ret_type anytype,      -- The return type of the table function
  5    stmt varchar2(32767),
  6    fmt  varchar2(32767),
  7    cur integer,
  8    static function ODCITableDescribe( rtype out anytype, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number := 0 )
  9    return number,
 10    static function ODCITablePrepare( sctx out PivotImpl, ti in sys.ODCITabFuncInfo, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number := 0 )
 11    return number,
 12    static function ODCITableStart( sctx in out PivotImpl, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number := 0 )
 13    return number,
 14    member function ODCITableFetch( self in out PivotImpl, nrows in number, outset out anydataset )
 15    return number,
 16    member function ODCITableClose( self in PivotImpl )
 17    return number
 18  )
 19  /

Type created.

SQL>
SQL> create or replace type body PivotImpl as
  2    static function ODCITableDescribe( rtype out anytype, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number )
  3    return number
  4    is
  5      atyp anytype;
  6      cur integer;
  7      numcols number;
  8      desc_tab dbms_sql.desc_tab2;
  9      rc sys_refcursor;
 10      t_c2 varchar2(32767);
 11      t_fmt varchar2(1000);
 12    begin
 13      cur := dbms_sql.open_cursor;
 14      dbms_sql.parse( cur, p_stmt, dbms_sql.native );
 15      dbms_sql.describe_columns2( cur, numcols, desc_tab );
 16      dbms_sql.close_cursor( cur );
 17  --
 18      anytype.begincreate( dbms_types.typecode_object, atyp );
 19  for i in 1 .. numcols - 2
 20  loop
 21        atyp.addattr( desc_tab( i ).col_name
 22                    , case desc_tab( i ).col_type
 23                        when 1   then dbms_types.typecode_varchar2
 24                        when 2   then dbms_types.typecode_number
 25                        when 9   then dbms_types.typecode_varchar2
 26                        when 11  then dbms_types.typecode_varchar2  -- show rowid as varchar2
 27                        when 12  then dbms_types.typecode_date
 28                        when 208 then dbms_types.typecode_varchar2  -- show urowid as varchar2
 29                        when 96  then dbms_types.typecode_char
 30                        when 180 then dbms_types.typecode_timestamp
 31                        when 181 then dbms_types.typecode_timestamp_tz
 32                        when 231 then dbms_types.typecode_timestamp_ltz
 33                        when 182 then dbms_types.typecode_interval_ym
 34                        when 183 then dbms_types.typecode_interval_ds
 35                      end
 36                    , desc_tab( i ).col_precision
 37                    , desc_tab( i ).col_scale
 38                    , case desc_tab( i ).col_type
 39                        when 11 then 18  -- for rowid col_max_len = 16, and 18 characters are shown
 40                        else desc_tab( i ).col_max_len
 41                      end
 42                    , desc_tab( i ).col_charsetid
 43                    , desc_tab( i ).col_charsetform
 44                    );
 45  end loop;
 46      if instr( p_fmt, '@p@' ) > 0
 47      then
 48        t_fmt := p_fmt;
 49      else
 50        t_fmt := '@p@';
 51      end if;
 52      open rc for replace( 'select distinct ' || t_fmt || '
 53                        from( ' || p_stmt || ' )
 54            order by ' || t_fmt
 55     , '@p@'
 56                         , desc_tab( numcols - 1 ).col_name
 57     );
 58  loop
 59        fetch rc into t_c2;
 60    exit when rc%notfound;
 61        atyp.addattr( t_c2
 62                    , case desc_tab( numcols ).col_type
 63                      when 1   then dbms_types.typecode_varchar2
 64                      when 2   then dbms_types.typecode_number
 65                      when 9   then dbms_types.typecode_varchar2
 66                      when 11  then dbms_types.typecode_varchar2  -- show rowid as varchar2
 67                      when 12  then dbms_types.typecode_date
 68                      when 208 then dbms_types.typecode_urowid
 69                      when 96  then dbms_types.typecode_char
 70                      when 180 then dbms_types.typecode_timestamp
 71                      when 181 then dbms_types.typecode_timestamp_tz
 72                      when 231 then dbms_types.typecode_timestamp_ltz
 73                      when 182 then dbms_types.typecode_interval_ym
 74                      when 183 then dbms_types.typecode_interval_ds
 75                    end
 76                  , desc_tab( numcols ).col_precision
 77                  , desc_tab( numcols ).col_scale
 78                  , case desc_tab( numcols ).col_type
 79                      when 11 then 18  -- for rowid col_max_len = 16, and 18 characters are shown
 80                      else desc_tab( numcols ).col_max_len
 81                    end
 82                  , desc_tab( numcols ).col_charsetid
 83                  , desc_tab( numcols ).col_charsetform
 84                    );
 85      end loop;
 86  close rc;
 87      atyp.endcreate;
 88      anytype.begincreate( dbms_types.typecode_table, rtype );
 89      rtype.SetInfo( null, null, null, null, null, atyp, dbms_types.typecode_object, 0 );
 90      rtype.endcreate();
 91      return odciconst.success;
 92    exception
 93      when others then
 94        return odciconst.error;
 95    end;
 96  --
 97    static function ODCITablePrepare( sctx out PivotImpl, ti in sys.ODCITabFuncInfo, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number )
 98    return number
 99    is
100      prec     pls_integer;
101      scale    pls_integer;
102      len      pls_integer;
103      csid     pls_integer;
104      csfrm    pls_integer;
105      elem_typ anytype;
106      aname    varchar2(30);
107      tc       pls_integer;
108    begin
109      tc := ti.RetType.GetAttrElemInfo( 1, prec, scale, len, csid, csfrm, elem_typ, aname );
110  --
111      if instr( p_fmt, '@p@' ) > 0
112      then
113        sctx := PivotImpl( elem_typ, p_stmt, p_fmt, null );
114      else
115        sctx := PivotImpl( elem_typ, p_stmt, '@p@', null );
116      end if;
117      return odciconst.success;
118    end;
119  --
120    static function ODCITableStart( sctx in out PivotImpl, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number )
121    return number
122    is
123      cur         integer;
124      numcols     number;
125      desc_tab    dbms_sql.desc_tab2;
126      t_stmt      varchar2(32767);
127      type_code   pls_integer;
128      prec        pls_integer;
129      scale       pls_integer;
130      len         pls_integer;
131      csid        pls_integer;
132      csfrm       pls_integer;
133      schema_name varchar2(30);
134      type_name   varchar2(30);
135      version     varchar2(30);
136      attr_count  pls_integer;
137      attr_type   anytype;
138      attr_name   varchar2(100);
139      dummy2      integer;
140    begin
141      cur := dbms_sql.open_cursor;
142      dbms_sql.parse( cur, p_stmt, dbms_sql.native );
143      dbms_sql.describe_columns2( cur, numcols, desc_tab );
144      dbms_sql.close_cursor( cur );
145  --
146  for i in 1 .. numcols - 2
147  loop
148        t_stmt := t_stmt || ', "' || desc_tab( i ).col_name || '"';
149  end loop;
150  --
151      type_code := sctx.ret_type.getinfo( prec
152                                        , scale
153                                        , len
154                                        , csid
155                                        , csfrm
156                                        , schema_name
157                                        , type_name
158                                        , version
159                                        , attr_count
160                                        );
161      for i in numcols - 1 .. attr_count
162      loop
163        type_code := sctx.ret_type.getattreleminfo( i
164                                                   , prec
165                                                   , scale
166                                                   , len
167                                                   , csid
168                                                   , csfrm
169                                                   , attr_type
170                                                   , attr_name
171                                                   );
172        t_stmt := t_stmt || replace( ', max( decode( ' || sctx.fmt || ', ''' || attr_name || ''', ' || desc_tab( numcols ).col_name || ' ) )'
173                               , '@p@'
174                           , desc_tab( numcols - 1 ).col_name
175             );
176  end loop;
177  t_stmt := 'select ' || substr( t_stmt, 2 ) || ' from ( ' || sctx.stmt || ' )';
178  for i in 1 .. numcols - 2
179  loop
180    if i = 1
181    then
182          t_stmt := t_stmt || ' group by "' || desc_tab( i ).col_name || '"';
183    else
184          t_stmt := t_stmt || ', "' || desc_tab( i ).col_name || '"';
185    end if;
186  end loop;
187  --
188  dbms_output.put_line( t_stmt );
189      sctx.cur := dbms_sql.open_cursor;
190      dbms_sql.parse( sctx.cur, t_stmt, dbms_sql.native );
191      for i in 1 .. attr_count
192      loop
193        type_code := sctx.ret_type.getattreleminfo( i
194                                                   , prec
195                                                   , scale
196                                                   , len
197                                                   , csid
198                                                   , csfrm
199                                                   , attr_type
200                                                   , attr_name
201                                                   );
202        case type_code
203          when dbms_types.typecode_char          then dbms_sql.define_column( sctx.cur, i, 'x', 32767 );
204          when dbms_types.typecode_varchar2      then dbms_sql.define_column( sctx.cur, i, 'x', 32767 );
205          when dbms_types.typecode_number        then dbms_sql.define_column( sctx.cur, i, cast( null as number ) );
206          when dbms_types.typecode_date          then dbms_sql.define_column( sctx.cur, i, cast( null as date ) );
207          when dbms_types.typecode_urowid        then dbms_sql.define_column( sctx.cur, i, cast( null as urowid ) );
208          when dbms_types.typecode_timestamp     then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp ) );
209          when dbms_types.typecode_timestamp_tz  then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp with time zone ) );
210          when dbms_types.typecode_timestamp_ltz then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp with local time zone ) );
211          when dbms_types.typecode_interval_ym   then dbms_sql.define_column( sctx.cur, i, cast( null as interval year to month ) );
212          when dbms_types.typecode_interval_ds   then dbms_sql.define_column( sctx.cur, i, cast( null as interval day to second ) );
213        end case;
214  end loop;
215      dummy2 := dbms_sql.execute( sctx.cur );
216      return odciconst.success;
217    end;
218  --
219    member function ODCITableFetch( self in out PivotImpl, nrows in number, outset out anydataset )
220    return number
221    is
222      c1_col_type pls_integer;
223      type_code   pls_integer;
224      prec        pls_integer;
225      scale       pls_integer;
226      len         pls_integer;
227      csid        pls_integer;
228      csfrm       pls_integer;
229      schema_name varchar2(30);
230      type_name   varchar2(30);
231      version     varchar2(30);
232      attr_count  pls_integer;
233      attr_type   anytype;
234      attr_name   varchar2(100);
235      v1     varchar2(32767);
236      n1     number;
237      d1     date;
238      ur1    urowid;
239      ids1   interval day to second;
240      iym1   interval year to month;
241      ts1    timestamp;
242      tstz1  timestamp with time zone;
243      tsltz1 timestamp with local time zone;
244    begin
245      outset := null;
246      if nrows < 1
247      then
248  -- is this possible???
249        return odciconst.success;
250      end if;
251  --
252  dbms_output.put_line( 'fetch' );
253      if dbms_sql.fetch_rows( self.cur ) = 0
254      then
255        return odciconst.success;
256      end if;
257  --
258  dbms_output.put_line( 'done' );
259      type_code := self.ret_type.getinfo( prec
260                                        , scale
261                                        , len
262                                        , csid
263                                        , csfrm
264                                        , schema_name
265                                        , type_name
266                                        , version
267                                        , attr_count
268                                        );
269      anydataset.begincreate( dbms_types.typecode_object, self.ret_type, outset );
270      outset.addinstance;
271      outset.piecewise();
272      for i in 1 .. attr_count
273      loop
274        type_code := self.ret_type.getattreleminfo( i
275                                                   , prec
276                                                   , scale
277                                                   , len
278                                                   , csid
279                                                   , csfrm
280                                                   , attr_type
281                                                   , attr_name
282                                                   );
283  dbms_output.put_line( attr_name );
284          case type_code
285            when dbms_types.typecode_char then
286              dbms_sql.column_value( self.cur, i, v1 );
287              outset.setchar( v1 );
288            when dbms_types.typecode_varchar2 then
289              dbms_sql.column_value( self.cur, i, v1 );
290              outset.setvarchar2( v1 );
291            when dbms_types.typecode_number then
292              dbms_sql.column_value( self.cur, i, n1 );
293              outset.setnumber( n1 );
294            when dbms_types.typecode_date then
295              dbms_sql.column_value( self.cur, i, d1 );
296              outset.setdate( d1 );
297            when dbms_types.typecode_urowid then
298              dbms_sql.column_value( self.cur, i, ur1 );
299              outset.seturowid( ur1 );
300            when dbms_types.typecode_interval_ds then
301              dbms_sql.column_value( self.cur, i, ids1 );
302
303      outset.setintervalds( ids1 );
304            when dbms_types.typecode_interval_ym then
305              dbms_sql.column_value( self.cur, i, iym1 );
306              outset.setintervalym( iym1 );
307            when dbms_types.typecode_timestamp then
308              dbms_sql.column_value( self.cur, i, ts1 );
309              outset.settimestamp( ts1 );
310            when dbms_types.typecode_timestamp_tz then
311              dbms_sql.column_value( self.cur, i, tstz1 );
312              outset.settimestamptz( tstz1 );
313            when dbms_types.typecode_timestamp_ltz then
314              dbms_sql.column_value( self.cur, i, tsltz1 );
315              outset.settimestampltz( tsltz1 );
316          end case;
317      end loop;
318      outset.endcreate;
319      return odciconst.success;
320    end;
321  --
322    member function ODCITableClose( self in PivotImpl )
323    return number
324    is
325      c integer;
326    begin
327      c := self.cur;
328      dbms_sql.close_cursor( c );
329      return odciconst.success;
330    end;
331  end;
332  /

Type body created.

SQL>
SQL> create or replace
  2  function pivot( p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number := 0 )
  3  return anydataset pipelined using PivotImpl;
  4  /

Function created.

SQL> CREATE TABLE app_users (
  2      username   VARCHAR2(40),
  3      db_name    VARCHAR2(40)
  4  );

Table created.

SQL>
SQL> insert all
  2  into app_users values ('USER1','DB1')
  3  into app_users values ('USER2','DB1')
  4  into app_users values ('USER3','DB1')
  5  into app_users values ('USER4','DB2')
  6  into app_users values ('USER5','DB2')
  7  select * from dual;

5 rows created.

SQL>
SQL>

SQL> select * from table(pivot('select row_number() over ( partition by db_name order by username) as x, db_name, username from app_users'));

         X DB1                                      DB2
---------- ---------------------------------------- ----------------------------------------
         1 USER1                                    USER4
         2 USER2                                    USER5
         3 USER3
         
复制


文章转载自ASKTOM,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论