用于动态 sql 透视的 Oracle 函数
Posted
技术标签:
【中文标题】用于动态 sql 透视的 Oracle 函数【英文标题】:Oracle function for dynamic sql pivoting 【发布时间】:2015-04-23 09:56:59 【问题描述】:我在article 中发现了这个神奇的功能。该函数将其中一列中的一组值转换为列标题:
CREATE OR REPLACE
type PivotImpl as object
(
ret_type anytype, -- The return type of the table function
stmt varchar2(32767),
fmt varchar2(32767),
cur integer,
static function ODCITableDescribe( rtype out anytype, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number := 0 )
return number,
static function ODCITablePrepare( sctx out PivotImpl, ti in sys.ODCITabFuncInfo, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number := 0 )
return number,
static function ODCITableStart( sctx in out PivotImpl, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number := 0 )
return number,
member function ODCITableFetch( self in out PivotImpl, nrows in number, outset out anydataset )
return number,
member function ODCITableClose( self in PivotImpl )
return number
)
/
create or replace type body PivotImpl as
static function ODCITableDescribe( rtype out anytype, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number )
return number
is
atyp anytype;
cur integer;
numcols number;
desc_tab dbms_sql.desc_tab2;
rc s
ys_refcursor;
t_c2 varchar2(32767);
t_fmt varchar2(1000);
begin
cur := dbms_sql.open_cursor;
dbms_sql.parse( cur, p_stmt, dbms_sql.native );
dbms_sql.describe_columns2( cur, numcols, desc_tab );
dbms_sql.close_cursor( cur );
--
anytype.begincreate( dbms_types.typecode_object, atyp );
for i in 1 .. numcols - 2
loop
atyp.addattr( desc_tab( i ).col_name
, case desc_tab( i ).col_type
when 1 then dbms_types.typecode_varchar2
when 2 then dbms_types.typecode_number
when 9 then dbms_types.typecode_varchar2
when 11 then dbms_types.typecode_varchar2 -- show rowid as varchar2
when 12 then dbms_types.typecode_date
when 208 then dbms_types.typecode_varchar2 -- show urowid as varchar2
when 96 then dbms_types.typecode_char
when 180 then dbms_types.typecode_timestamp
when 181 then dbms_types.typecode_timestamp_tz
when 231 then dbms_types.typecode_timestamp_ltz
when 182 then dbms_types.typecode_interval_ym
when 183 then dbms_types.typecode_interval_ds
end
, desc_tab( i ).col_precision
, desc_tab( i ).col_scale
, case desc_tab( i ).col_type
when 11 then 18 -- for rowid col_max_len = 16, and 18 characters are shown
else desc_tab( i ).col_max_len
end
, desc_tab( i ).col_charsetid
, desc_tab( i ).col_charsetform
);
end loop;
if instr( p_fmt, '@p@' ) > 0
then
t_fmt := p_fmt;
else
t_fmt := '@p@';
end if;
open rc for replace( 'select distinct ' || t_fmt || '
from( ' || p_stmt || ' )
order by ' || t_fmt
, '@p@'
, desc_tab( numcols - 1 ).col_name
);
loop
fetch rc into t_c2;
exit when rc%notfound;
atyp.addattr( t_c2
, case desc_tab( numcols ).col_type
when 1 then dbms_types.typecode_varchar2
when 2 then dbms_types.typecode_number
when 9 then dbms_types.typecode_varchar2
when 11 then dbms_types.typecode_varchar2 -- show rowid as varchar2
when 12 then dbms_types.typecode_date
when 208 then dbms_types.typecode_urowid
when 96 then dbms_types.typecode_char
when 180 then dbms_types.typecode_timestamp
when 181 then dbms_types.typecode_timestamp_tz
when 231 then dbms_types.typecode_timestamp_ltz
when 182 then dbms_types.typecode_interval_ym
when 183 then dbms_types.typecode_interval_ds
end
, desc_tab( numcols ).col_precision
, desc_tab( numcols ).col_scale
, case desc_tab( numcols ).col_type
when 11 then 18 -- for rowid col_max_len = 16, and 18 characters are shown
else desc_tab( numcols ).col_max_len
end
, desc_tab( numcols ).col_charsetid
, desc_tab( numcols ).col_charsetform
);
end loop;
close rc;
atyp.endcreate;
anytype.begincreate( dbms_types.typecode_table, rtype );
rtype.SetInfo( null, null, null, null, null, atyp, dbms_types.typecode_object, 0 );
rtype.endcreate();
return odciconst.success;
exception
when others then
return odciconst.error;
end;
--
static function ODCITablePrepare( sctx out PivotImpl, ti in sys.ODCITabFuncInfo, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number )
return number
is
prec pls_integer;
scale pls_integer;
len pls_integer;
csid pls_integer;
csfrm pls_integer;
elem_typ anytype;
aname varchar2(30);
tc pls_integer;
begin
tc := ti.RetType.GetAttrElemInfo( 1, prec, scale, len, csid, csfrm, elem_typ, aname );
--
if instr( p_fmt, '@p@' ) > 0
then
sctx := PivotImpl( elem_typ, p_stmt, p_fmt, null );
else
sctx := PivotImpl( elem_typ, p_stmt, '@p@', null );
end if;
return odciconst.success;
end;
--
static function ODCITableStart( sctx in out PivotImpl, p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number )
return number
is
cur integer;
numcols number;
desc_tab dbms_sql.desc_tab2;
t_stmt varchar2(32767);
type_code pls_integer;
prec pls_integer;
scale pls_integer;
len pls_integer;
csid pls_integer;
csfrm pls_integer;
schema_name varchar2(30);
type_name varchar2(30);
version varchar2(30);
attr_count pls_integer;
attr_type anytype;
attr_name varchar2(100);
dummy2 integer;
begin
cur := dbms_sql.open_cursor;
dbms_sql.parse( cur, p_stmt, dbms_sql.native );
dbms_sql.describe_columns2( cur, numcols, desc_tab );
dbms_sql.close_cursor( cur );
--
for i in 1 .. numcols - 2
loop
t_stmt := t_stmt || ', "' || desc_tab( i ).col_name || '"';
end loop;
--
type_code := sctx.ret_type.getinfo( prec
, scale
, len
, csid
, csfrm
, schema_name
, type_name
, version
, attr_count
);
for i in numcols - 1 .. attr_count
loop
type_code := sctx.ret_type.getattreleminfo( i
, prec
, scale
, len
, csid
, csfrm
, attr_type
, attr_name
);
t_stmt := t_stmt || replace( ', max( decode( ' || sctx.fmt || ', ''' || attr_name || ''', ' || desc_tab( numcols ).col_name || ' ) )'
, '@p@'
, desc_tab( numcols - 1 ).col_name
);
end loop;
t_stmt := 'select ' || substr( t_stmt, 2 ) || ' from ( ' || sctx.stmt || ' )';
for i in 1 .. numcols - 2
loop
if i = 1
then
t_stmt := t_stmt || ' group by "' || desc_tab( i ).col_name || '"';
else
t_stmt := t_stmt || ', "' || desc_tab( i ).col_name || '"';
end if;
end loop;
--
dbms_output.put_line( t_stmt );
sctx.cur := dbms_sql.open_cursor;
dbms_sql.parse( sctx.cur, t_stmt, dbms_sql.native );
for i in 1 .. attr_count
loop
type_code := sctx.ret_type.getattreleminfo( i
, prec
, scale
, len
, csid
, csfrm
, attr_type
, attr_name
);
case type_code
when dbms_types.typecode_char then dbms_sql.define_column( sctx.cur, i, 'x', 32767 );
when dbms_types.typecode_varchar2 then dbms_sql.define_column( sctx.cur, i, 'x', 32767 );
when dbms_types.typecode_number then dbms_sql.define_column( sctx.cur, i, cast( null as number ) );
when dbms_types.typecode_date then dbms_sql.define_column( sctx.cur, i, cast( null as date ) );
when dbms_types.typecode_urowid then dbms_sql.define_column( sctx.cur, i, cast( null as urowid ) );
when dbms_types.typecode_timestamp then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp ) );
when dbms_types.typecode_timestamp_tz then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp with time zone ) );
when dbms_types.typecode_timestamp_ltz then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp with local time zone ) );
when dbms_types.typecode_interval_ym then dbms_sql.define_column( sctx.cur, i, cast( null as interval year to month ) );
when dbms_types.typecode_interval_ds then dbms_sql.define_column( sctx.cur, i, cast( null as interval day to second ) );
end case;
end loop;
dummy2 := dbms_sql.execute( sctx.cur );
return odciconst.success;
end;
--
member function ODCITableFetch( self in out PivotImpl, nrows in number, outset out anydataset )
return number
is
c1_col_type pls_integer;
type_code pls_integer;
prec pls_integer;
scale pls_integer;
len pls_integer;
csid pls_integer;
csfrm pls_integer;
schema_name varchar2(30);
type_name varchar2(30);
version varchar2(30);
attr_count pls_integer;
attr_type anytype;
attr_name varchar2(100);
v1 varchar2(32767);
n1 number;
d1 date;
ur1 urowid;
ids1 interval day to second;
iym1 interval year to month;
ts1 timestamp;
tstz1 timestamp with time zone;
tsltz1 timestamp with local time zone;
begin
outset := null;
if nrows < 1
then
-- is this possible???
return odciconst.success;
end if;
--
dbms_output.put_line( 'fetch' );
if dbms_sql.fetch_rows( self.cur ) = 0
then
return odciconst.success;
end if;
--
dbms_output.put_line( 'done' );
type_code := self.ret_type.getinfo( prec
, scale
, len
, csid
, csfrm
, schema_name
, type_name
, version
, attr_count
);
anydataset.begincreate( dbms_types.typecode_object, self.ret_type, outset );
outset.addinstance;
outset.piecewise();
for i in 1 .. attr_count
loop
type_code := self.ret_type.getattreleminfo( i
, prec
, scale
, len
, csid
, csfrm
, attr_type
, attr_name
);
dbms_output.put_line( attr_name );
case type_code
when dbms_types.typecode_char then
dbms_sql.column_value( self.cur, i, v1 );
outset.setchar( v1 );
when dbms_types.typecode_varchar2 then
dbms_sql.column_value( self.cur, i, v1 );
outset.setvarchar2( v1 );
when dbms_types.typecode_number then
dbms_sql.column_value( self.cur, i, n1 );
outset.setnumber( n1 );
when dbms_types.typecode_date then
dbms_sql.column_value( self.cur, i, d1 );
outset.setdate( d1 );
when dbms_types.typecode_urowid then
dbms_sql.column_value( self.cur, i, ur1 );
outset.seturowid( ur1 );
when dbms_types.typecode_interval_ds then
dbms_sql.column_value( self.cur, i, ids1 );
outset.setintervalds( ids1 );
when dbms_types.typecode_interval_ym then
dbms_sql.column_value( self.cur, i, iym1 );
outset.setintervalym( iym1 );
when dbms_types.typecode_timestamp then
dbms_sql.column_value( self.cur, i, ts1 );
outset.settimestamp( ts1 );
when dbms_types.typecode_timestamp_tz then
dbms_sql.column_value( self.cur, i, tstz1 );
outset.settimestamptz( tstz1 );
when dbms_types.typecode_timestamp_ltz then
dbms_sql.column_value( self.cur, i, tsltz1 );
outset.settimestampltz( tsltz1 );
end case;
end loop;
outset.endcreate;
return odciconst.success;
end;
--
member function ODCITableClose( self in PivotImpl )
return number
is
c integer;
begin
c := self.cur;
dbms_sql.close_cursor( c );
return odciconst.success;
end;
end;
/
create or replace
function pivot( p_stmt in varchar2, p_fmt in varchar2 := 'upper(@p@)', dummy in number := 0 )
return anydataset pipelined using PivotImpl;
/
不幸的是,该函数不对列进行排序:我的选择查询的结果是:
ID NAME EDITS DAYS
1 Example Edt1 10
1 Example Edt2 5
1 Example Edt3 3
当我使用pivot
函数时,我失去了订单:
Select * from table (PIVOT('My select query'));
ID NAME Edt2 Edt1 Edt3
1 Example 5 10 3
你能帮我编辑这个函数以保持列的顺序与初始列的值一样吗?谢谢。
更新:我的选择查询:
SELECT ID_DEMAND
, NAME
, EDIT
, LEAD (EDIT_START) OVER (PARTITION BY ID_DEMAND ORDER BY ORDER_EDIT) - EDIT_START AS TOTAL
FROM (
SELECT
ID_DEMAND
, NAME
, EDIT
, ORDER_EDIT
, MIN (DATE_ACTION) as EDIT_START
FROM
(SELECT
D.ID_DEMAND
, J.NAME
, L.DATE_ACTION
, L.EDIT
, L.ORDER_EDIT
FROM (DEMAND D LEFT JOIN LOG L ON D.ID_DEMAND = L.ID_DEMAND)
LEFT JOIN JOB J ON D.ID_JOB = J.ID_JOB )T
GROUP BY
ID_DEMAND
, NAME
, EDIT
, ORDER_EDIT)
JHERE ID_DEMAND = 1601
ORDER BY ID_DEMAND, ORDER_EDIT
【问题讨论】:
你如何定义列的顺序;从 EDITS 值的 varchar2 排序顺序?通过快速测试,我按该顺序获得列。您可以添加表和数据设置来重新创建问题吗? 顺序是使用列定义的,每个编辑都有其预定义的顺序(我没有使用编辑的值)。刚刚用选择查询更新了我的问题。 我的意思是为什么你认为你得到的顺序是错误的。EDIT
的实际值是多少-您在问题中显示的内容,更多样的不同情况?除非您传递了 p_fmt
参数,否则我认为您不会得到那些确切的数据透视表头,这让我认为您已经编辑了这些值 - 可能隐藏了问题......
是的,编辑是不同的,每个需求都有一组预定义的编辑(更复杂的名称..这就是我在示例中使用虚拟名称的原因^^)
但是我们看不到这些值,所以我们不知道枢轴当前放置它们的顺序,以及您期望它们的顺序。代码按EDITS
排序价值;好吧,默认情况下是upper(EDITS)
。您可能会想出一个p_fmt
来生成您想要的订单,或者更改您的查询,但我们没有足够的信息来告诉您如何(或者如果可能的话)。
【参考方案1】:
您正在使用的函数会忽略您使用 order by
子句设置的值的顺序,并根据结果集中的排序值基于默认的第二个参数来设置透视列名称及其顺序将pivot
保留为upper(@p)
,稍后将转换为upper(EDIT)
。似乎没有一种简单的方法来获得您想要的结果,因为该排序必须基于倒数第二个 EDIT
字段 - 它可以保留为小写或以其他方式操作,但不是一种方式做你想做的事。
您可以修改实现以要求和使用附加排序列,有效地将pivot
函数定义转换为:
create or replace
function pivot( p_stmt in varchar2,
p_fmt in varchar2 := 'upper(@p@)',
p_ord in varchar2 := '@o@', -- added
dummy in number := 0 )
return anydataset pipelined using PivotImpl;
/
这有一个额外的排序占位符,本着现有占位符的精神,我默认为'@o@'
,并且可能还允许进行一些操作。这必须反映在所有PivotImpl
函数声明中,然后必须在提取列值并决定它们的顺序的游标中使用。我已经这样做了:
open rc for replace( replace( 'select ' || t_fmt || '
from( ' || p_stmt || ' )
group by ' || t_fmt || '
order by min(' || t_ord || ')'
, '@p@'
, desc_tab( numcols - 1 ).col_name
)
, '@o@'
, desc_tab( numcols - 2 ).col_name
);
这意味着您的ORDER_EDIT
值必须包含在选择列表中,并且必须位于EDIT
之前的列位置,方法是我修改了列号处理方式;但也意味着您现有的order by
子句是多余的。
设置一些非常简单的虚拟数据:
create table job (id_job number, name varchar2(10));
create table demand (id_demand number, id_job number);
create table log (id_demand number, edit varchar2(4),
order_edit number, date_action date);
insert into job values (42, 'Name');
insert into demand values (1601, 42);
insert into log values (1601, 'EdtC', 1, sysdate);
insert into log values (1601, 'EdtA', 2, sysdate);
insert into log values (1601, 'EdtB', 3, sysdate);
你的内部查询得到:
ID_DEMAND NAME EDIT TOTAL
---------- ---------- ---- ----------
1601 Name EdtC 0
1601 Name EdtA 0
1601 Name EdtB
原始实现得到:
ID_DEMAND NAME EDTA EDTB EDTC
---------- ---------- ---------- ---------- ----------
1601 Name 0 0
这个修改后的实现调用:
Select * from table (PIVOT('SELECT ID_DEMAND
, NAME
, ORDER_EDIT
, EDIT
, LEAD (EDIT_START) OVER (PARTITION BY ID_DEMAND ORDER BY ORDER_EDIT) - EDIT_START AS TOTAL
FROM (
SELECT
ID_DEMAND
, NAME
, EDIT
, ORDER_EDIT
, MIN (DATE_ACTION) as EDIT_START
FROM
(SELECT
D.ID_DEMAND
, J.NAME
, L.DATE_ACTION
, L.EDIT
, L.ORDER_EDIT
FROM (DEMAND D LEFT JOIN LOG L ON D.ID_DEMAND = L.ID_DEMAND)
LEFT JOIN JOB J ON D.ID_JOB = J.ID_JOB )T
GROUP BY
ID_DEMAND
, NAME
, EDIT
, ORDER_EDIT)
WHERE ID_DEMAND = 1601'));
...ORDER_EDIT
包含在选择列表中,因此结果集现在看起来像:
ID_DEMAND NAME ORDER_EDIT EDIT TOTAL
---------- ---------- ---------- ---- ----------
1601 Name 1 EdtC 0
1601 Name 2 EdtA 0
1601 Name 3 EdtB
修改后的函数调用得到:
ID_DEMAND NAME EDTC EDTA EDTB
---------- ---------- ---------- ---------- ----------
1601 Name 0 0
哪个具有您的标志指定的顺序的透视列,这是我认为您想要实现的。
这是整个修改后的实现,对于 SQL Fiddle 来说太大了。我已经用-- added
和-- changed
cmets 标记了我更改的位:
CREATE OR REPLACE
type PivotImpl as object
(
ret_type anytype, -- The return type of the table function
stmt varchar2(32767),
fmt varchar2(32767),
ord varchar2(32767), -- added
cur integer,
static function ODCITableDescribe( rtype out anytype,
p_stmt in varchar2,
p_fmt in varchar2 := 'upper(@p@)',
p_ord in varchar2 := '@o@', -- added
dummy in number := 0 )
return number,
static function ODCITablePrepare( sctx out PivotImpl,
ti in sys.ODCITabFuncInfo,
p_stmt in varchar2,
p_fmt in varchar2 := 'upper(@p@)',
p_ord in varchar2 := '@o@', -- added
dummy in number := 0 )
return number,
static function ODCITableStart( sctx in out PivotImpl,
p_stmt in varchar2,
p_fmt in varchar2 := 'upper(@p@)',
p_ord in varchar2 := '@o@', -- added
dummy in number := 0 )
return number,
member function ODCITableFetch( self in out PivotImpl,
nrows in number,
outset out anydataset )
return number,
member function ODCITableClose( self in PivotImpl )
return number
)
/
create or replace type body PivotImpl as
static function ODCITableDescribe( rtype out anytype,
p_stmt in varchar2,
p_fmt in varchar2 := 'upper(@p@)',
p_ord in varchar2 := '@o@', -- added
dummy in number )
return number
is
atyp anytype;
cur integer;
numcols number;
desc_tab dbms_sql.desc_tab2;
rc sys_refcursor;
t_c2 varchar2(32767);
t_fmt varchar2(1000);
t_ord varchar2(1000); -- added
begin
cur := dbms_sql.open_cursor;
dbms_sql.parse( cur, p_stmt, dbms_sql.native );
dbms_sql.describe_columns2( cur, numcols, desc_tab );
dbms_sql.close_cursor( cur );
--
anytype.begincreate( dbms_types.typecode_object, atyp );
for i in 1 .. numcols - 3 -- changed
loop
atyp.addattr( desc_tab( i ).col_name
, case desc_tab( i ).col_type
when 1 then dbms_types.typecode_varchar2
when 2 then dbms_types.typecode_number
when 9 then dbms_types.typecode_varchar2
when 11 then dbms_types.typecode_varchar2 -- show rowid as varchar2
when 12 then dbms_types.typecode_date
when 208 then dbms_types.typecode_varchar2 -- show urowid as varchar2
when 96 then dbms_types.typecode_char
when 180 then dbms_types.typecode_timestamp
when 181 then dbms_types.typecode_timestamp_tz
when 231 then dbms_types.typecode_timestamp_ltz
when 182 then dbms_types.typecode_interval_ym
when 183 then dbms_types.typecode_interval_ds
end
, desc_tab( i ).col_precision
, desc_tab( i ).col_scale
, case desc_tab( i ).col_type
when 11 then 18 -- for rowid col_max_len = 16, and 18 characters are shown
else desc_tab( i ).col_max_len
end
, desc_tab( i ).col_charsetid
, desc_tab( i ).col_charsetform
);
end loop;
if instr( p_fmt, '@p@' ) > 0
then
t_fmt := p_fmt;
else
t_fmt := '@p@';
end if;
-- added, but not really implemented
if instr( p_ord, '@o@' ) > 0
then
t_ord := p_ord;
else
t_ord := '@o@';
end if;
-- changed cursor/replace to handle ordering
open rc for replace( replace( 'select ' || t_fmt || '
from( ' || p_stmt || ' )
group by ' || t_fmt || '
order by min(' || t_ord || ')'
, '@p@'
, desc_tab( numcols - 1 ).col_name
)
, '@o@'
, desc_tab( numcols - 2 ).col_name
);
loop
fetch rc into t_c2;
exit when rc%notfound;
atyp.addattr( t_c2
, case desc_tab( numcols ).col_type
when 1 then dbms_types.typecode_varchar2
when 2 then dbms_types.typecode_number
when 9 then dbms_types.typecode_varchar2
when 11 then dbms_types.typecode_varchar2 -- show rowid as varchar2
when 12 then dbms_types.typecode_date
when 208 then dbms_types.typecode_urowid
when 96 then dbms_types.typecode_char
when 180 then dbms_types.typecode_timestamp
when 181 then dbms_types.typecode_timestamp_tz
when 231 then dbms_types.typecode_timestamp_ltz
when 182 then dbms_types.typecode_interval_ym
when 183 then dbms_types.typecode_interval_ds
end
, desc_tab( numcols ).col_precision
, desc_tab( numcols ).col_scale
, case desc_tab( numcols ).col_type
when 11 then 18 -- for rowid col_max_len = 16, and 18 characters are shown
else desc_tab( numcols ).col_max_len
end
, desc_tab( numcols ).col_charsetid
, desc_tab( numcols ).col_charsetform
);
end loop;
close rc;
atyp.endcreate;
anytype.begincreate( dbms_types.typecode_table, rtype );
rtype.SetInfo( null, null, null, null, null, atyp, dbms_types.typecode_object, 0 );
rtype.endcreate();
return odciconst.success;
exception
when others then
return odciconst.error;
end;
--
static function ODCITablePrepare( sctx out PivotImpl,
ti in sys.ODCITabFuncInfo,
p_stmt in varchar2,
p_fmt in varchar2 := 'upper(@p@)',
p_ord in varchar2 := '@o@', -- added
dummy in number )
return number
is
prec pls_integer;
scale pls_integer;
len pls_integer;
csid pls_integer;
csfrm pls_integer;
elem_typ anytype;
aname varchar2(30);
tc pls_integer;
begin
tc := ti.RetType.GetAttrElemInfo( 1, prec, scale, len, csid, csfrm, elem_typ, aname );
--
if instr( p_fmt, '@p@' ) > 0
then
sctx := PivotImpl( elem_typ, p_stmt, p_fmt, p_ord, null ); -- changed
else
sctx := PivotImpl( elem_typ, p_stmt, '@p@', '@o@', null ); -- changed
end if;
return odciconst.success;
end;
--
static function ODCITableStart( sctx in out PivotImpl,
p_stmt in varchar2,
p_fmt in varchar2 := 'upper(@p@)',
p_ord in varchar2 := '@o@', -- added
dummy in number )
return number
is
cur integer;
numcols number;
desc_tab dbms_sql.desc_tab2;
t_stmt varchar2(32767);
type_code pls_integer;
prec pls_integer;
scale pls_integer;
len pls_integer;
csid pls_integer;
csfrm pls_integer;
schema_name varchar2(30);
type_name varchar2(30);
version varchar2(30);
attr_count pls_integer;
attr_type anytype;
attr_name varchar2(100);
dummy2 integer;
begin
cur := dbms_sql.open_cursor;
dbms_sql.parse( cur, p_stmt, dbms_sql.native );
dbms_sql.describe_columns2( cur, numcols, desc_tab );
dbms_sql.close_cursor( cur );
--
for i in 1 .. numcols - 3 -- changed
loop
t_stmt := t_stmt || ', "' || desc_tab( i ).col_name || '"';
end loop;
--
dbms_output.put_line(t_stmt);
type_code := sctx.ret_type.getinfo( prec
, scale
, len
, csid
, csfrm
, schema_name
, type_name
, version
, attr_count
);
for i in numcols - 2 .. attr_count -- changed
loop
type_code := sctx.ret_type.getattreleminfo( i
, prec
, scale
, len
, csid
, csfrm
, attr_type
, attr_name
);
t_stmt := t_stmt || replace( ', max( decode( ' || sctx.fmt || ', ''' || attr_name || ''', ' || desc_tab( numcols ).col_name || ' ) )'
, '@p@'
, desc_tab( numcols - 1 ).col_name
);
end loop;
t_stmt := 'select ' || substr( t_stmt, 2 ) || ' from ( ' || sctx.stmt || ' )';
for i in 1 .. numcols - 3 -- changed
loop
if i = 1
then
t_stmt := t_stmt || ' group by "' || desc_tab( i ).col_name || '"';
else
t_stmt := t_stmt || ', "' || desc_tab( i ).col_name || '"';
end if;
end loop;
--
sctx.cur := dbms_sql.open_cursor;
dbms_sql.parse( sctx.cur, t_stmt, dbms_sql.native );
for i in 1 .. attr_count
loop
type_code := sctx.ret_type.getattreleminfo( i
, prec
, scale
, len
, csid
, csfrm
, attr_type
, attr_name
);
case type_code
when dbms_types.typecode_char then dbms_sql.define_column( sctx.cur, i, 'x', 32767 );
when dbms_types.typecode_varchar2 then dbms_sql.define_column( sctx.cur, i, 'x', 32767 );
when dbms_types.typecode_number then dbms_sql.define_column( sctx.cur, i, cast( null as number ) );
when dbms_types.typecode_date then dbms_sql.define_column( sctx.cur, i, cast( null as date ) );
when dbms_types.typecode_urowid then dbms_sql.define_column( sctx.cur, i, cast( null as urowid ) );
when dbms_types.typecode_timestamp then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp ) );
when dbms_types.typecode_timestamp_tz then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp with time zone ) );
when dbms_types.typecode_timestamp_ltz then dbms_sql.define_column( sctx.cur, i, cast( null as timestamp with local time zone ) );
when dbms_types.typecode_interval_ym then dbms_sql.define_column( sctx.cur, i, cast( null as interval year to month ) );
when dbms_types.typecode_interval_ds then dbms_sql.define_column( sctx.cur, i, cast( null as interval day to second ) );
end case;
end loop;
dummy2 := dbms_sql.execute( sctx.cur );
return odciconst.success;
end;
--
member function ODCITableFetch( self in out PivotImpl,
nrows in number,
outset out anydataset )
return number
is
c1_col_type pls_integer;
type_code pls_integer;
prec pls_integer;
scale pls_integer;
len pls_integer;
csid pls_integer;
csfrm pls_integer;
schema_name varchar2(30);
type_name varchar2(30);
version varchar2(30);
attr_count pls_integer;
attr_type anytype;
attr_name varchar2(100);
v1 varchar2(32767);
n1 number;
d1 date;
ur1 urowid;
ids1 interval day to second;
iym1 interval year to month;
ts1 timestamp;
tstz1 timestamp with time zone;
tsltz1 timestamp with local time zone;
begin
outset := null;
if nrows < 1
then
-- is this possible???
return odciconst.success;
end if;
--
dbms_output.put_line( 'fetch' );
if dbms_sql.fetch_rows( self.cur ) = 0
then
return odciconst.success;
end if;
--
dbms_output.put_line( 'done' );
type_code := self.ret_type.getinfo( prec
, scale
, len
, csid
, csfrm
, schema_name
, type_name
, version
, attr_count
);
anydataset.begincreate( dbms_types.typecode_object, self.ret_type, outset );
outset.addinstance;
outset.piecewise();
for i in 1 .. attr_count
loop
type_code := self.ret_type.getattreleminfo( i
, prec
, scale
, len
, csid
, csfrm
, attr_type
, attr_name
);
dbms_output.put_line( attr_name );
case type_code
when dbms_types.typecode_char then
dbms_sql.column_value( self.cur, i, v1 );
outset.setchar( v1 );
when dbms_types.typecode_varchar2 then
dbms_sql.column_value( self.cur, i, v1 );
outset.setvarchar2( v1 );
when dbms_types.typecode_number then
dbms_sql.column_value( self.cur, i, n1 );
outset.setnumber( n1 );
when dbms_types.typecode_date then
dbms_sql.column_value( self.cur, i, d1 );
outset.setdate( d1 );
when dbms_types.typecode_urowid then
dbms_sql.column_value( self.cur, i, ur1 );
outset.seturowid( ur1 );
when dbms_types.typecode_interval_ds then
dbms_sql.column_value( self.cur, i, ids1 );
outset.setintervalds( ids1 );
when dbms_types.typecode_interval_ym then
dbms_sql.column_value( self.cur, i, iym1 );
outset.setintervalym( iym1 );
when dbms_types.typecode_timestamp then
dbms_sql.column_value( self.cur, i, ts1 );
outset.settimestamp( ts1 );
when dbms_types.typecode_timestamp_tz then
dbms_sql.column_value( self.cur, i, tstz1 );
outset.settimestamptz( tstz1 );
when dbms_types.typecode_timestamp_ltz then
dbms_sql.column_value( self.cur, i, tsltz1 );
outset.settimestampltz( tsltz1 );
end case;
end loop;
outset.endcreate;
return odciconst.success;
end;
--
member function ODCITableClose( self in PivotImpl )
return number
is
c integer;
begin
c := self.cur;
dbms_sql.close_cursor( c );
return odciconst.success;
end;
end;
/
create or replace
function pivot( p_stmt in varchar2,
p_fmt in varchar2 := 'upper(@p@)',
p_ord in varchar2 := '@o@', -- added
dummy in number := 0 )
return anydataset pipelined using PivotImpl;
/
【讨论】:
【参考方案2】:我正在使用相同的枢轴并遇到相同的问题。您提到的动态透视按字母顺序排列透视列。我发现的 hacky 解决方法是使用 case 语句添加字符 像
SELECT
ID_DEMAND
, NAME
, CASE
WHEN EDIT < 'EDIT2' THEN ' EDIT2'
WHEN EDIT > 'EDIT4' THEN '_EDIT4'
ELSE EDIT
END AS EDIT,
, ORDER_EDIT
, MIN (DATE_ACTION) as EDIT_START
这缺乏复杂性和风格,但可以完成工作。
【讨论】:
问题是我的编辑没有预定义的值(名称可能不同,可以从专用表中更改)所以我无法比较:/ @RidRoid - 那么你怎么知道你想要它们的顺序?是否在专用表中定义,例如带有序列/优先级列,您可以加入并在案例中使用? @AlexPoole 在我最初的select
query 中有ORDER BY ID_DEMAND, ORDER_EDIT
.. 但是一旦我将查询作为参数传递,订单就会丢失。【参考方案3】:
如果您不关心列名,可以作为代码修改的替代方法...
ROWNUM 将给出我认为与原始顺序相同的顺序。
由于文本排序,ROWNUM 无法自行正确排序。
IE: 1,10,11,12,2,20,21,22
如果你不关心列名,那么
用 0 前缀或填充数字允许“正常”排序行为。
IE 001,002,003,004,005,006,007
或
001 列名 1,002 列名 2, 003 列名 3
如果使用连接在一定程度上保持原始列名
注意: to_number 似乎不起作用,因为我相信在内部这会被按摩回文本。
【讨论】:
以上是关于用于动态 sql 透视的 Oracle 函数的主要内容,如果未能解决你的问题,请参考以下文章
使用 Sql Developer Oracle 的动态数据透视查询