如何根据 Oracle SQL 中的某些条件将列拆分为 2?
Posted
技术标签:
【中文标题】如何根据 Oracle SQL 中的某些条件将列拆分为 2?【英文标题】:How do I split column into 2 based on certain conditions in Oracle SQL? 【发布时间】:2020-02-04 14:28:31 【问题描述】:我有一个包含 250k 数据的表,在这些数据中,我有 1000 行,每列都有相同的数据,一个不同的参考列。
如果满足某些条件,我想做的是拆分参考列,如果不满足这些条件,参考值可以保持原样。
以下是我的创建表和插入语句:
CREATE TABLE "BU_TABLE"
(
"NAME" VARCHAR2(255 BYTE),
"TEL_NO" VARCHAR2(255 BYTE),
"POST_CODE" VARCHAR2(8 BYTE),
"REF_NO" VARCHAR2(255 BYTE)
;
我将Tel_No
和Ref_No
列设置为Varchar2
的原因是因为当数字有空格时有空格,然后a)插入时会提示Invalid Number error
b)Tel_No
在示例数据的那一刻没有这个问题,但随着更多数据的添加,我可能会遇到这个问题。
插入语句:
Insert into BU_TABLE (NAME,TEL_NO,POST_CODE,REF_NO) values ('Damian','7900123456','ME1 2BC','12345678 1234567891234');
Insert into BU_TABLE (NAME,TEL_NO,POST_CODE,REF_NO) values ('Graeme','7900789012','ME1 2DE','12 345 5678901234567');
Insert into BU_TABLE (NAME,TEL_NO,POST_CODE,REF_NO) values ('Sarah','7900456789','ME1 2FG','90123456 890123456789');
现在我希望BU_TABLE
保持原样,使用原始数据。我想在这个表的后面创建另一个表来拆分Ref_No
,所以我有一个由 8 个数字组成的序列,一个空格,然后是 13 个数字。无论这种模式出现在哪里,我都想复制该行并给我以下内容:
Name | Tel_No | Post_Code | Ref_No
Damian | 7900123456 | ME1 2BC | 12345678
Damian | 7900123456 | ME1 2BC | 1234567891234
所需代码未找到序列的行,数据行将在表中保持不变,因此我的最终表将如下所示。这显示了新的数据拆分,其中 Damian 的 2 个参考编号已被拆分,现在显示为 2 行,而 Graeme 和 Sarah 有他们的原始参考编号,因为他们的序列不符合标准。
决赛桌:
Name | Tel_No | Post_Code | Ref_No
Graeme | 7900789012 | ME1 2DE | 12 345 5678901234567
Sarah | 7900456789 | ME1 2FG | 90123456 890123456789
Damian | 7900123456 | ME1 2BC | 12345678
Damian | 7900123456 | ME1 2BC | 1234567891234
希望我的要求很明确。
很抱歉,因为有另一个帖子有同样的问题: How do I copy a row and split one of the columns based on certain criteria in Oracle SQL?
但是问题已经结束,因为我被要求添加我的创建和插入语句以使我的要求更清晰,因此我创建了一个新问题。
【问题讨论】:
您使用的是 mysql 还是 Oracle? @jarlh - 我编辑了标签。使用 MySQL 的人不会将“Oracle SQL”放在他们的帖子标题中。 【参考方案1】:您可以使用union all
来执行此操作。我在下面展示了一种不同的方法 - 使用过去常见的非透视技术(在 Oracle 引入 unpivot
运算符之前)。这将比使用union all
的解决方案更有效,因为它只需要一次全表扫描。
我没有显示表的创建;我展示了一个 select
语句,您可以使用它来填充您的新表。
select bu.name, bu.tel_no, bu.post_code,
case when regexp_like(bu.ref_no, '^\d8 \d13$')
then
case t.c when 1 then substr(bu.ref_no, 1, 8)
else substr(bu.ref_no, 10) end
else bu.ref_no
end as ref_no
from bu_table bu join (select 1 as c from dual union all select 2 from dual) t
on t.c = 1 or regexp_like(bu.ref_no, '^\d8 \d13$')
;
NAME TEL_NO POST_CODE REF_NO
------ ---------- --------- -----------------------
Damian 7900123456 ME1 2BC 12345678
Graeme 7900789012 ME1 2DE 12 345 5678901234567
Sarah 7900456789 ME1 2FG 90123456 890123456789
Damian 7900123456 ME1 2BC 1234567891234
【讨论】:
感谢 mathguy,这正是我想要的 :) 太棒了 只是出于好奇和学习,如果表中有一个 ID 列(数字列),您将如何分配它以将新数字添加到拆分列?顺便说一句,当前数据的 ID 将是 1-3,因此 2 个新的 Damian 列将是 4 和 5。 @MichaelOwen - 如果有一个 ID 列用作主键(但不编码任何真实数据 - 这是“最佳实践”,PK 通常不应编码有意义的信息),那么最好被定义为identity
列;或者,在旧版本的 Oracle 数据库中,它应该由触发器使用序列自动填充。无论哪种方式,您都不必担心自己填充 ID 列。无论如何,这就是 I 会做的事情 - 你问 I 将如何分配新的 ID。
谢谢,这就是我想要的。再次感谢您的帮助。【参考方案2】:
你可以使用union all
做你想做的事:
select bu.Name, bu.Tel_No, bu.Post_Code, bu.Ref_No
from bu
where not regexp_like(ref_no, '^[0-9]8 [0-9]13$')
union all
select bu.Name, bu.Tel_No, bu.Post_Code, substr(bu.Ref_No, 1, 8)
from bu
where regexp_like(ref_no, '^[0-9]8 [0-9]13$')
union all
select bu.Name, bu.Tel_No, bu.Post_Code, substr(bu.Ref_No, -13)
from bu
where regexp_like(ref_no, '^[0-9]8 [0-9]13$');
【讨论】:
如果您在union all
的一个成员中使用case
表达式,您可以将全表扫描的次数从三个减少到两个。 (然后,您可以通过取消透视将其进一步减少到单个表扫描 - 我在我的回答中展示了这一点。)【参考方案3】:
您可以使用以下查询:
SQL> SELECT NAME, TEL_NO, POST_CODE, 2 CASE 3 WHEN CNT = 1 THEN REF_NO 4 ELSE CASE 5 WHEN L = 1 THEN SUBSTR(REF_NO, 1, 8) 6 ELSE SUBSTR(REF_NO, 10) 7 END 8 END AS REF_NO 9 FROM 10 (SELECT T.*, 11 CASE 12 WHEN REGEXP_LIKE ( REF_NO, 13 '^[0-9]8 [0-9]13$' ) THEN 2 14 ELSE 1 15 END AS CNT 16 FROM BU_TABLE T ) MT 17 JOIN (SELECT LEVEL AS L 18 FROM DUAL CONNECT BY LEVEL <= 2 19 ) ON ( CNT >= L ); NAME TEL_NO POST_COD REF_NO ---------- ------------ -------- ------------------------ Damian 7900123456 ME1 2BC 12345678 Graeme 7900789012 ME1 2DE 12 345 5678901234567 Sarah 7900456789 ME1 2FG 90123456 890123456789 Damian 7900123456 ME1 2BC 1234567891234 SQL>
干杯!!
【讨论】:
以上是关于如何根据 Oracle SQL 中的某些条件将列拆分为 2?的主要内容,如果未能解决你的问题,请参考以下文章
如何根据 Oracle 表格 6i 中的某些条件在两个 LOV 之间进行选择
如何在Oracle中仅将列用作1行到1行返回pl / sql函数中的参数