Snowflake SQL,如何向前看直到某个值出现

Snowflake SQL, how to lookahead until a certain occurrence of a value

下面是我正在处理的文本示例。

--- 
info1:
*  val: "A"
---
Type:
*  answers:
*  - !<string>
*    val: "B"
*  - !<string>
*    val: "C"
--- 
info2:
*  val: "D"
---

我正在尝试 select 以下文本:

Type:
*  answers:
*  - !<string>
*    val: "B"
*  - !<string>
*    val: "C"

我试图使用前瞻性,但没有取得太大的成功。 REGEXP_SUBSTR(col, 'Type:(.*---)') 在这里,我试图查找直到下一次出现“---”,但我想我误解了它是如何工作的。

REGEXP_SUBSTR 在雪花中相当有限,在原生 SQL 中,所以当你告诉是匹配多行并通过 REGEXP_SUBSTR(t, '(Type:.*)---',1,1,'mes',1) 匹配换行符时,正则表达式是 greddy 因此:

SELECT '--- 
info1:
*  val: "A"
---
Type:
*  answers:
*  - !<string>
*    val: "B"
*  - !<string>
*    val: "C"
--- 
info2:
*  val: "D"
---' as t
,REGEXP_SUBSTR(t, '(Type:.*)',1,1,'mes',1) as r1 
,REGEXP_SUBSTR(t, '(Type:.*)---',1,1,'mes',1) as r2;

给你的数据太多了:

Type:
*  answers:
*  - !<string>
*    val: "B"
*  - !<string>
*    val: "C"
--- 
info2:
*  val: "D"

所以一个想法是,如果 --- 始终是一个部分标记,则先在其上进行字符串拆分,然后再进行正则表达式

WITH input as (
select '--- 
info1:
*  val: "A"
---
Type:
*  answers:
*  - !<string>
*    val: "B"
*  - !<string>
*    val: "C"
--- 
info2:
*  val: "D"
---' as t
)
select t, c.value::string as part, REGEXP_SUBSTR(part, 'Type:.*',1,1,'mes') as r1 
from input,
     lateral flatten(input=>split(t, '---')) c;

给予

T   PART    R1
---   info1:  *  val: "A"  ---  Type:  *  answers:  *  - !<string>  *    val: "B"  *  - !<string>  *    val: "C"  ---   info2:  *  val: "D"  ---        
---   info1:  *  val: "A"  ---  Type:  *  answers:  *  - !<string>  *    val: "B"  *  - !<string>  *    val: "C"  ---   info2:  *  val: "D"  ---       info1:  *  val: "A"      
---   info1:  *  val: "A"  ---  Type:  *  answers:  *  - !<string>  *    val: "B"  *  - !<string>  *    val: "C"  ---   info2:  *  val: "D"  ---      Type:  *  answers:  *  - !<string>  *    val: "B"  *  - !<string>  *    val: "C"      Type:  *  answers:  *  - !<string>  *    val: "B"  *  - !<string>  *    val: "C"  
---   info1:  *  val: "A"  ---  Type:  *  answers:  *  - !<string>  *    val: "B"  *  - !<string>  *    val: "C"  ---   info2:  *  val: "D"  ---       info2:  *  val: "D"      
---   info1:  *  val: "A"  ---  Type:  *  answers:  *  - !<string>  *    val: "B"  *  - !<string>  *    val: "C"  ---   info2:  *  val: "D"  ---        

您应该可以从中取得进步。

此外,如果您需要非常复杂的正则表达式,您可以制作一个 javascript-UDF 并使用 javascript 正则表达式引擎..

Snowflake 不支持向前看或向后看,但它支持组提取(和嵌套组提取)。这些可以在 REGEXP_REPLACE 或 REGEXP_SUBSTR 中使用。在这种情况下,我更喜欢 REGEXP_SUBSTR 因为您希望从中提取,而不是在其中替换。在我下面的示例中,您将看到两者。

您有 3 个破折号 (-) 作为分隔符,问题是您的数据中有一个破折号。我建议用您的数据中不存在的内容替换定界符,我选择波浪号 (~)。

下面的代码示例将起作用。

备注:

  • C2 列对 KEEPS_LEADING_SPACE 列进行操作,它不会删除前导 space。在这种情况下,前导 space 是第一个捕获组。
  • C3 列对实际数据列进行操作,但假定前导 space 是 space、回车符 return、换行符或垂直 space 中的一个或多个].

捕获组:

  1. (~([^~]+))? - 捕获以波浪号 (~) 开头的内容零次或多次,并在其中
  2. ([^~]+) - 捕获任何不是波浪号 (~) 一次或多次
USE ROLE SYSADMIN;
USE WAREHOUSE PUBLIC_WH;
USE UTIL_DB.PUBLIC;

CREATE OR REPLACE TEMP TABLE REGEXP_TEST
AS
SELECT ::VARIANT AS C1
FROM VALUES 
($$ 
--- 
info1:
*  val: "A"
---
Type:
*  answers:
*  - !<string>
*    val: "B"
*  - !<string>
*    val: "C"
--- 
info2:
*  val: "D"
---
$$);


SELECT C1
    ,REPLACE(C1,'---','~') AS KEEPS_LEADING_SPACE
    ,REGEXP_SUBSTR(KEEPS_LEADING_SPACE,'(~([^~]+))?',1,4,'is') AS C2
    ,REGEXP_SUBSTR(REGEXP_REPLACE(C1,'[\s\r\n\v]?-{3}','~'),'(~([^~]+))?',1,3,'is') AS C3
FROM REGEXP_TEST
;

Snowflake 中的正则表达式确实支持负数,但如果您要查找多个字符,我倾向于发现它们很难使用,但在这种情况下,我们有一个字符来否定波浪号 [^~]。

你不需要正则表达式前瞻来获得你想要的字符串,它只是例如。

REGEXP_SUBSTR(col, '(^Type:\s+(^[*].*$\s+)*)^---', 1, 1, 'm', 1)

如果您需要具有前瞻性的正则表达式等,请通过函数包装器使用 JavaScript 正则表达式,例如

CREATE OR REPLACE FUNCTION RegExp_Match("STRING" VARCHAR, "REGEXP" VARCHAR)
RETURNS VARIANT LANGUAGE JAVASCRIPT STRICT IMMUTABLE AS
'return STRING.match(REGEXP);';
CREATE OR REPLACE FUNCTION RegExp_Match("STRING" VARCHAR, "RX" VARCHAR, "FLAGS" VARCHAR)
RETURNS VARIANT LANGUAGE JAVASCRIPT STRICT IMMUTABLE AS
'return STRING.match(new RegExp(RX, FLAGS));';

SELECT RegExp_Match('<aA>', '(?<=<)(.)\1(?=>)', 'i');
-- RegExp with lookback, back reference and lookahead ignoring case
=> [ "aA", "a" ]