redshift regex 获取多个匹配项并扩展行

Posted

技术标签:

【中文标题】redshift regex 获取多个匹配项并扩展行【英文标题】:redshift regex get multiple matches and expand rows 【发布时间】:2018-11-28 21:40:12 【问题描述】:

我正在处理 AWS Redshift 上的 URL 提取。 URL 列如下所示:

url                       item     origin
http://B123//ajdsb        apple    US
http://BYHG//B123         banana   UK
http://B325//BF89//BY85   candy    CA

我想要得到的结果是获取以 B 开头的系列,如果 URL 中有多个系列,还扩展行。

extracted    item     origin
B123         apple    US
BYHG         banana   UK
B123         banana   UK
B325         candy    CA
BF89         candy    CA
BY85         candy    CA

我当前的代码是:

select REGEXP_SUBSTR(url, '(B[0-9A-Z]3)') as extracted, item, origin
from data

正则表达式部分运行良好,但我在提取多个值并将它们扩展到新行时遇到问题。我尝试使用REGEXP_MATCHES(url, '(B[0-9A-Z]3)', 'g'),但 Redshift 上不存在函数 regexp_matches...

【问题讨论】:

至少,我想知道如何在一个字符串中找到多个匹配项。如果我在一行中获得多个 macthe 也没关系 【参考方案1】:

我使用的解决方案相当丑陋,但达到了预期的效果。它涉及使用REGEXP_COUNT 确定一行中的最大匹配数,然后使用REGEXP_SUBSTR 将生成的数字表连接到查询。

-- Get a table with the count of matches
-- e.g. if one row has 5 matches this query will return 0, 1, 2, 3, 4, 5
WITH n_table AS (
    SELECT
        DISTINCT REGEXP_COUNT(url, '(B[0-9A-Z]3)') AS n
    FROM data
)
-- Join the previous table to the data table and use n in the REGEXP_SUBSTR call to get the nth match
SELECT
    REGEXP_SUBSTR(url, '(B[0-9A-Z]3)', 1, n) AS extracted,
    item,
    origin
FROM data,
     n_table
-- Only keep non-null matches
WHERE n > 0
  AND REGEXP_COUNT(url, '(B[0-9A-Z]3)') >= N

【讨论】:

这个解决方案真的很容易理解!谢谢。我已经在我的采样数据集上对此进行了测试。效果很好!我现在正在整个表上测试这个查询。 超级有用且简单!有人可以在where子句中解释N吗? 假设一个示例 URL 有 3 个匹配项,匹配最多的 URL 有 5 个匹配项。在这种情况下,n_table 将保存值 0、1、2、3、4、5。但是,对于我们的示例 URL(有 3 个匹配项),我们只需要 n 值 1、2、3,因此需要 where 子句。颠倒过来可能更有意义,n <= REGEXP_COUNT(url, '(B[0-9A-Z]3)')。我们说对于我们的案例有 3 个匹配项(REGEXP_COUNT 为我们的示例 URL 返回 3),我们希望 n 在生成输出时具有值 1,2,3。【参考方案2】:

IronFarm 的回答启发了我,虽然我想找到一个不需要交叉连接的解决方案。这是我想出的:

with 

-- raw data
src as (
  select 
    1 as id,
    'abc def ghi' as stuff
  union all 
  select
    2 as id,
    'qwe rty' as stuff
),

-- for each id, get a series of indexes for
-- each match in the string
match_idxs as (
  select
    id,
    generate_series(1, regexp_count(stuff, '[a-z]3')) as idx
  from
    src
)

select 
  src.id,
  match_idxs.idx,
  regexp_substr(src.stuff, '[a-z]3', 1, match_idxs.idx) as stuff_match
from 
  src 
  join match_idxs using (id)
order by 
  id, idx
;

这会产生:

 id | idx | stuff_match
----+-----+-------------
  1 |   1 | abc
  1 |   2 | def
  1 |   3 | ghi
  2 |   1 | qwe
  2 |   2 | rty
(5 rows)

【讨论】:

请注意,generate_series() 实际上在 redshift 中不受支持,并且似乎仅适用于玩具示例。当我弄清楚如何最好地处理这个问题时会更新我的答案。 是的...我在 Redshift 上进行了测试,generate_series() 无法正常工作。但它仍然是 postgresql 的绝佳解决方案 ^ ^

以上是关于redshift regex 获取多个匹配项并扩展行的主要内容,如果未能解决你的问题,请参考以下文章

亚马逊 Redshift 的 REGEXP_SUBSTR 中的“匹配但排除”

匹配 .NET Regex 中的扩展 ASCII 字符

多个 RegEx 否定匹配

Redshift 在多个条件下加入,但仅在一个条件不匹配时返回

在 Redshift 中使用正则表达式来获取匹配模式之前的单词

python regex如何避免匹配多个分号?