1

ユーザーの状態を記録し、保持している変更の記録に基づいて履歴をレポートできるようにしたいと考えています。私はこれを SQL (PostgreSQL を使用) で実行しようとしていますが、次のようなユーザーの変更を記録するための提案された構造があります。

CREATE TABLE users (
  userid SERIAL NOT NULL PRIMARY KEY, 
  name VARCHAR(40), 
  status CHAR NOT NULL
);

CREATE TABLE status_log (
  logid SERIAL, 
  userid INTEGER NOT NULL REFERENCES users(userid), 
  status CHAR NOT NULL, 
  logcreated TIMESTAMP
);

これは、データに基づいて、私が提案したテーブル構造です。

ステータス フィールドの「a」はアクティブなユーザーを表し、「s」は停止中のユーザーを表します。

INSERT INTO status_log (userid, status, logcreated) VALUES (1, 's', '2008-01-01'); 
INSERT INTO status_log (userid, status, logcreated) VALUES (1, 'a', '2008-02-01'); 

したがって、このユーザーは 1 月 1 日に停止され、2 月 1 日に再びアクティブになりました。

2008 年 1 月 15 日に停止された顧客のリストを取得したい場合、userid 1 が表示されるはずです。2008 年 2 月 15 日に停止された顧客リストを受け取った場合、userid 1 は表示されません。

1) これは、この種のクエリに対してこのデータを構造化するための最良の方法ですか?

2) この構造または提案された変更された構造のいずれかでデータを照会して、単純に日付 (1 月 15 日など) を取得し、SQL のみでその日付にアクティブなステータスを持っていた顧客のリストを見つけるにはどうすればよいですか? これはSQLの仕事ですか?

4

4 に答える 4

2

これは実行できますが、各ログの終了日を保存すると、はるかに効率的になります。あなたのモデルでは、次のようなことをしなければなりません:

select l1.userid
from status_log l1
where l1.status='s'
and l1.logcreated = (select max(l2.logcreated)
                     from status_log l2
                     where l2.userid = l1.userid
                     and   l2.logcreated <= date '2008-02-15'
                    );

追加の列を使用すると、次のようになります。

select userid
from status_log
where status='s'
and logcreated <= date '2008-02-15'
and logsuperseded >= date '2008-02-15';

(構文エラーについては申し訳ありません。私は Postgresql を知りません。)

Phil によって提起されたいくつかのさらなる問題に対処するには:

ユーザーは、アクティブから一時停止、キャンセル、再びアクティブに移行する場合があります。これは単純化されたバージョンであり、実際にはさらに多くの状態があり、人々はある状態から別の状態に直接移動できます。

これは、次のようにテーブルに表示されます。

userid  from       to         status
FRED    2008-01-01 2008-01-31 s
FRED    2008-02-01 2008-02-07 c
FRED    2008-02-08            a

現在のレコードの「終了日」に null を使用しました。2999-12-31 のような将来の日付を使用することもできましたが、いくつかの点で null の方が望ましいです。

さらに、現在のステータスには「終了日」もありません。これにより、クエリが少し壊れていると思いますか?

はい、私のクエリは次のように書き直す必要があります

select userid
from status_log
where status='s'
and logcreated <= date '2008-02-15'
and (logsuperseded is null or logsuperseded >= date '2008-02-15');

この設計の欠点は、ユーザーのステータスが変更されるたびに、現在の status_log の日付を終了し、新しいものを作成する必要があることです。しかし、それは難しいことではなく、おそらくクエリの利点がこれを上回っていると思います。

于 2008-10-08T11:33:09.410 に答える
1

Postgres は分析クエリをサポートしていますか? これにより、2008-02-15 のアクティブ ユーザーが得られます。

select userid
from
(
select logid, 
       userid, 
       status, 
       logcreated,
       max(logcreated) over (partition by userid) max_logcreated_by_user
from   status_log
where  logcreated <= date '2008-02-15'
)
where  logcreated = max_logcreated_by_user
  and  status     = 'a'
/
于 2008-10-08T20:30:09.840 に答える
0

@Phil

私はトニーの解決策が好きです。説明されている状況を最も適切にモデル化しているようです。特定のユーザーは、特定の期間(1分、1時間、1日など)のステータスを持ちますが、それは一定期間であり、瞬間的なものではありません。特定の期間に誰がアクティブであったかを知りたいので、情報を期間としてモデル化することが最善のアプローチのように思われます。

追加のステータスが問題になるかどうかはわかりません。誰かがアクティブで、一時停止され、キャンセルされ、そして再びアクティブになった場合、それらのステータスのそれぞれは、特定の期間に適用されますか?数秒や1分など、非常に短い時間である可能性がありますが、それでも長い時間かかります。

ある人のステータスが特定の日に複数回変化する可能性があることを懸念していますが、特定の日に誰がアクティブであったかを知りたいですか?もしそうなら、あなたは特定の日にアクティブであることが何を意味するかをより具体的に定義する必要があります。彼らがその日のいずれかの部分で活動していたのに十分であれば、トニーの答えはそのままでうまくいきます。特定の日に特定の時間アクティブにする必要がある場合は、Tonyのソリューションを変更して、時間の長さ(時間、分、または日)を単純に決定し、WHERE句にさらに制限を追加することができます。適切な日付、ステータス、およびそのステータスの期間を取得します。

現在のステータスに「終了日」がないことに関しては、終了日がnull許容である限り、それも問題ありません。「WHEREenddate<='2008-08-15'またはenddateがnull」のようなものを使用するだけです。

于 2008-10-08T20:24:25.093 に答える
0

@Tony「終了」日は必ずしも適用されません。

ユーザーは、アクティブから一時停止、キャンセル、再びアクティブに移行する場合があります。これは単純化されたバージョンであり、実際にはさらに多くの状態があり、人々はある状態から別の状態に直接移動できます。

さらに、現在のステータスには「終了日」もありません。これにより、クエリが少し壊れていると思いますか?

于 2008-10-08T13:54:18.390 に答える