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>
更新
如果模式不固定,则使用SUBSTR和INSTR。
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
两个不依赖于给定顺序的数据的解决方案:
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
| 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
| X | Y | Z | A |
|--------|----|--------|--------|
| 1 | 2 | 3 | (null) |
| 4 | 5 | 6 | (null) |
| 14 | 15 | 16 | 25 |
| (null) | 9 | (null) | 4 |
我有一个 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>
更新
如果模式不固定,则使用SUBSTR和INSTR。
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
两个不依赖于给定顺序的数据的解决方案:
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
| 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
| X | Y | Z | A |
|--------|----|--------|--------|
| 1 | 2 | 3 | (null) |
| 4 | 5 | 6 | (null) |
| 14 | 15 | 16 | 25 |
| (null) | 9 | (null) | 4 |