1

我在 oracle 中有一个表 MyTable:

MXP_ID         MX_ID    TB_SRC          DESC    COL_LOC
1              MX3      MB_SHEET_ROW    TEST    APS
1              MX1      MB_SHEET_ROW    DEV     APT
1             MX120     MB_SHEET_ROW    PROD    APU
1             MX5       MB_SHEET_ROW    SET     APV
1             MX6       MB_SHEET_ROW    CHECK   APW
1             MX54    MB_SHEET_ROW      WHILE   APX
1             MX14    MB_SHEET_ROW       DO     APY
1              MX2    MB_SHEET_ROW       FOR    APZ

这是一个 excel 表,其中 colloc 字段指定了不同的列:即 excel 表示将是:

APS APT APU     APV APW APX     APY     APZ
MX3 MX1 MX120   MX5 MX6 MX54    MX14    MX2

如果我在 APT 列之后的上述 excel 中插入一个新列,那么在生成的 excel 中,MX120 将转移到 APV 列,依此类推。

APS APT APU      APV    APW APX APY     APZ     AQA
MX3 MX1 MX_NEW  MX120   MX5 MX6 MX54    MX14    MX2

所以,oracle中对应的表会被修改为:MyTable:

MXP_ID  MX_ID   TB_SRC         DESC       COL_LOC
1       MX3    MB_SHEET_ROW     TEST        APS
1       MX1     MB_SHEET_ROW    DEV         APT
1       MX_NEW  MB_SHEET_ROW    NEW_ENTRY   APU
1       MX120   MB_SHEET_ROW    PROD        APV
1       MX5    MB_SHEET_ROW      SET        APW
1       MX6     MB_SHEET_ROW    CHECK       APX
1       MX54    MB_SHEET_ROW    WHILE        APY
1       MX14    MB_SHEET_ROW    DO          APZ
1       MX2    MB_SHEET_ROW     FOR         AQA

如何在 oracle 中自动化此过程,以便插入中间的每一行都会COL_LOC像在 excel 中一样移动到下一列。

我已使用此查询来查找与列位置对应的列号。但我不知道如何更新列位置以指向下一列。

SELECT MXP_ID, MX_ID , TB_SRC , DESC, COL_LOC ,
To_Number(
   case substr(COL_LOC,1,1)

        when 'A' then 1
     when 'B' then 2
     when 'C' then 3
     when 'D' then 4
     when 'E' then 5
     when 'F' then 6
     when 'G' then 7
     when 'H' then 8
     when 'I' then 9
     when 'J' then 10
     when 'K' then 11
     when 'L' then 12
     when 'M' then 13
     when 'N' then 14
     when 'O' then 15
     when 'P' then 16
     when 'Q' then 17
     when 'R' then 18
     when 'S' then 19
     when 'T' then 20
     when 'U' then 21
     when 'V' then 22
     when 'W' then 23
     when 'X' then 24
     when 'Y' then 25
     when 'Z' then 26    

   End  ||

   case substr(COL_LOC,2,1)
     when 'A' then 27
     when 'B' then 28
     when 'C' then 29
     when 'D' then 30
     when 'E' then 31
     when 'F' then 32
     when 'G' then 33
     when 'H' then 34
     when 'I' then 35
     when 'J' then 36
     when 'K' then 37
     when 'L' then 38
     when 'M' then 39
     when 'N' then 40
     when 'O' then 41
     when 'P' then 42
     when 'Q' then 43
     when 'R' then 44
     when 'S' then 45
     when 'T' then 46
     when 'U' then 47
     when 'V' then 48
     when 'W' then 49
     when 'X' then 50
     when 'Y' then 51
     when 'Z' then 52    
   End  ||

   case substr(COL_LOC,3,1)

        when 'A' then 52.3
     when 'B' then 53
     when 'C' then 54
     when 'D' then 55
     when 'E' then 56
     when 'F' then 57
     when 'G' then 58
     when 'H' then 59
     when 'I' then 60
     when 'J' then 61
     when 'K' then 62
     when 'L' then 63
     when 'M' then 64
     when 'N' then 65
     when 'O' then 66
     when 'P' then 67
     when 'Q' then 68
     when 'R' then 69
     when 'S' then 70
     when 'T' then 71
     when 'U' then 72
     when 'V' then 73
     when 'W' then 74
     when 'X' then 75
     when 'Y' then 76
     when 'Z' then 77

   End)  as Column_number
FROM MyTable
order by Column_number;
4

1 回答 1

0

问题的核心似乎是我们需要将一个值从一个数字系统(整数、数字)转换为另一个数字系统(列名、字母),因为我们不能只是“加 1”到一系列列名以“将它们向右移动”,就像在电子表格中一样。为了执行转换,我建议使用 2 个函数。(警告:以下代码只是粗略的 0.01 版本!)

-- take a spreadsheet column name
-- find and return an integer that represents the column's position
-- eg 'A' -> 1, 'Z'-> 26, 'AA' -> 27, 'AMJ' -> 1024
create or replace function alphaToInt( colname varchar2 )
return number
is
  alphabet constant varchar2( 26 ) := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ;
  numberbase number := 26 ;
  lettervalue number := 0 ;
  currentletter varchar2( 1 ) := '' ;
  currentpos number := 0 ;
  posval number := 0 ;
  result number := 0 ;
