汇总行中的唯一列

Summarize unique column in row

我是 Firebird 的新手,需要您的帮助。

我有一个具有以下减少输出的存储过程:

Player Team Number
Reus Ahlen 18
Lewandowski Posen 19
Reus MG 11
Reus BVB 11
Lewandowski BVB 9
Haaland BVB 9

我想总结一下球员,把Team&Number改成一个新的列。

输出应该是:

Player Station 1 Station 2 Station 3
Reus Ahlen 18 MG 11 BVB 11
Lewandowski Posen 19 BVB 9
Haaland BVB 9

我正在使用 Firebird 2.5.8

对于此答案的其余部分,我使用以下设置:

create table player (
  player varchar(100) not null,
  team varchar(100) not null,
  number smallint not null
);

commit;

insert into player (player, team, number) values ('Reus', 'Ahlen', 18);
insert into player (player, team, number) values ('Lewandowski', 'Posen', 19);
insert into player (player, team, number) values ('Reus', 'MG', 11);
insert into player (player, team, number) values ('Reus', 'BVB', 11);
insert into player (player, team, number) values ('Lewandowski', 'BVB', 9);
insert into player (player, team, number) values ('Haaland', 'BVB', 9);

commit;

示例假定玩家名称足以唯一标识玩家。

由于 Firebird 的 DSQL 方言的性质和查询执行的实现,'station' 列的数量是固定的。在此示例中,我使用了三个,就像您的问题一样,但如果需要,可以将其扩展到更多列。在 Firebird 2.5 中,生成所需的枢轴 table 很混乱,并且可能不会很好地执行。

一个基本的、纯粹的 SQL 解决方案,类似于:

with unique_players as (
  select distinct player from player
)
select 
  player,
  (select team || ' ' || number 
   from player 
   where player = unique_players.player 
   order by number desc
   rows 1) as station_1,
  (select team || ' ' || number 
   from player 
   where player = unique_players.player 
   order by number desc
   rows 2 to 2) as station_2,
  (select team || ' ' || number 
   from player 
   where player = unique_players.player 
   order by number desc
   rows 3 to 3) as station_3
from unique_players;

在这个例子中,我们确定唯一玩家,然后select第一,第二和第三站使用子查询使用rows (you can also use first/skip, or, in Firebird 3.0 and higher, offset/fetch)

另一种可能性能更好的方法是使用存储过程或执行块,但代码确实变得更复杂。

execute block 
  returns (
    player type of column player.player, 
    station_1 varchar(120), 
    station_2 varchar(120), 
    station_3 varchar(120)
  )
as
  declare station smallint = 0;
  declare current_player type of column player.player;
  declare current_team type of column player.team;
  declare current_number type of column player.number;
begin
  for select player, team, number 
      from player 
      order by player, number desc
      into current_player, current_team, current_number
  do
  begin
      if (current_player is distinct from player) then
      begin
         -- output row when player changes
         if (player is not null) then suspend;
         station = 1;
         player = current_player;
         station_1 = current_team || ' ' || current_number;
      end
      else
      begin
        station = station + 1;
        if (station = 2) then
        begin
          station_2 = current_team || ' ' || current_number;
        end
        else if (station = 3) then
        begin
          station_3 = current_team || ' ' || current_number;
        end
        -- rows for station > 3 are ignored
      end
  end
  -- output final player
  if (player is not null) then suspend;
end

这会遍历行,填充执行块的输出列,并在找到下一个玩家时输出它们。

另一方面,在 Firebird 4.0 中,您可以执行以下操作:

使用NTH_VALUEwindow函数:

select player, station_1, station_2, station_3
from (
  select 
    player, 
    row_number() over player_order as rownum,
    nth_value(team || number, 1) over player_order as station_1,
    nth_value(team || number, 2) over player_order as station_2,
    nth_value(team || number, 3) over player_order as station_3
  from player
  window player_order as (
    partition by player 
    order by number desc 
    rows between unbounded preceding and unbounded following)
)
where rownum = 1

或使用filtered aggregate functions:

with player_stations as (
  select 
    player, 
    team || ' ' || number as station, 
    row_number() over (partition by player order by number desc) as rownum
  from player
)
select 
  player, 
  max(station) filter (where rownum = 1) as station_1,
  max(station) filter (where rownum = 2) as station_2,
  max(station) filter (where rownum = 3) as station_3
from player_stations
group by player

使用 lateral join:

with player_stations as (
  select player, number, team || ' ' || number as station from player
)
select 
  player, 
  st1.station as station_1, 
  st2.station as station_2, 
  st3.station as station_3
from (select distinct player from player) p
left join lateral (
  select station
  from player_stations
  where player = p.player
  order by number desc
  fetch first row only
) st1 on true
left join lateral (
  select station
  from player_stations
  where player = p.player
  order by number desc
  offset 1 row
  fetch next row only
) st2 on true
left join lateral (
  select station
  from player_stations
  where player = p.player
  order by number desc
  offset 2 rows
  fetch next row only
) st3 on true

这与第一个示例类似,但将站的 selection 向下推入横向连接(Firebird 4.0 中引入的功能)。