检查每个组的列中是否存在值
Check whether value exists in column for each group
很难把我想做的事情用文字表达出来,所以搜索也很困难。
基本上我试图查看列中是否存在某个值,按组分区,然后向前传播该值。
在此示例中,我想检查用户是否已完成教程并设置一个继续前进的标志。
pk | user | ... | activity
1 | A | ... | "login"
2 | A | ... | "started_tutorial"
3 | A | ... | "completed_tutorial"
4 | A | ... | "some other activity"
5 | A | ... | "logout"
5 | B | ... | "login"
6 | B | ... | "logout"
我觉得应该是这样的
select *,
check(activity in ('completed_tutorial')) as completed_activity
from tbl
但我认为我不能在 select 语句中使用 check
,这将是一个常量标志,而不是仅在找到后才设置为 true。
我想要得到的例子:
pk | user | ... | activity | completed_tutorial
1 | A | ... | "login" | 0
2 | A | ... | "started_tutorial" | 0
3 | A | ... | "completed_tutorial" | 1
4 | A | ... | "some other activity" | 1
5 | A | ... | "logout" | 1
5 | B | ... | "login" | 0
6 | B | ... | "logout" | 0
您可以使用 HAVING
clause 筛选 SQL 个组。例如,您可以按用户及其 activity 对 table 进行分组,然后对其进行过滤以仅包含已完成教程的用户:
SELECT user FROM tbl
GROUP BY user, activity
HAVING activity = 'completed_tutorial';
编辑: OP 编辑了他们的问题后,这是我的新答案。在这里,我假设您的 table 有一个日期字段。
SELECT *, COALESCE(date >= (
SELECT date FROM tbl WHERE activity = 'completed_tutorial'
AND user = outertbl.user
), FALSE)
FROM tbl AS outertbl
ORDER BY date
请注意,此类查询在未优化时本质上是 N²,因此我建议您只从数据库中获取数据,然后在您的程序中对其进行处理。
你可以试试这样的
SELECT a.*, coalesce(b.completed, 0)
FROM tbl a
LEFT JOIN (SELECT user, 1 completed
FROM tbl
WHERE user = a.user
AND activity='completed_tutorial') b
ON a.user = b.user AND b.pk >= a.pk
它假定最多有一行 activity='completed_tutorial'
并且用户标识 "session"。如果用户可以制作多个教程,则必须添加另一个字段。
我不确定这样做的速度,但是下面的解决方案呢?
SELECT
user
,max(CASE
WHEN activity = "completed_tutorial" THEN 1
ELSE 0
END) AS completed_tutorial
FROM tbl
GROUP BY user
;
SELECT user FROM tbl
GROUP BY user
HAVING COUNT(CASE WHEN activity = 'completed_tutorial' THEN 1 ELSE 0 END) = 1;
这会给你所有完成教程的用户,至少一次。
SELECT 来自 tbl 的用户
按用户分组
计数(当 activity = 'completed_tutorial' THEN 1 ELSE 0 END 时的情况)= 1;
这将为您提供所有完成教程的用户,恰好一次。
很难把我想做的事情用文字表达出来,所以搜索也很困难。
基本上我试图查看列中是否存在某个值,按组分区,然后向前传播该值。
在此示例中,我想检查用户是否已完成教程并设置一个继续前进的标志。
pk | user | ... | activity
1 | A | ... | "login"
2 | A | ... | "started_tutorial"
3 | A | ... | "completed_tutorial"
4 | A | ... | "some other activity"
5 | A | ... | "logout"
5 | B | ... | "login"
6 | B | ... | "logout"
我觉得应该是这样的
select *,
check(activity in ('completed_tutorial')) as completed_activity
from tbl
但我认为我不能在 select 语句中使用 check
,这将是一个常量标志,而不是仅在找到后才设置为 true。
我想要得到的例子:
pk | user | ... | activity | completed_tutorial
1 | A | ... | "login" | 0
2 | A | ... | "started_tutorial" | 0
3 | A | ... | "completed_tutorial" | 1
4 | A | ... | "some other activity" | 1
5 | A | ... | "logout" | 1
5 | B | ... | "login" | 0
6 | B | ... | "logout" | 0
您可以使用 HAVING
clause 筛选 SQL 个组。例如,您可以按用户及其 activity 对 table 进行分组,然后对其进行过滤以仅包含已完成教程的用户:
SELECT user FROM tbl
GROUP BY user, activity
HAVING activity = 'completed_tutorial';
编辑: OP 编辑了他们的问题后,这是我的新答案。在这里,我假设您的 table 有一个日期字段。
SELECT *, COALESCE(date >= (
SELECT date FROM tbl WHERE activity = 'completed_tutorial'
AND user = outertbl.user
), FALSE)
FROM tbl AS outertbl
ORDER BY date
请注意,此类查询在未优化时本质上是 N²,因此我建议您只从数据库中获取数据,然后在您的程序中对其进行处理。
你可以试试这样的
SELECT a.*, coalesce(b.completed, 0)
FROM tbl a
LEFT JOIN (SELECT user, 1 completed
FROM tbl
WHERE user = a.user
AND activity='completed_tutorial') b
ON a.user = b.user AND b.pk >= a.pk
它假定最多有一行 activity='completed_tutorial'
并且用户标识 "session"。如果用户可以制作多个教程,则必须添加另一个字段。
我不确定这样做的速度,但是下面的解决方案呢?
SELECT
user
,max(CASE
WHEN activity = "completed_tutorial" THEN 1
ELSE 0
END) AS completed_tutorial
FROM tbl
GROUP BY user
;
SELECT user FROM tbl
GROUP BY user
HAVING COUNT(CASE WHEN activity = 'completed_tutorial' THEN 1 ELSE 0 END) = 1;
这会给你所有完成教程的用户,至少一次。
SELECT 来自 tbl 的用户 按用户分组 计数(当 activity = 'completed_tutorial' THEN 1 ELSE 0 END 时的情况)= 1; 这将为您提供所有完成教程的用户,恰好一次。