SQL-状态机-基于变更集的历史数据报告

SQL-状态机-基于变更集的历史数据报告,sql,postgresql,Sql,Postgresql,我希望记录用户状态,然后能够根据我们保存的更改记录进行历史报告。我正试图在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 INTEGE

我希望记录用户状态,然后能够根据我们保存的更改记录进行历史报告。我正试图在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日获得一份暂停的客户名单,那么userid1应该出现。如果我在2008年2月15日收到一份暂停的客户名单,那么userid1应该不会出现

1) 这是为这种查询构造数据的最佳方法吗


2) 我如何查询此结构或您建议的修改结构中的数据,以便只需输入一个日期(例如1月15日),并仅在SQL中查找在该日期处于活动状态的客户列表?这是SQL的工作吗?

可以这样做,但如果存储每个日志的结束日期,效率会更高。对于您的模型,您必须执行以下操作:

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'
                    );
我使用空值表示当前记录的“截止”日期。我本可以使用像2999-12-31这样的未来日期,但在某些方面空值更可取

此外,当前状态也没有“结束日期”,所以我认为这会稍微打断您的查询

是的,我的查询必须重写为

select userid
from status_log
where status='s'
and logcreated <= date '2008-02-15'
and (logsuperseded is null or logsuperseded >= date '2008-02-15');
选择userid
从状态日志
在哪里
和logcreated=日期“2008-02-15”);
这种设计的一个缺点是,每当用户的状态发生变化时,您都必须结束当前状态日志的日期,并创建一个新的日志。然而,这并不困难,我认为查询的优势可能超过这一点。

@Tony“结束”日期不一定适用

用户可能会从活动状态移动到暂停状态、取消状态或再次活动状态。这是一个简化的版本,实际上,有更多的州,人们可以直接从一个州转移到另一个州

此外,当前状态也没有“结束日期”,因此我认为这稍微打破了您的查询?

@Phil

我喜欢托尼的解决方案。它似乎最恰当地模拟了所描述的情况。任何特定的用户在给定的时间段(一分钟、一小时、一天等)内都有一个状态,但它是一个持续时间,而不是一个瞬间。因为您想知道谁在某段时间内处于活动状态,所以将信息建模为持续时间似乎是最好的方法

我不确定其他状态是否有问题。如果某人处于活动状态,然后被暂停,然后被取消,然后再次处于活动状态,那么这些状态中的每一个都将在给定的持续时间内适用,不是吗?它的持续时间可能很短,例如几秒钟或一分钟,但它们仍然会持续一段时间

您是否担心一个人的状态在给定的一天内会发生多次变化,但您想知道在给定的一天内谁是活跃的?如果是这样的话,那么您只需要更具体地定义在给定的一天中活动意味着什么。如果他们在那天的任何时候都很活跃就足够了,那么托尼的回答也很有效。如果它们必须在给定的一天中处于活动状态一段时间,那么可以修改Tony的解决方案,以简单地确定时间长度(以小时、分钟或天为单位),并在WHERE子句中添加进一步的限制,以检索该状态下的适当日期、状态和时间长度


至于当前状态没有“结束日期”,只要结束日期可以为空,这也没有问题。只需使用类似“WHERE enddatePostgres支持分析查询吗?这将为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'
/
选择userid
从…起
(
选择logid,
用户ID,
地位
创建日志,
max(logcreated)over(按用户ID划分)max_logcreated_按用户划分
从状态日志

如果logcreated是,则它支持分析查询->
select userid
from status_log
where status='s'
and logcreated <= date '2008-02-15'
and (logsuperseded is null or logsuperseded >= date '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'
/