如何根据 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_NoRef_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函数中的参数

如何将列的当前值与sql server和Oracle中同一列的先前值进行比较

如何对sql数据库中的某一字段进行替换?

oracle pl sql中如何将列数据转置为行

oracle sql 11G中如何将列转置为行