sql 根据两个属性拆分列的查询

sql query to split columns as per two attributes

我有一个 table 说 A,它只有一列说数据。

DATA
-----
x=1;y=2;z=3
x=4;y=5;z=6
x=14;y=15;z=16;a=25

我想要如下结果:

x         y          z        a 
--------------------------------
1         2          3        0
4         5          6        0
14        15         16       25

我正在使用 toad 进行查询处理。

首先,您的 table 未标准化。您应该将它们存储在不同的列中,而不是作为分隔字符串存储在单个列中。阅读Normalization。修复 design.

应该是一个永久的解决方案

无论如何,作为一种解决方法,在不更改设计的情况下,您可以按照下面的说明进行操作:

如果模式是固定的,那么你可以使用SUBSTR.

SQL> WITH DATA(str) AS(
  2  SELECT 'x=1;y=2;z=3' FROM dual UNION ALL
  3  SELECT 'x=4;y=5;z=6' FROM dual
  4  )
  5  SELECT substr(str, 3, 1) a,
  6         substr(str, 7, 1) b,
  7         substr(str, 11, 1) c
  8  FROM DATA;

A B C
- - -
1 2 3
4 5 6

SQL>

更新

如果模式不固定,则使用SUBSTRINSTR

SQL> WITH DATA(str) AS
  2    ( SELECT 'x=1;y=2;z=3' FROM dual
  3    UNION ALL
  4    SELECT 'x=4;y=5;z=6' FROM dual
  5    UNION ALL
  6    SELECT 'x=14;y=15;z=16;A=25' FROM dual
  7    )
  8  SELECT NVL(SUBSTR(str
  9    ||';', instr(str
 10    ||';', '=', 1, 1) +1, instr(str
 11    ||';', ';', 1, 1) - instr(str
 12    ||';', '=', 1, 1) -1), '0') a,
 13    NVL(SUBSTR(str
 14    ||';', instr(str
 15    ||';', '=', 1, 2) +1, instr(str
 16    ||';', ';', 1, 2) - instr(str
 17    ||';', '=', 1, 2) -1), '0') b,
 18    NVL(SUBSTR(str
 19    ||';', instr(str
 20    ||';', '=', 1, 3) +1, instr(str
 21    ||';', ';', 1, 3) - instr(str
 22    ||';', '=', 1, 3) -1), '0') c,
 23    NVL(SUBSTR(str
 24    ||';', instr(str
 25    ||';', '=', 1, 4) +1, instr(str
 26    ||';', ';', 1, 4) - instr(str
 27    ||';', '=', 1, 4) -1), '0') d
 28  FROM DATA;

A B C D
- - - -
1 2 3 0
4 5 6 0
1 1 1 2
4 5 6 5

您可以使用正则表达式,这看起来代码会更短,但是,简单的SUBTR 和 INSTR 将比正则表达式更快.

在 12c 中测试。首先我用';'分割数据然后按“=”。

      with dat (data) as
          (
          select 'x=1;y=2;z=3' from dual union
          select 'x=4;y=5;z=6' from dual
          )
          select 
          REGEXP_SUBSTR(REGEXP_SUBSTR(data,'[^;]+',1,1),'[^=]+',1,2) as x,
          REGEXP_SUBSTR(REGEXP_SUBSTR(data,'[^;]+',1,2),'[^=]+',1,2) as y,
          REGEXP_SUBSTR(REGEXP_SUBSTR(data,'[^;]+',1,3),'[^=]+',1,2) as z
          from dat

输出

          X Y Z
          1 2 3
          4 5 6  

两个不依赖于给定顺序的数据的解决方案:

SQL Fiddle

Oracle 11g R2 架构设置:

CREATE TABLE A ( DATA ) AS
          SELECT 'x=1;y=2;z=3' FROM DUAL
UNION ALL SELECT 'y=5;z=6;x=4' FROM DUAL
UNION ALL SELECT 'x=14;y=15;z=16;a=25' FROM DUAL
UNION ALL SELECT 'y=9;a=4' FROM DUAL

查询 1:

SELECT REGEXP_SUBSTR( DATA, 'x=(\d+)', 1, 1, 'i', 1 ) AS X,
       REGEXP_SUBSTR( DATA, 'y=(\d+)', 1, 1, 'i', 1 ) AS Y,
       REGEXP_SUBSTR( DATA, 'z=(\d+)', 1, 1, 'i', 1 ) AS Z,
       REGEXP_SUBSTR( DATA, 'a=(\d+)', 1, 1, 'i', 1 ) AS A
FROM   A

Results:

|      X |  Y |      Z |      A |
|--------|----|--------|--------|
|      1 |  2 |      3 | (null) |
|      4 |  5 |      6 | (null) |
|     14 | 15 |     16 |     25 |
| (null) |  9 | (null) |      4 |

查询 2:

WITH POSITIONS AS (
  SELECT DATA,
         INSTR( DATA, 'x=' ) AS X,
         INSTR( DATA, ';', INSTR( DATA, 'x=' ) ) AS X_SEP,
         INSTR( DATA, 'y=' ) AS Y,
         INSTR( DATA, ';', INSTR( DATA, 'y=' ) ) AS Y_SEP,
         INSTR( DATA, 'z=' ) AS Z,
         INSTR( DATA, ';', INSTR( DATA, 'z=' ) ) AS Z_SEP,
         INSTR( DATA, 'a=' ) AS A,
         INSTR( DATA, ';', INSTR( DATA, 'a=' ) ) AS A_SEP
  FROM   A
)
SELECT CASE
          WHEN X = 0     THEN NULL
          WHEN X_SEP = 0 THEN TO_NUMBER( SUBSTR( DATA, X+2 ) )
                         ELSE TO_NUMBER( SUBSTR( DATA, X+2, X_SEP-X-2 ) )
          END
          AS X,
        CASE
          WHEN Y = 0     THEN NULL
          WHEN Y_SEP = 0 THEN TO_NUMBER( SUBSTR( DATA, Y+2 ) )
                         ELSE TO_NUMBER( SUBSTR( DATA, Y+2, Y_SEP-Y-2 ) )
          END
          AS Y,
        CASE
          WHEN Z = 0     THEN NULL
          WHEN Z_SEP = 0 THEN TO_NUMBER( SUBSTR( DATA, Z+2 ) )
                         ELSE TO_NUMBER( SUBSTR( DATA, Z+2, Z_SEP-Z-2 ) )
          END
          AS Z,
        CASE
          WHEN A = 0     THEN NULL
          WHEN A_SEP = 0 THEN TO_NUMBER( SUBSTR( DATA, A+2 ) )
                         ELSE TO_NUMBER( SUBSTR( DATA, A+2, A_SEP-A-2 ) )
          END
          AS A
FROM    POSITIONS

Results:

|      X |  Y |      Z |      A |
|--------|----|--------|--------|
|      1 |  2 |      3 | (null) |
|      4 |  5 |      6 | (null) |
|     14 | 15 |     16 |     25 |
| (null) |  9 | (null) |      4 |