我有一个用于记录实验室访问数据的表。表结构如下:
create table accesslog
(
userid int not null,
direction int not null,
accesstime datetime not null
);
本实验室只有一个门禁,可以进行访问控制。因此,用户必须先“进入”实验室才能“离开”。在我的原始设计中,我将“方向”字段设置为1(用于进入实验室)或-1(用于离开实验室)的标记。这样我就可以使用像这样的查询:
SELECT SUM(direction) FROM accesslog;
获取实验室内的总用户数。从理论上讲,它起作用了;因为“方向”总是处于1 =>的模式中。 -1 => 1 => -1表示任何给定的用户ID。
但很快我发现日志消息会在从实验室门到服务器的传输路径中丢失,被繁忙的网络或硬件故障丢弃。当然,我可以使用序列号,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之间为该用户输入实验室丢失了一条日志消息。当我查询这些数据时,他仍在实验室中,所以在2013/01/03 18:00之后还没有退出日志;这是肯定的。
我的问题是:有没有办法“找到”这个数据与SQL命令不一致?我的系统中共有5000个用户,实验室24小时运行,实验室没有这样的“神奇时间”。如果我要编写代码逐行检查“方向”字段的连续性,我会很恐怖。
我知道用正确的数据“修复”日志是不可能的。我只是想知道“哦,我有一个userid = 1的数据不一致问题”,这样我就可以添加一个标记的修正数据到正确的最终统计数据。
任何建议都会受到赞赏,即使改变表格结构也没问题。
感谢。
编辑:对不起,我没有提到细节。
目前我正在使用混合SQL解决方案。上面显示的表是MySQL,它只包含24小时内的日志作为快速浏览的“实时”状态。
每天凌晨03:00,将启动在POSIX上用C ++编写的预先安排的流程。此过程将计算统计数据,并通过专有协议TCP套接字将每日统计信息添加到Oracle DB,然后它将从MySQL中删除旧数据。
Oracle部分不由我处理,我无能为力。我只是想确保每一天的最终统计数据是正确的。
数据大小每天约200,000条记录 - 我知道这听起来很疯狂,但确实如此。
答案 0 :(得分: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中的每一行,您将获得一个列" status"这表明行"是否打破"规则与否。
您可以使用以下方法过滤掉无效的内容:
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的排除约束可以在这里提供帮助)
答案 1 :(得分:1)
为什么不使用带有WHERE字段的SUM()来按USER过滤。
如果你得到0或1以外的任何东西,你肯定有问题。
答案 2 :(得分: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;
首先,我使用@inout创建了一个模式,它将产生1 => -1 => 1 => -1为“规则”列中的每一行。比我通过计算乘积来比较方向字段和规则列。
即使某些用户有奇数记录也没关系;因为每个用户都应该遵循相同或颠倒的模式作为“规则”。所以乘法乘积的总和应该等于COUNT()或-1 * COUNT()。
通过检查SUM()和COUNT(),我可以确切地知道哪个用户ID出错了。