使用列的前一个值更新oracle表

时间:2018-05-22 11:18:06

标签: excel oracle

我在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;

1 个答案:

答案 0 :(得分: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使用我们的函数,并在“APU”列之后更改所有col_locs。

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。你可以写一个小程序,为你做两个步骤。