PL / SQL - SQL动态行和列解析

时间:2017-12-18 15:30:06

标签: sql oracle pivot

我看了一下论坛,找不到我需要的东西。

我所拥有的是两个表,一个表(Parse_Table)

File_ID|Start_Pos|Length|Description
------------------------------------
   1   |    1    |   9  |    Pos1
   1   |    10   |   1  |    Pos2
   1   |    11   |   1  |    Pos3
   2   |    1    |   4  |    Pos1
   2   |    5    |   7  |    Pos2

和另一个需要解析的表,如(Input_file)

String
ABCDEFGHI12
ASRQWERTQ45
123456789AB
321654852PO

我希望得到结果如果我把它放在使用这个特定的解析规范

select DESCRIPTION, Start_pos,Length from Parse_table where File_ID=1

并且能够解析输入文件

String      |    Pos1  |Pos2|Pos3
---------------------------------
ABCDEFGHI12 |ABCDEFGHI |  1 |  2
ASRQWERTQ45 |ASRQWERTQ |  4 |  5
123456789AB |123456789 |  A |  B
321654852PO |321654852 |  P |  O

或者如果我把file_id = 2,它会以不同的方式解析值。

我查看了使用Pivot函数,但看起来列数是静态的,至少据我所知。

在此先感谢您的支持,请告诉我在SQL中可以做些什么。

2 个答案:

答案 0 :(得分:0)

你可以得到" close-ish"使用标准解码技巧来旋转表,假设预期的最大字段数上限。

SQL> create table t ( fid int, st int, len int, pos varchar2(10));

Table created.

SQL>
SQL> insert into t values (   1   ,    1    ,   9  ,    'Pos1');

1 row created.

SQL> insert into t values (   1   ,    10   ,   1  ,    'Pos2');

1 row created.

SQL> insert into t values (   1   ,    11   ,   1  ,    'Pos3');

1 row created.

SQL> insert into t values (   2   ,    1    ,   4  ,    'Pos1');

1 row created.

SQL> insert into t values (   2   ,    5    ,   7  ,    'Pos2');

1 row created.

SQL>
SQL> create table t1 ( s varchar2(20));

Table created.

SQL>
SQL> insert into t1 values ('ABCDEFGHI12');

1 row created.

SQL> insert into t1 values ('ASRQWERTQ45');

1 row created.

SQL> insert into t1 values ('123456789AB');

1 row created.

SQL> insert into t1 values ('321654852PO');

1 row created.

SQL>
SQL>
SQL> select
  2    t1.s,
  3    max(decode(t.seq,1,substr(t1.s,t.st,t.len))) c1,
  4    max(decode(t.seq,2,substr(t1.s,t.st,t.len))) c2,
  5    max(decode(t.seq,3,substr(t1.s,t.st,t.len))) c3,
  6    max(decode(t.seq,4,substr(t1.s,t.st,t.len))) c4,
  7    max(decode(t.seq,5,substr(t1.s,t.st,t.len))) c5,
  8    max(decode(t.seq,6,substr(t1.s,t.st,t.len))) c6
  9  from t1,
 10       ( select t.*, row_number() over ( partition by fid order by st ) as seq
 11         from t
 12         where fid = 1
 13       ) t
 14  group by t1.s
 15  order by 1;

S                    C1            C2            C3            C4            C5            C6
-------------------- ------------- ------------- ------------- ------------- ------------- -------------
123456789AB          123456789     A             B
321654852PO          321654852     P             O
ABCDEFGHI12          ABCDEFGHI     1             2
ASRQWERTQ45          ASRQWERTQ     4             5

4 rows selected.

SQL>
SQL> select
  2    t1.s,
  3    max(decode(t.seq,1,substr(t1.s,t.st,t.len))) c1,
  4    max(decode(t.seq,2,substr(t1.s,t.st,t.len))) c2,
  5    max(decode(t.seq,3,substr(t1.s,t.st,t.len))) c3,
  6    max(decode(t.seq,4,substr(t1.s,t.st,t.len))) c4,
  7    max(decode(t.seq,5,substr(t1.s,t.st,t.len))) c5,
  8    max(decode(t.seq,6,substr(t1.s,t.st,t.len))) c6
  9  from t1,
 10       ( select t.*, row_number() over ( partition by fid order by st ) as seq
 11         from t
 12         where fid = 2
 13       ) t
 14  group by t1.s
 15  order by 1;

S                    C1            C2            C3            C4            C5            C6
-------------------- ------------- ------------- ------------- ------------- ------------- -------------
123456789AB          1234          56789AB
321654852PO          3216          54852PO
ABCDEFGHI12          ABCD          EFGHI12
ASRQWERTQ45          ASRQ          WERTQ45

4 rows selected.

如果您真的希望结果只返回所需的列数和自定义列名,那么您将进入动态SQL领域。您如何解决这个问题取决于您提供数据的工具。如果它可以使用REF CURSOR,那么一点点PL / SQL就可以了。

答案 1 :(得分:0)

可以从SQL语句返回未知数量的列,但它需要使用PL / SQL,ANY类型和Oracle数据盒构建的代码。

编写代码很棘手,但您可以从我的开源项目Method4开始。下载,解压缩,@install,然后  编写一个SQL语句来生成一个SQL语句。

查询

select * from table(method4.dynamic_query(
    q'[
        --Create a SQL statement to query PARSE_FILE.
        select
            'select '||
                listagg(column_expression, ',') within group (order by start_pos) ||
                ' from parse_file'
            column_expressions
        from
        (
            --Create individual SUBSTR column expressions.
            select
                parse_table.*,
                'substr(string, '||start_pos||', '||length||') '||description column_expression
            from parse_table
            --CHANGE BELOW LINE TO USE A DIFFERENT FILE:
            where file_id = 2
            order by start_pos
        )
    ]'
));

示例架构

create table parse_table as
select 1 file_id, 1  start_pos, 9 length, 'Pos1' description from dual union all
select 1 file_id, 10 start_pos, 1 length, 'Pos2' description from dual union all
select 1 file_id, 11 start_pos, 1 length, 'Pos3' description from dual union all
select 2 file_id, 1  start_pos, 4 length, 'Pos1' description from dual union all
select 2 file_id, 5  start_pos, 7 length, 'Pos2' description from dual;

create table parse_file as
select 'ABCDEFGHI12' string from dual union all
select 'ASRQWERTQ45' string from dual union all
select '123456789AB' string from dual union all
select '321654852PO' string from dual;

结果

FILE_ID = 1时:

POS1        POS2   POS3
----        ----   ----
ABCDEFGHI   1      2
ASRQWERTQ   4      5
123456789   A      B
321654852   P      O

FILE_ID = 2时:

POS1   POS2
----   ----
ABCD   EFGHI12
ASRQ   WERTQ45
1234   56789AB
3216   54852PO