begin
  for i in 1 .. length( colname )
  loop
    currentletter := substr( colname, i * -1, 1 ) ;
    currentpos := instr( alphabet, currentletter ) ;
    posval := power( numberbase, i - 1 ) ;
    result := result + ( posval * currentpos ) ;
  end loop;
  return result ;
end;
/
-- SQL>  select alphaToInt( 'AA' ) from dual  ;
-- ALPHATOINT('AA')
-- ----------------
--               27

对于反向操作,您可以使用类似...

-- take an integer
-- find and return its corresponding spreadsheet column name
create or replace function intToAlpha( colnumber number )
return varchar2
is
  base number := 26 ;
  remaining number := colnumber ;
  remainder number := 0 ;
  resultstring varchar2( 4000 ) := '' ;
begin
  while remaining > 0 
  loop
    remainder := mod( remaining - 1, base ) ;
    resultstring := chr( 65 + remainder ) || resultstring ; 
    remaining := trunc( ( remaining - remainder ) / base ) ; 
  end loop;
  return resultstring ;
end;
/
-- SQL> select intToAlpha( 1024 ) from dual ;
-- INTTOALPHA(1024)                                                                                                           
-- -----------------
-- AMJ 

测试表

create table mytable (
  mxp_id  number
, mx_id   varchar2( 64 )
, tb_src  varchar2( 64 )
, descr   varchar2( 64 )
, col_loc char(3)
);

insert into mytable( mxp_id, mx_id, tb_src, descr, col_loc)
select 1 as MXP_ID, 'MX3' as MX_ID, 'MB_SHEET_ROW' as TB_SRC 
, 'TEST' as DESCR, 'APS' as COL_LOC from dual union all
select 1, 'MX1',   'MB_SHEET_ROW', 'DEV', 'APT' from dual union all 
select 1, 'MX120', 'MB_SHEET_ROW', 'PROD','APU' from dual union all 
select 1, 'MX5',   'MB_SHEET_ROW', 'SET', 'APV' from dual union all 
select 1, 'MX6',   'MB_SHEET_ROW', 'CHECK','APW' from dual union all 
select 1, 'MX54',  'MB_SHEET_ROW', 'WHILE','APX' from dual union all 
select 1, 'MX14',  'MB_SHEET_ROW', 'DO', 'APY' from dual union all 
select 1, 'MX2',   'MB_SHEET_ROW', 'FOR', 'APZ' from dual;

我们现在有以下情况:

SQL> select * from mytable ;

MXP_ID  MX_ID  TB_SRC        DESCR  COL_LOC  
1       MX3    MB_SHEET_ROW  TEST   APS      
1       MX1    MB_SHEET_ROW  DEV    APT    
----------------------------------------- <-- want to insert here.   
1       MX120  MB_SHEET_ROW  PROD   APU      
1       MX5    MB_SHEET_ROW  SET    APV      
1       MX6    MB_SHEET_ROW  CHECK  APW      
1       MX54   MB_SHEET_ROW  WHILE  APX      
1       MX14   MB_SHEET_ROW  DO     APY      
1       MX2    MB_SHEET_ROW  FOR    APZ 

不确定 MX_ID 列是否包含唯一值。因此,添加一个 id 列以获得良好的度量。

alter table mytable
add id_ raw( 16 ) default sys_guid() ;
-- Table MYTABLE altered.

下面的 MERGE 使用我们的函数,并更改所有 col_locs "after" 列 APU。

merge into mytable M
using (
  select id_, mxp_id, mx_id, tb_src, descr, col_loc
  from mytable
  where col_loc >= 'APU'
) M2 
on ( M.id_ = M2.id_ )
when matched then
  update set col_loc = inttoalpha( alphatoint( col_loc ) + 1 )
;
-- 6 rows merged.

您可能知道数据库表的行不像电子表格的行那样“有序”。插入新行时请记住这一点。

insert into mytable ( mxp_id, mx_id, tb_src, descr, col_loc )
values ( 1, 'MX_NEW(!)', 'MB_SHEET_ROW', 'NEW_ENTRY', 'APU' ) ;

-- 1 row inserted.

该表现在包含:

select * from mytable order by col_loc;

MXP_ID  MX_ID      TB_SRC        DESCR      COL_LOC  ID_                               
1       MX3        MB_SHEET_ROW  TEST       APS      6CD1B226D8CE12DAE0530100007F80CA  
1       MX1        MB_SHEET_ROW  DEV        APT      6CD1B226D8CF12DAE0530100007F80CA  
1       MX_NEW(!)  MB_SHEET_ROW  NEW_ENTRY  APU      6CD1B226D8D612DAE0530100007F80CA  
1       MX120      MB_SHEET_ROW  PROD       APV      6CD1B226D8D012DAE0530100007F80CA  
1       MX5        MB_SHEET_ROW  SET        APW      6CD1B226D8D112DAE0530100007F80CA  
1       MX6        MB_SHEET_ROW  CHECK      APX      6CD1B226D8D212DAE0530100007F80CA  
1       MX54       MB_SHEET_ROW  WHILE      APY      6CD1B226D8D312DAE0530100007F80CA  
1       MX14       MB_SHEET_ROW  DO         APZ      6CD1B226D8D412DAE0530100007F80CA  
1       MX2        MB_SHEET_ROW  FOR        AQA      6CD1B226D8D512DAE0530100007F80CA 

有了这 2 个函数,在添加新的电子表格列时,您只需要 MERGE 和 INSERT。你可能会写一个小程序,它会为你完成这两个步骤。

于 2018-05-23T06:18:07.313 回答