基本上,我尝试的结果是:“获取成功的记录的数量,这些记录在过去的一定时间内有0 不成功的记录”。 “成功”和“不成功”只是引用列的值。
虽然它有点复杂,但这里是我正在处理的表格的描述:
`log`
id int PRIMARY KEY AUTO_INCREMENT
fingerprint_id int (foreign key)
status boolean
date timestamp
我们所拥有的小系统的工作流程是,当用户滑动指纹时,会在此表中添加一条记录,并根据匹配是否设置status
(再次,它还有更多,我'我只是想简化)。我们根据用户执行此操作获得fingerprint_id
,因此这是将记录与个人相关联的标识符。
现在,我们要求他们最多尝试3次。所以,他们可以在3月1日,3月2日,3月3日或者根本不匹配。这意味着他们的“组”中可以有1,2或3条记录。虽然事实并非如此,但我们可以假设用户将继续尝试,直到他们匹配或达到3次失败尝试(我们发现有时人们在失败一次或两次后可能无法继续尝试。)
以下是一些数据的示例:
id fp_id status date
----------------------------------------
20 2 0 '2013-01-21 12:30:01'
21 2 0 '2013-01-21 12:30:05'
22 2 0 '2013-01-21 12:30:10'
23 9 1 '2013-01-21 12:31:30'
24 1 0 '2013-01-21 12:35:00'
25 1 1 '2013-01-21 12:35:05'
在数据中,用户(fingerprint_id
)2尝试了3次并且从未匹配过。用户9在第一次尝试时匹配。用户1尝试过一次并失败,然后再次尝试并匹配。
重点是找出在35秒之前有多少成功(status
= 1)日志记录有0个不成功(status
= 0)的记录。当然,“连接”它们的唯一方法是fingerprint_id
。
同样,我们承担了很多事情,但那没关系。
这是我的尝试:
SELECT COUNT(*)
FROM log AS log_main
WHERE log_main.status=1 AND
(SELECT COUNT(*)
FROM log AS log_inner
WHERE log_inner.fingerprint_id=log_main.fingerprint_id AND
log_inner.status=0 AND
log_inner.date<log_main.date AND log_inner.date>=(log_main.date - INTERVAL 35 SECOND))=0
^我希望这个能够选择所有成功记录,这些记录在35秒之前(对于该用户)发生了0次不成功记录。但我不知道,因为查询需要600秒以上。我刚刚发现了如何扩展MySQL Workbench的最大超时,但不管怎样,它需要很长时间。该表总共有大约120,000条记录,因此我不确定这是否足以使此查询变慢。
无论如何,这是另一次尝试:
SELECT COUNT(*)
FROM (SELECT log.fingerprint_id, log.date
FROM log
WHERE log.status=1) successful,
(SELECT log.fingerprint_id, log.date
FROM log
WHERE log.status=0) unsuccessful
WHERE successful.fingerprint_id=unsuccessful.fingerprint_id AND
unsuccessful.date<successful.date AND unsuccessful.date>=(successful.date - INTERVAL 35 SECOND)
^我觉得这个更接近,但当然,没有比较过去有多少记录匹配的“数量”。这是我对如何解决而感到困惑的部分。我有一种感觉它与GROUP BY
或使用IN
有关,但我所做的只是似乎不起作用(从某种意义上说它超过600秒或类似的东西) )。这是我用GROUP BY
SELECT successful.id, COUNT(*) cnt
FROM (SELECT log.fingerprint_id, log.date, log.id
FROM log
WHERE log.status=1) successful,
(SELECT log.fingerprint_id, log.date, log.id
FROM log
WHERE log.status=0) unsuccessful
WHERE successful.fingerprint_id=unsuccessful.fingerprint_id AND
unsuccessful.date<successful.date AND unsuccessful.date>=(successful.date - INTERVAL 35 SECOND)
GROUP BY successful.id
^但结果只包含非0计数的行。我猜这是因为WHERE
条款。但我只需要0个计数。
我已经尝试了很多组合,我认为我的大脑只是油炸。
答案 0 :(得分:1)
尝试使用NOT EXISTS
代替COUNT = 0
。这应该会好得多。
SELECT COUNT(*)
FROM log AS log_main
WHERE log_main.status=1
AND NOT EXISTS
( SELECT 1
FROM log AS log_inner
WHERE log_inner.fingerprint_id=log_main.fingerprint_id
AND log_inner.status = 0
AND log_inner.date < log_main.date
AND log_inner.date >= (log_main.date - INTERVAL 35 SECOND)
);
您还应该确保表格已正确编入索引。
修改强>
我认为在MySQL中使用LEFT JOIN/IS NULL
比使用NOT EXISTS
更有效,所以这将比上面的表现更好(尽管可能不是很明显):
SELECT COUNT(*)
FROM log AS log_main
LEFT JOIN log AS log_inner
ON log_inner.fingerprint_id=log_main.fingerprint_id
AND log_inner.status = 0
AND log_inner.date < log_main.date
AND log_inner.date >= (log_main.date - INTERVAL 35 SECOND)
WHERE log_main.status = 1
AND Log_inner.fingerprint_id IS NULL;
编辑2
要获得1次或2次尝试的记录等,我仍然会使用JOIN,但是如此:
SELECT COUNT(*)
FROM ( SELECT log_Main.id
FROM log AS log_main
INNER JOIN log AS log_inner
ON log_inner.fingerprint_id=log_main.fingerprint_id
AND log_inner.status = 0
AND log_inner.date < log_main.date
AND log_inner.date >= (log_main.date - INTERVAL 35 SECOND)
WHERE log_main.status = 1
AND Log_inner.fingerprint_id IS NULL
GROUP BY log_Main.id
HAVING COUNT(log_Inner.id) = 1
) d