3

基本上,我正在尝试的结果是:“获取过去一定时间内有 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个不成功( =0)的记录。status当然,“连接”它们的唯一方法是通过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 计数。

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

4

1 回答 1

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)
        );

您还应该确保该表已正确编入索引。

编辑

我相信LEFT JOIN/IS NULL在 MySQL 中 using 比 using 更有效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
于 2013-01-21T20:13:22.877 回答