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 回の試行に一致するか、失敗するまで試行を続けると想定できます (1 回か 2 回失敗すると続行しない場合があることがわかりました)。

いくつかのデータの例を次に示します。

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 は 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

^ ただし、結果には NOT 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 NULLMySQL では を使用するよりも を使用する方が効率的であると私は信じて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 に答える