如何检查SQL表中的数据完整性?

时间:2013-01-03 10:26:33

标签: sql

我有一个用于记录实验室访问数据的表。表结构如下:

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条记录 - 我知道这听起来很疯狂,但确实如此。

3 个答案:

答案 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出错了。