我提前道歉 - 我不是数据库专家,所以我可能错过了一个非常明显的答案。
我有一个查询,用于计算特定状态的用户连续签到的次数。有5个登记入住状态,我们需要计算状态'U'的连续签到(有5种可能的状态:'U','O','D','F','M') 。如果有人签入任何其他状态,则条纹将停止。在经过多次学习,测试和失败之后,我想出了以下查询,以便主要基于this stackoverflow thread和this stack overflow thread返回任何用户的最长条纹(感谢这些帖子/解决方案的作者!)。
SELECT tmi, static_streak, MAX(count) AS consec
FROM (
SELECT @prev_tmi := @tmi AS prev_tmi,
@prev_s := @status AS prev_s,
@tmi := rtmc.team_member_id AS tmi,
@status := rtmc.status AS status,
@count := if(@prev_s = @status AND @status = 'U', @count + 1, 1) AS count
FROM readiness_team_member_checkins rtmc
CROSS JOIN (SELECT @count:=0) var_init
LEFT JOIN readiness_sessions rs ON rtmc.readiness_sessions_id = rs.readiness_sessions_id
WHERE rtmc.team_member_id = 83
ORDER BY rtmc.readiness_sessions_id, tmi
)
AS sub
GROUP BY tmi
以下是表格/测试数据:
CREATE TABLE IF NOT EXISTS `readiness_sessions` (
`readiness_sessions_id` int(11) NOT NULL AUTO_INCREMENT,
`session_date` date NOT NULL,
`session_level` varchar(3) DEFAULT NULL,
PRIMARY KEY (`readiness_sessions_id`,`session_date`)
);
INSERT INTO `readiness_sessions` (`readiness_sessions_id`,
`session_date`,
`session_level`
) VALUES
(1, '2015-09-02', '4'),
(2, '2015-09-03', '4'),
(6, '2015-09-04', '4'),
(7, '2015-09-05', '4'),
(8, '2015-09-06', '4'),
(10, '2015-09-07', '4'),
(11, '2015-09-07', '3'),
(12, '2015-09-08', '4'),
(13, '2015-09-09', '4'),
(14, '2015-09-29', '4'),
(15, '2015-09-30', '4'),
(16, '2015-10-01', '3'),
(17, '2015-10-02', '4'),
(18, '2015-10-06', '4'),
(19, '2015-10-20', '4');
CREATE TABLE IF NOT EXISTS `readiness_team_member_checkins` (
`readiness_team_member_checkins_id` int(11),
`readiness_sessions_id` int(11),
`readiness_team_checkins_id` int(11),
`team_member_id` int(11),
`status` enum('U','D','O','M','F','X')
);
INSERT INTO `readiness_team_member_checkins`
(`readiness_team_member_checkins_id`,
`readiness_sessions_id`,
`team_member_id`,
`status`) VALUES
(1, 1, 83, 'U'),
(2, 2, 83, 'O'),
(3, 6, 83, 'U'),
(4, 7, 83, 'U'),
(5, 8, 83, 'U'),
(6, 10, 83, 'M'),
(7, 11, 83, 'U'),
(8, 12, 83, 'U'),
(9, 13, 83, 'U'),
(10, 14, 83, 'U'),
(11, 15, 83, 'U'),
(12, 16, 83, 'D'),
(13, 17, 83, 'U'),
(14, 18, 83, 'U'),
(15, 19, 83, 'U');
我现在有一个附加功能,需要在某些情况下考虑第二个状态。
对新流程的要求基本上表明,“U”或“D”的签到状态不会破坏连续计数,但只有“U”会增加计数 - “D”将被忽略但不会中断连续计数。我尝试使用其他用户定义的变量来压缩上述查询,但我对这个级别的mysql并不熟悉。
包含的数据应该为当前实现返回5,并且对于附加要求将返回8。
我有两个问题:
当前查询通常会返回正确的数据,但有时会返回错误的数字,我需要重新加载查询并获得正确的数字。我假设这是由于用户定义的变量,我基本上学习了上周使用的变量。有什么暗示吗?我一直无法一再重现这个问题......
关于我如何干净地调整上述内容以获得“U”的最长连续条纹的任何提示都不会被二级要求中描述的“D”中断?
SQL小提琴here
答案 0 :(得分:1)
我使用您问题中的数据更新您的sqlfiddle
@count := if(condition1, true1, false1)
condition1 = ( @prev_s = 'U' OR @prev_s = 'D') <- mean i will continue chain
true1 = if(condition2, true2, false2)
false1 = 1 <- start new chain
condition2 = (@status = 'U')
true2 = @count + 1 <- increase count
false2 = if(condition3, true3, false3)
condition3 = (@status = 'D')
true3 = @count <- keep the chain count
false3 = 1 <- not 'U' or 'D' reset count
SELECT tmi, MAX(count) AS consec
FROM (
SELECT @prev_tmi := @tmi AS prev_tmi,
@prev_s := @status AS prev_s,
@tmi := rtmc.team_member_id AS tmi,
@status := rtmc.status AS status,
@count := if(@prev_s = 'U' OR @prev_s = 'D',
if(@status = 'U',
@count + 1,
if(@status = 'D', @count, 1)
),
1
) AS count
FROM readiness_team_member_checkins rtmc
CROSS JOIN (SELECT @count:=0) var_init
LEFT JOIN readiness_sessions rs ON rtmc.readiness_sessions_id = rs.readiness_sessions_id
WHERE rtmc.team_member_id = 83 AND rs.session_level in (3,4)
ORDER BY rtmc.readiness_sessions_id, tmi
)
AS sub
GROUP BY tmi
部分输出
| prev_tmi | prev_s | tmi | status | count |
|----------|--------|-----|--------|-------|
| 83 | (null) | 83 | U | 1 |
| 83 | U | 83 | O | 1 |
| 83 | O | 83 | U | 1 |
| 83 | U | 83 | U | 2 |
| 83 | U | 83 | U | 3 |
| 83 | U | 83 | M | 1 |
| 83 | M | 83 | U | 1 |
| 83 | U | 83 | U | 2 |
| 83 | U | 83 | U | 3 |
| 83 | U | 83 | U | 4 |
| 83 | U | 83 | U | 5 |
| 83 | U | 83 | D | 5 | <- skip count / no break chain
| 83 | D | 83 | U | 6 |
| 83 | U | 83 | U | 7 |
| 83 | U | 83 | U | 8 |
答案 1 :(得分:1)
以下查询计算给定成员的所有序列:
SELECT team_member_id, grp, count(*) AS consec
FROM (SELECT rtmc.*,
(@grp := if(@ms = concat_ws(':', rtmc.team_member_id, rtmc.status), @grp,
if(@ms := concat_ws(':', rtmc.team_member_id, rtmc.status), @grp + 1, @grp + 1)
)
) as grp
FROM readiness_team_member_checkins rtmc CROSS JOIN
(SELECT @grp := 0, @ms := '') params
WHERE rtmc.team_member_id = 83
ORDER BY rtmc.readiness_sessions_id, team_member_id
) s
GROUP BY team_member_id, grp;
如果您只想要最长的话,可以添加order by count(*) desc limit 1
。
关于使用变量的说明:
select
中分配变量并在不同的表达式中使用它们。 MySQL文档明确警告不要这样做;这可能是为什么你的查询有时会工作但并非总是如此。params
的子查询中这样做。readiness_sessions
,所以我删除了它。where status = 'U'
。答案 2 :(得分:1)
在阅读第1部分的戈登答案之后,我得到了第2部分的另一个版本
我创建了一个虚拟列以将'D', 'U'
作为单个组加入
并为COUNT()
SUM()
<强> SqlFiddleDemo 强>
SELECT team_member_id, grp, status, SUM(CASE WHEN status = 'U' THEN 1 ELSE 0 END ) AS consec
FROM (SELECT rtmc.*,
@ms,
(@grp := if(@ms = concat_ws(':', rtmc.team_member_id, rtmc.dummy_status), @grp,
if(@ms := concat_ws(':', rtmc.team_member_id, rtmc.dummy_status), @grp + 1, @grp + 1)
)
) as grp
FROM (SELECT rtmc.*, CASE
WHEN status = 'D' THEN 'U'
ELSE status
END as dummy_status
FROM
readiness_team_member_checkins rtmc
) rtmc
CROSS JOIN
(SELECT @grp := 0, @ms := '') params
WHERE rtmc.team_member_id = 83
ORDER BY rtmc.readiness_sessions_id, team_member_id
) s
GROUP BY team_member_id, grp, status;
输出
| team_member_id | grp | status | consec |
|----------------|-----|--------|--------|
| 83 | 1 | U | 1 |
| 83 | 2 | O | 0 |
| 83 | 3 | U | 3 |
| 83 | 4 | M | 0 |
| 83 | 5 | U | 8 |
| 83 | 5 | D | 0 |