查找过去有相关记录的记录

时间:2013-01-21 20:03:17

标签: mysql sql

基本上,我尝试的结果是:“获取成功的记录的数量,这些记录在过去的一定时间内有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个计数。

我已经尝试了很多组合,我认为我的大脑只是油炸。

1 个答案:

答案 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