基于String在SQL(Snowflake)中选择一行
Posted
技术标签:
【中文标题】基于String在SQL(Snowflake)中选择一行【英文标题】:Selecting a row in SQL (Snowflake) based on String 【发布时间】:2021-11-09 23:21:14 【问题描述】:所以我在这里有一个顽固的人,我已经绞尽脑汁一段时间了。
假设我有一张如下表:
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
所以我需要一个表,其中每一行都包含一个不同的 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
【问题讨论】:
每个 id 可以有多个 *-direct 条目吗?还是每个 id 具有相同组的多行? @EdmCoff,是的!参见 ID 008,有两个 B-Direct。但是两者的优先级都比B低。我还添加了一个ID 009来进一步说明。如果 Direct 上有两个不同的组具有相同的 ID(B-Direct AND C-Direct)或(B-Direct AND B-Direct),那么它只是最新的。 【参考方案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 的最新 ID(使用您当前的代码),但 select
子句中的 case
/exists
语句会检查是否存在没有“-Direct”的匹配项,如果有,我们从字符串中删除“-Direct”。
【讨论】:
这不适用于我的数据。是什么||做什么?||
等价于concat
。所以t.group || '-Direct' = d.group
正在检查是否存在与 d.group(例如“B-Direct”)匹配的 t.group(例如“B”)【参考方案2】:
使用:
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”覆盖。
【讨论】:
以上是关于基于String在SQL(Snowflake)中选择一行的主要内容,如果未能解决你的问题,请参考以下文章
在没有 JavaScript 的情况下将 Oracle PL/SQL 移植到 Snowflake
Snowflake 中的 SQL Server 等效表类型是啥