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

如何检查SQL表中的数据完整性?,sql,Sql,我有一个用于记录实验室访问数据的表。表结构如下: create table accesslog ( userid int not null, direction int not null, accesstime datetime not null ); userid direction accesstime ------------------------------------- 1 1 2013/01/03 08:30 1

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

create table accesslog
(
    userid int not null,
    direction int not null,
    accesstime datetime not null
);
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(用于进入实验室)或-1(用于离开实验室)的标志。因此,我可以使用以下查询:

SELECT SUM(direction) FROM accesslog;
获取实验室内的总用户数。理论上,它是有效的;因为对于任何给定的用户ID,“方向”总是以1=>-1=>1=>-1的模式出现

但很快我发现日志消息将丢失在从实验室门到服务器的传输路径中,可能是由于网络繁忙或硬件故障而丢失。当然,我可以使用序列号、ACK、重传、硬件冗余等强制传输路径,但最终我可能会得到如下结果:

create table accesslog
(
    userid int not null,
    direction int not null,
    accesstime datetime not null
);
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数据库中,然后从MySQL中删除旧数据

甲骨文部分不是我处理的,我对此无能为力。我只是想确保每天的最终统计数据是正确的


数据大小约为每天200000条记录——我知道这听起来很疯狂,但事实确实如此。

您没有说明您的DBMS,因此这是ANSI SQL(适用于大多数现代DBMS)

对于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的排除约束在这里会有所帮助)

为什么不使用SUM()和WHERE字段来按用户过滤呢


如果您得到的不是0或1,那么您肯定有问题。

好的,我已经解决了。感谢一匹没有名字的马提供的创意

我的最终解决方案是这个查询:

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创建了一个模式,该模式将为“rule”列中的每一行生成1=>-1=>1=>-1。然后通过计算乘法积,将方向字段与规则列进行比较

即使某些用户有奇数记录也可以;因为每个用户都应该遵循相同或相反的模式作为“规则”。所以乘法乘积的总和应该等于COUNT()或-1*COUNT()


通过检查SUM()和COUNT(),我可以准确地知道哪个用户ID出错。

您使用的是哪种RDBMS?问题在于“消息”没有传递到数据库服务器。使用一个保证交付的消息代理(如果你是Microsoft的话,请使用MSMQ)这对于SQL来说很难做到,但在代码上却很简单。您的数据量是多少?你用什么语言来驱动SQL?对不起,我没有提到。这是MySQL 5.5。这确实是我的第一反应。但请看我上面的例子:和(方向)是0,但仍然有数据不一致。使用SUM()可以检测一些错误情况,但不是全部。好主意。我也许能从这方面继续下去。