4

ラボのアクセスデータをログに記録するためのテーブルがあります。次のようなテーブル構造:

create table accesslog
(
    userid int not null,
    direction int not null,
    accesstime datetime not null
);

このラボには、アクセス制御下にあるゲートが1つだけあります。したがって、ユーザーは「退出」する前に、まずラボに「入室」する必要があります。私の元の設計では、「方向」フィールドを1(ラボに入る場合)または-1(ラボから出る場合)のいずれかのフラグとして設定しました。次のようなクエリを使用できるようにします。

SELECT SUM(direction) FROM accesslog;

ラボ内の合計ユーザー数を取得します。理論的には、それは機能しました。「方向」は、任意のユーザーIDに対して常に1 => -1 => 1=>-1のパターンになるためです。

しかしすぐに、ラボのゲートからサーバーへの伝送パスでログメッセージが失われ、ビジーなネットワークまたはハードウェアの不具合によってドロップされることがわかりました。もちろん、シーケンス番号、ACK、再送信、ハードウェアの冗長性などを使用して伝送パスを強制することはできますが、最終的には次のようなものが得られる可能性があります。

userid   direction   accesstime
-------------------------------------
1         1          2013/01/03 08:30
1        -1          2013/01/03 09:20
1         1          2013/01/03 10:10
1        -1          2013/01/03 10:50
1        -1          2013/01/03 13:40
1         1          2013/01/03 18:00

これは、ユーザー「1」の最近のログです。10:50から13:40の間にラボに入るそのユーザーのログメッセージを1つ失ったことは明らかです。私がこのデータを照会している間、彼はまだラボにいるので、2013/01/0318:00以降に終了するログはまだありません。それは肯定的です。

私の質問は次のとおりです。SQLコマンドとのこのデータの不整合を「見つける」方法はありますか?私のシステムには合計5000人のユーザーがいて、ラボは24時間稼働しており、ラボがクリアされるような「魔法の時間」はありません。「方向」フィールドの連続性を行ごと、ユーザーごとにチェックするコードを書く必要があるとしたら、恐ろしいことです。

正しいデータでログを「修正」することは不可能であることを私は知っています。「ああ、userid = 1のデータの不整合の問題があります」を知りたいので、マークされた修正データを正しい最終統計に追加できます。

テーブルの構造を変えても大丈夫ですので、アドバイスをいただければ幸いです。

ありがとう。

編集:申し訳ありませんが、詳細については触れませんでした。

現在、混合SQLソリューションを使用しています。上に示した表はMySQLであり、高速ブラウジングの「リアルタイム」ステータスとして24時間以内のログのみが含まれています。

毎日午前03:00に、POSIXでC++で記述された事前にスケジュールされたプロセスが開始されます。このプロセスでは、統計データを計算し、プロプライエタリプロトコルのTCPソケットを介して毎日の統計をOracle DBに追加し、MySQLから古いデータを削除します。

オラクルの部分は私が扱っていないので、私はそれについて何もできません。毎日の最終的な統計が正しいことを確認したいだけです。

データサイズは1日あたり約200,000レコードです。これはおかしなことに聞こえますが、本当です。

4

3 に答える 3

2

DBMS について言及していないので、これは ANSI SQL です (最新の DBMS のほとんどで動作します)。

select userid,
       direction,
       accesstime,
       case 
         when lag(direction) over (partition by userid order by accesstime) = direction then 'wrong'
         else 'correct'
       end as status
from accesslog
where userid = 1

accesslog の各行について、行がルールに「違反」しているかどうかを示す「ステータス」列を取得します。

以下を使用して、無効なものを除外できます。

select *
from (
  select userid,
         direction,
         accesstime,
         case 
           when lag(direction) over (partition by userid order by accesstime) = direction then 'wrong'
           else 'correct'
         end as status
  from accesslog
  where userid = 1
) t
where status = 'wrong'

データベースの制約を使用してこの種のルールを適用する方法はないと思います (ただし、PostgreSQL の除外制約ここで役立つと感じています)。

于 2013-01-03T10:41:29.433 に答える
1

SUM() を WHERE フィールドで使用して、USER でフィルタリングしてみませんか。

0 または 1 以外の値が得られた場合は、問題があることは間違いありません。

于 2013-01-03T10:44:02.657 に答える
0

わかりました。a_horse_with_no_name さんから提供されたアイデアに感謝します。

私の最終的な解決策は、次のクエリです。

SELECT userid, COUNT(*), SUM(direction * rule) FROM (
    SELECT userid, direction, @inout := @inout * -1 AS rule
    FROM accesslog l, (SELECT @inout := -1) r
    ORDER by userid, accesstime
) g GROUP by userid;

最初に、「ルール」列の各行に対して 1 => -1 => 1 => -1 を生成する @inout を使用してパターンを作成しました。乗算積を計算して、方向フィールドとルール列を比較します。

特定のユーザーの奇妙なレコードがあっても問題ありません。各ユーザーは、「ルール」として同一または逆のパターンに従うことになっているためです。したがって、乗算積の合計は、COUNT() または -1 * COUNT() のいずれかに等しい必要があります。

SUM() と COUNT() をチェックすることで、どのユーザー ID が間違っていたかを正確に知ることができます。

于 2013-01-04T02:43:28.177 に答える