【问题标题】:Transpose columns and rows in Firebird 2.5在 Firebird 2.5 中转置列和行
【发布时间】:2018-06-06 15:15:17
【问题描述】:

我在 Firebird(方言 3)中编写了一个程序,它返回如下内容:

 column1  |  column2    |  column3  |   column4  |  ...
----------|-------------|-----------|------------|--------
 1        |  55         |  2.5      |    100€    |  ...

具体的列名并不重要。我这样访问它

SELECT * FROM MY_PROCEDURE(:START_DATE, :END_DATE);

它只返回一行,所以我想我也可以使用 EXECUTE_PROCEDURE 访问它。 现在我想要的是转置返回中的列和行

 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 知识相当平庸,我根本不知道如何在单次选择中获取每一列作为一行。

有人可以帮忙吗?提前致谢。

【问题讨论】:

  • 重写存储过程可能更简单,性能更好。顺便说一句:您使用了多少列?
  • 调用程序 255 次以上来一次又一次地做同样的计算?确实,重写程序。或者制作一个辅助程序,调用这个程序,然后从变量中填充一些转置的 GTT(全局临时表)。
  • 为什么还需要这个程序?它可以处理哪些数据,而您不能将其作为常规的select 而不是 PSQL 代码?

标签: firebird transpose unions firebird2.5


【解决方案1】:

Firebird 本身没有 unpivot 或其他对转置列的内置支持。

“最好”的解决方案,也可能是性能最好的解决方案是重写 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

【讨论】:

  • 其实这听起来很不错。我没有编写初始过程 MY_PROCEDURE。它给了我一起工作。我可以在星期二进行测试,但看起来很有希望。谢谢
  • 我按照你的建议做了。重写该过程以将每个值输出到自己的列中非常容易。现在看起来很尴尬,因为我自己没有想出来。无论如何,谢谢!
猜你喜欢
  • 2022-08-19
  • 1970-01-01
  • 2011-07-07
  • 2018-12-05
  • 2013-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多