简体   繁体   中英

PL/SQL - SQL dynamic row and column parsing

I took a look into the forums and couldn't really find something that I needed.

What I have is two tables one table with (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

and another table that needs to be parsed like (Input_file)

String
ABCDEFGHI12
ASRQWERTQ45
123456789AB
321654852PO

and I want to have the result where If I put it will use this specific parsing spec

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

and be able to parse input file

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

and alternatively if I put file_id=2 it would parse the values differently.

I looked at using the Pivot function, but it looks like number of columns are static, at least to my knowledge.

thanks in advance for your support please let me know what I can do in SQL.

You can get "close-ish" with the standard decode tricks to pivot the table assuming a ceiling on the maximum number of fields expected.

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.

If you really wanted that result to then come back with only the desired column count and custom column names, then you're into dynamic SQL territory. How you'd tackle that depends on the tool you are providing the data to. If it can consume a REF CURSOR, then a little PL/SQL would do the trick.

An unknown number of columns can be returned from a SQL statement, but it requires code built with PL/SQL, ANY types, and Oracle Data Cartridge.

That code is tricky to write but you can start with my open source project Method4 . Download, unzip, @install , and then write a SQL statement to generate a SQL statement.

Query

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
        )
    ]'
));

Sample Schema

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;

Results

When FILE_ID = 1 :

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

When FILE_ID = 2 :

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM