在 Firebird 2.5 中转置列和行
Transpose columns and rows in Firebird 2.5
我用 Firebird(方言 3)写了一个程序,return我是这样的:
column1 | column2 | column3 | column4 | ...
----------|-------------|-----------|------------|--------
1 | 55 | 2.5 | 100€ | ...
具体的列名并不重要。我是这样访问的
SELECT * FROM MY_PROCEDURE(:START_DATE, :END_DATE);
它只有 return 一行,所以我想我也可以使用 EXECUTE_PROCEDURE 访问它。
现在我想要的是 t运行spose return
中的列和行
row | result
----------|---------
column1 | 1
column2 | 55
column3 | 2.0
column4 | 100€
... | ...
我最初做的是这样想的:
select 'column1' AS row, column1 AS result
FROM MY_PROCEDURE(:START_DATE, :END_DATE)
union all
select 'column2' AS row, column2 AS result
FROM MY_PROCEDURE(:START_DATE, :END_DATE)
union all
...
基本上每一列一个查询。有效。然而,最终我运行陷入了这个问题:
Dynamic SQL Error
Too many Contexts of Relation/Procedure/Views. Maxium allowed is 255.
所以我需要重组我的脚本。如您所见,我的 SQL 知识非常平庸,我根本不知道如何在单个 select.
中将每一列作为一行获取
有人能帮忙吗?提前致谢。
Firebird 本身没有 unpivot
或其他对转置列的内置支持。
'best' 解决方案,可能是性能最好的解决方案是重写 MY_PROCEDURE
(或编写替代版本)以输出转置的行。
例如,假设您的存储过程执行如下操作:
set term #;
create procedure test_1
returns (id integer, column1 double precision, column2 double precision, column3 double precision)
as
begin
for
select id, column1, column2, column3
from sometable
into :id, :column1, :column2, :column3 do
begin
suspend;
end
end#
set term ;#
然后您可以通过手动将值转换为单独的暂停来重写它:
set term #;
create procedure test_2
returns (id integer, columnname varchar(100), columnvalue double precision)
as
declare column1 double precision;
declare column2 double precision;
declare column3 double precision;
begin
for
select id, column1, column2, column3
from sometable
into :id, :column1, :column2, :column3 do
begin
columnname = 'column1';
columnvalue = column1;
suspend;
columnname = 'column2';
columnvalue = column2;
suspend;
columnname = 'column3';
columnvalue = column3;
suspend;
end
end#
set term ;#
这将输出类似
的内容
id columnname columnvalue
1 column1 1.0
1 column2 1.5
1 column3 5.0
2 ...etc
此解决方案确实要求所有输出 (columnvalue
) 具有相同的类型。否则,您将需要转换为通用数据类型。
或者,您可以使用 for select * from test_1 into ...
将第一个过程链接到第二个过程。根据存储过程的内部结构,这可能或多或少有效:
set term #;
create procedure test_3
returns (id integer, columnname varchar(100), columnvalue double precision)
as
declare column1 double precision;
declare column2 double precision;
declare column3 double precision;
begin
for
select id, column1, column2, column3 from test_1
into :id, :column1, :column2, :column3 do
begin
columnname = 'column1';
columnvalue = column1;
suspend;
columnname = 'column2';
columnvalue = column2;
suspend;
columnname = 'column3';
columnvalue = column3;
suspend;
end
end#
set term ;#
如果您需要输出的两种变体,那么最后一个选项可能是最好的,因为这意味着您将只有一个位置用于该存储过程的逻辑。
对于临时查询,您还可以将存储过程替换为具有相同代码的 execute block
。
我用 Firebird(方言 3)写了一个程序,return我是这样的:
column1 | column2 | column3 | column4 | ...
----------|-------------|-----------|------------|--------
1 | 55 | 2.5 | 100€ | ...
具体的列名并不重要。我是这样访问的
SELECT * FROM MY_PROCEDURE(:START_DATE, :END_DATE);
它只有 return 一行,所以我想我也可以使用 EXECUTE_PROCEDURE 访问它。 现在我想要的是 t运行spose return
中的列和行 row | result
----------|---------
column1 | 1
column2 | 55
column3 | 2.0
column4 | 100€
... | ...
我最初做的是这样想的:
select 'column1' AS row, column1 AS result
FROM MY_PROCEDURE(:START_DATE, :END_DATE)
union all
select 'column2' AS row, column2 AS result
FROM MY_PROCEDURE(:START_DATE, :END_DATE)
union all
...
基本上每一列一个查询。有效。然而,最终我运行陷入了这个问题:
Dynamic SQL Error
Too many Contexts of Relation/Procedure/Views. Maxium allowed is 255.
所以我需要重组我的脚本。如您所见,我的 SQL 知识非常平庸,我根本不知道如何在单个 select.
中将每一列作为一行获取有人能帮忙吗?提前致谢。
Firebird 本身没有 unpivot
或其他对转置列的内置支持。
'best' 解决方案,可能是性能最好的解决方案是重写 MY_PROCEDURE
(或编写替代版本)以输出转置的行。
例如,假设您的存储过程执行如下操作:
set term #;
create procedure test_1
returns (id integer, column1 double precision, column2 double precision, column3 double precision)
as
begin
for
select id, column1, column2, column3
from sometable
into :id, :column1, :column2, :column3 do
begin
suspend;
end
end#
set term ;#
然后您可以通过手动将值转换为单独的暂停来重写它:
set term #;
create procedure test_2
returns (id integer, columnname varchar(100), columnvalue double precision)
as
declare column1 double precision;
declare column2 double precision;
declare column3 double precision;
begin
for
select id, column1, column2, column3
from sometable
into :id, :column1, :column2, :column3 do
begin
columnname = 'column1';
columnvalue = column1;
suspend;
columnname = 'column2';
columnvalue = column2;
suspend;
columnname = 'column3';
columnvalue = column3;
suspend;
end
end#
set term ;#
这将输出类似
的内容id columnname columnvalue
1 column1 1.0
1 column2 1.5
1 column3 5.0
2 ...etc
此解决方案确实要求所有输出 (columnvalue
) 具有相同的类型。否则,您将需要转换为通用数据类型。
或者,您可以使用 for select * from test_1 into ...
将第一个过程链接到第二个过程。根据存储过程的内部结构,这可能或多或少有效:
set term #;
create procedure test_3
returns (id integer, columnname varchar(100), columnvalue double precision)
as
declare column1 double precision;
declare column2 double precision;
declare column3 double precision;
begin
for
select id, column1, column2, column3 from test_1
into :id, :column1, :column2, :column3 do
begin
columnname = 'column1';
columnvalue = column1;
suspend;
columnname = 'column2';
columnvalue = column2;
suspend;
columnname = 'column3';
columnvalue = column3;
suspend;
end
end#
set term ;#
如果您需要输出的两种变体,那么最后一个选项可能是最好的,因为这意味着您将只有一个位置用于该存储过程的逻辑。
对于临时查询,您还可以将存储过程替换为具有相同代码的 execute block
。