【问题标题】:Extract if string contains numbers pattern提取字符串是否包含数字模式
【发布时间】:2019-11-09 22:44:44
【问题描述】:

我的数据集中有一列,我想在其中提取特定位置的数字仅当字符串结构包含#-#-#

我如何用 SQL 写这个(目前用雪花写)?

例子:

Column_A
abc-net-met-1234-12345-431-5968
abc-eme-ejt-emdn-1948-192
jen-mdk-ent-193-102-1029398
loe-ekd-12-49-nfm

我希望我的预期输出是:

Column_A                            Number1    Number2    Number3   Number4
abc-net-met-1234-12345-431-5968        1234      12345        431      5968
abc-eme-ejt-emdn-1948-192              NULL       NULL       NULL      NULL            
jen-mdk-ent-193-102-1029398             193        102    1029398      NULL
loe-ekd-12-49-nfm                      NULL       NULL       NULL      NULL

我认为它会是这样但有问题:

CASE WHEN COLUMN_A LIKE '%#-#-#%' THEN SPLIT_PART(COLUMN_A, '-', 4) ELSE NULL END Number1
CASE WHEN COLUMN_A LIKE '%#-#-#%' THEN SPLIT_PART(COLUMN_A, '-', 5) ELSE NULL END Number2
CASE WHEN COLUMN_A LIKE '%#-#-#%' THEN SPLIT_PART(COLUMN_A, '-', 6) ELSE NULL END Number3
CASE WHEN COLUMN_A LIKE '%#-#-#%' THEN SPLIT_PART(COLUMN_A, '-', 7) ELSE NULL END Number4

【问题讨论】:

  • 哪种类型的 SQL?
  • 雪花文档说“ANSI”
  • 那么,如果序列中有至少 3 个数字,您想要这些数字...不管字符串数如何?如果数字用 A-B-1-2-Z-3 之类的字符串分隔会怎样?
  • 那不行,需要这样:#-#-#
  • 由于不同的Column_A的数字序列从不同的部分开始,因此无法提取具有常量4-7的数字。

标签: sql string split sql-like snowflake-cloud-data-platform


【解决方案1】:

我认为您的like 表达方式过于宽泛。您可以使用正则表达式匹配来缩小范围,例如:

CASE 
    WHEN COLUMN_A RLIKE '.*(^|-)[0-9]+-[0-9]+-[0-9]+($|-).*' 
    THEN SPLIT_PART(COLUMN_A, '-', 4) 
    ELSE NULL 
END Number1

正则表达式匹配由破折号分隔的 3 组连续数字,前面有破折号(或位于字符串的开头),或后跟一个破折号(或位于字符串的结尾)。

这也可以拼写:

CASE 
    WHEN COLUMN_A RLIKE '.*(^|-)\\d+-\\d+-\\d+($|-).*' 
    THEN SPLIT_PART(COLUMN_A, '-', 4) 
    ELSE NULL 
END Number1

【讨论】:

  • 运气不好,我在这个特定的字符串上试过了:ABC-ACROBAT-OWL-23020365-255738062-451886974-121804749
  • 我所有的值都是大写的,有区别吗?
  • @nak5120:这个字符串应该匹配正则表达式。如果您执行以下操作会得到什么:SELECT CASE WHEN 'ABC-ACROBAT-OWL-23020365-255738062-451886974-121804749' RLIKE '^[a-zA-Z]+-[a-zA-Z]+-[a-zA-Z]+-\d+-' THEN 1 ELSE 0 END: 0 或 1?
  • 不幸的是我得到了 0
  • 好的。如果你改用这个正则表达式怎么办:'^[a-zA-Z]+-[a-zA-Z]+-[a-zA-Z]+-[0-9]+-'?
【解决方案2】:

我不确定为什么当前的“正确答案”被标记为这样,因为尽管它选择了正确的行,但它并没有以任何特别有用的方式提取结果。

以下查询提供了正确答案,它首先提取一个包含至少 3 个连续数字的子字符串,然后从该 3+ 个数字的子字符串中提取第 n 个数字:

WITH TEST_TAB AS (SELECT * FROM (VALUES
    ('abc-net-met-1234-12345-431-5968'), ('abc-eme-ejt-emdn-1948-192'),
    ('jen-mdk-ent-193-102-1029398'),     ('loe-ekd-12-49-nfm')
  ) T(COLUMN_A))
SELECT COLUMN_A,
  REGEXP_SUBSTR(REGEXP_SUBSTR('-'||COLUMN_A||'-', '(-\\d+){3,}-'), '\\d+', 2, 1) Number1,
  REGEXP_SUBSTR(REGEXP_SUBSTR('-'||COLUMN_A||'-', '(-\\d+){3,}-'), '\\d+', 2, 2) Number2,
  REGEXP_SUBSTR(REGEXP_SUBSTR('-'||COLUMN_A||'-', '(-\\d+){3,}-'), '\\d+', 2, 3) Number3,
  REGEXP_SUBSTR(REGEXP_SUBSTR('-'||COLUMN_A||'-', '(-\\d+){3,}-'), '\\d+', 2, 4) Number4
FROM TEST_TAB;

【讨论】:

    【解决方案3】:

    我手头没有雪花,但这样的东西应该可以工作: 以下是建议的方法:将字符串拆分为表格,枚举数字,然后使用条件聚合。

    我手头没有 Snowflake 可供测试,但类似这样:

    select column_a,
           max(case when seqnum = 1 then value end) as number_1,
           max(case when seqnum = 2 then value end) as number_2,
           max(case when seqnum = 3 then value end) as number_3,
           max(case when seqnum = 4 then value end) as number_4
    from (select t.column_a, s.*,
                 row_number() over (partition by (case when s.value rlike '^[0-9]+$'
                                                       then 1 else 0
                                                  end)
                                                  order by s.index
                                   ) as seqnum
          from t cross join lateral
               split_to_table(t.column_a, '-') s
         ) t
    group by column_a;
    

    【讨论】:

    • 感谢 Gordon,但我希望避免任何连接。像以前的答案这样的东西会很完美,因为除此之外我还有其他规则发生
    • @nak5120 。 . .这可以满足您的要求,并且通常适用于所有列。这似乎是您所提问题的答案。
    • 我已经运行了上面的代码,但它似乎没有产生正确的结果。一方面,COLUMN_A 的所有第一个文本部分(abc、abc、jen、loe)都报告为数字。也没有代码可以确保至少有 3 个连续数字。
    猜你喜欢
    • 2013-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2015-02-07
    • 2010-12-19
    相关资源
    最近更新 更多