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 日获得暂停的客户列表,那么应该显示用户 ID 1。如果我在 2008 年 2 月 15 日收到一份暂停的客户名单,那么用户 ID 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。)

为了解决菲尔提出的一些进一步的问题:

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

这将出现在表格中,如下所示:

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

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

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

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

于 2008-10-08T13:54:18.390 回答
0

@菲尔

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

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

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

至于当前状态没有“结束日期”,只要结束日期可以为空,那也没有问题。只需使用类似“WHERE enddate <= '2008-08-15' or enddate is null”的内容。

于 2008-10-08T20:24:25.093 回答