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 中的一个或多个].
捕获组:
- (~([^~]+))? - 捕获以波浪号 (~) 开头的内容零次或多次,并在其中
- ([^~]+) - 捕获任何不是波浪号 (~) 一次或多次
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" ]
下面是我正在处理的文本示例。
---
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 中的一个或多个].
捕获组:
- (~([^~]+))? - 捕获以波浪号 (~) 开头的内容零次或多次,并在其中
- ([^~]+) - 捕获任何不是波浪号 (~) 一次或多次
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" ]