根据 String 在 SQL (Snowflake) 中选择一行
Selecting a row in SQL (Snowflake) based on String
所以我有一个硬汉,我已经绞尽脑汁了一段时间。
假设我有一个 table 如下:
ID Group Timestamp Data
001 A 2021-04-13 12:51:12.063 content121
001 A-Direct 2021-04-13 12:52:13.063 content121
002 A-Direct 2021-04-13 12:50:14.063 content133
003 B-Direct 2021-04-13 12:55:12.063 content132
003 B 2021-04-13 12:56:11.063 content142
003 BA 2021-04-13 12:57:22.063 content153
004 D 2021-04-13 12:10:23.063 content113
004 C 2021-04-13 12:11:43.063 content144
005 C 2021-04-13 12:12:12.063 content111
005 A 2021-04-13 12:13:23.063 content100
005 D-Direct 2021-04-13 12:15:23.063 content121
006 A 2021-04-13 12:51:12.063 content121
006 B-Direct 2021-04-13 12:52:13.063 content121
007 A-Direct 2021-04-13 12:51:12.063 content121
007 A 2021-04-13 12:52:13.063 content121
008 B-Direct 2021-04-13 12:55:12.063 content132
008 B 2021-04-13 12:56:11.063 content142
008 B-Direct 2021-04-13 12:57:22.063 content153
009 B-Direct 2021-04-13 12:55:12.063 content132
009 C-Direct 2021-04-13 12:56:11.063 content142
009 D-Direct 2021-04-13 12:57:22.063 content153
所以我需要一个 table,其中每一行包含一个不同的 ID。但是选择ID的标准有点复杂。
默认选择应该是最近的条目,通过TIMESTAMP
选择。
但复杂性来自任何具有 -Direct
行的 ID。具体来说,如果一行有多个条目并且一个是(例如)A
,另一个是 A-Direct
,我们需要 A
。只有当字母匹配时才会出现这种情况。正如在 ID = 006
的例子中看到的,我们想要 B-Direct
因为它的对应物是 A
.
所以我要寻找的核心逻辑是
如果 ID 有以相同字符串开头的行,其中一行以 -Direct
结尾,请将其替换为删除的 -Direct
。
最终输出:
ID Group
001 A
002 A-Direct
003 BA
004 C
005 D-Direct
006 B-Direct
007 A
008 B
009 D-Direct
为了更加清楚,这里概述了每个 ID 发生的情况:
- ID 001:
A
后面跟着 A-Direct
所以我们用 A-Direct
代替 A
- ID 002:
A-Direct
是唯一的结果,简单!
- ID 003:
BA
、B
、B-Direct
是不同的,因此我们坚持使用最新的 BA
.
- ID 004: 没有直接的,所以我们只取最近的,
C
- ID 005:
D-Direct
是最近的,但是因为没有 D
,所以我们坚持使用 D-Direct
- ID 006:
B-Direct
是最近的,但是因为没有 B
,所以我们坚持使用 B-Direct
- ID 007:
A-Direct
后面是 A
所以我们只取最近的一个,没问题。
- ID 008:
B
和B-Direct
(x2)出现在这里,因此我们可以使用B
.
- ID 009:所有选项都是直接的,所以我们选择最近的选项,
D-Direct
我知道如何获取最新的,但根据上述标准,我不确定如何调整
WITH data AS (
select d.*,
rank() over (
partition by ID
order by TIMESTAMP DESC
) as num
FROM table d
)
select ID, TIMESTAMP
from data
where num = 1
我可能会从以下内容开始。它不是非常漂亮,所以可能有更好的解决方案,但我认为它可以满足您的需求。
WITH data AS (
select d.*,
rank() over (
partition by ID
order by TIMESTAMP DESC
) as num
FROM table d
)
select ID,
CASE
WHEN EXISTS (SELECT * FROM table t WHERE t.id = d.id AND t.group || '-Direct' = d.group)
THEN replace(d.group, '-Direct')
ELSE d.group
END group
from data d
where num = 1
这会为每个 id 获取最近的一个(使用您当前的代码),但是 case
/exists
语句中的 select
子句会检查是否存在匹配项“-Direct”,如果是,我们从字符串中删除“-Direct”。
使用:
SELECT ID
,CASE WHEN MIN(group) OVER(PARTITION BY ID, REPLACE(group, '-Direct'))
= MAX(group) OVER(PARTITION BY ID, REPLACE(group, '-Direct'))
THEN group
ELSE REPLACE(group, '-Direct')
END AS grp
FROM tab
QUALIFY RANK() OVER(PARTITION BY ID ORDER BY TIMESTAMP DESC) = 1;
Qualify 确保采用每个时间戳的最新值,并且 case 表达式处理“-Direct”覆盖。
所以我有一个硬汉,我已经绞尽脑汁了一段时间。
假设我有一个 table 如下:
ID Group Timestamp Data
001 A 2021-04-13 12:51:12.063 content121
001 A-Direct 2021-04-13 12:52:13.063 content121
002 A-Direct 2021-04-13 12:50:14.063 content133
003 B-Direct 2021-04-13 12:55:12.063 content132
003 B 2021-04-13 12:56:11.063 content142
003 BA 2021-04-13 12:57:22.063 content153
004 D 2021-04-13 12:10:23.063 content113
004 C 2021-04-13 12:11:43.063 content144
005 C 2021-04-13 12:12:12.063 content111
005 A 2021-04-13 12:13:23.063 content100
005 D-Direct 2021-04-13 12:15:23.063 content121
006 A 2021-04-13 12:51:12.063 content121
006 B-Direct 2021-04-13 12:52:13.063 content121
007 A-Direct 2021-04-13 12:51:12.063 content121
007 A 2021-04-13 12:52:13.063 content121
008 B-Direct 2021-04-13 12:55:12.063 content132
008 B 2021-04-13 12:56:11.063 content142
008 B-Direct 2021-04-13 12:57:22.063 content153
009 B-Direct 2021-04-13 12:55:12.063 content132
009 C-Direct 2021-04-13 12:56:11.063 content142
009 D-Direct 2021-04-13 12:57:22.063 content153
所以我需要一个 table,其中每一行包含一个不同的 ID。但是选择ID的标准有点复杂。
默认选择应该是最近的条目,通过TIMESTAMP
选择。
但复杂性来自任何具有 -Direct
行的 ID。具体来说,如果一行有多个条目并且一个是(例如)A
,另一个是 A-Direct
,我们需要 A
。只有当字母匹配时才会出现这种情况。正如在 ID = 006
的例子中看到的,我们想要 B-Direct
因为它的对应物是 A
.
所以我要寻找的核心逻辑是
如果 ID 有以相同字符串开头的行,其中一行以 -Direct
结尾,请将其替换为删除的 -Direct
。
最终输出:
ID Group
001 A
002 A-Direct
003 BA
004 C
005 D-Direct
006 B-Direct
007 A
008 B
009 D-Direct
为了更加清楚,这里概述了每个 ID 发生的情况:
- ID 001:
A
后面跟着A-Direct
所以我们用A-Direct
代替A
- ID 002:
A-Direct
是唯一的结果,简单! - ID 003:
BA
、B
、B-Direct
是不同的,因此我们坚持使用最新的BA
. - ID 004: 没有直接的,所以我们只取最近的,
C
- ID 005:
D-Direct
是最近的,但是因为没有D
,所以我们坚持使用D-Direct
- ID 006:
B-Direct
是最近的,但是因为没有B
,所以我们坚持使用B-Direct
- ID 007:
A-Direct
后面是A
所以我们只取最近的一个,没问题。 - ID 008:
B
和B-Direct
(x2)出现在这里,因此我们可以使用B
. - ID 009:所有选项都是直接的,所以我们选择最近的选项,
D-Direct
我知道如何获取最新的,但根据上述标准,我不确定如何调整
WITH data AS (
select d.*,
rank() over (
partition by ID
order by TIMESTAMP DESC
) as num
FROM table d
)
select ID, TIMESTAMP
from data
where num = 1
我可能会从以下内容开始。它不是非常漂亮,所以可能有更好的解决方案,但我认为它可以满足您的需求。
WITH data AS (
select d.*,
rank() over (
partition by ID
order by TIMESTAMP DESC
) as num
FROM table d
)
select ID,
CASE
WHEN EXISTS (SELECT * FROM table t WHERE t.id = d.id AND t.group || '-Direct' = d.group)
THEN replace(d.group, '-Direct')
ELSE d.group
END group
from data d
where num = 1
这会为每个 id 获取最近的一个(使用您当前的代码),但是 case
/exists
语句中的 select
子句会检查是否存在匹配项“-Direct”,如果是,我们从字符串中删除“-Direct”。
使用:
SELECT ID
,CASE WHEN MIN(group) OVER(PARTITION BY ID, REPLACE(group, '-Direct'))
= MAX(group) OVER(PARTITION BY ID, REPLACE(group, '-Direct'))
THEN group
ELSE REPLACE(group, '-Direct')
END AS grp
FROM tab
QUALIFY RANK() OVER(PARTITION BY ID ORDER BY TIMESTAMP DESC) = 1;
Qualify 确保采用每个时间戳的最新值,并且 case 表达式处理“-Direct”覆盖。