这是创建SQL断言的正确方法吗?

时间:2011-02-06 12:38:16

标签: sql assertion

以便进行以下类型的断言

create assertion assert  
check "EMPTY SET" = (select User         
     from Video  
     where date=current_date()  
     group by user  
having count(*) >= 10

这个断言是对的吗?

create assertion assert  
check  0 = (select count(*)  
     from Video  
     where date=current_date()  
     group by user  
having count(*) >= 10

1 个答案:

答案 0 :(得分:10)

有关CREATE ASSERTION的完整详细信息,请参阅ISO SQL-92标准规范。

CHECK定义应该在括号中。

CURRENT_DATE没有括号。

USERDATE是保留字。

SQL语句应以分号结尾。

SQL关键字应为大写。

尝试更像这样的事情:

CREATE ASSERTION assert  
CHECK (0 = (
            SELECT COUNT(*)  
              FROM Video  
             WHERE my_date = CURRENT_DATE  
             GROUP 
                BY my_user
            HAVING COUNT(*) >= 10
           ));

您可以使用在线Mimer SQL-92 Validator测试语法是否正确。但是,您还应该测试您的逻辑,例如CURRENT_DATE是不确定的。

另外,我认为ASSERTION不会咬人。当子查询的基数小于10时,它将返回零行,0 = empty set将评估为UNKNOWN。当子查询的基数为10或更大时,搜索条件将评估TRUE。 SQL-92标准状态

  

如果和,则不满足断言   只有评估的结果   搜索条件是错误的。

请注意,您可以将CHECK (0 = (SELECT COUNT(*) FROM...))构造替换为CHECK (NOT EXISTS (SELECT * FROM...)),我发现后者更容易编写。


更新:

  

我应该如何使用编写断言   检查不存在?

正如我上面所说,你的逻辑看似有缺陷,所以很难正确实施;)

假设规则是将视频限制为每个用户每天10个。因为这只涉及一个表,所以使用表级CHECK约束会更合适;在更新表时检查这样的约束,这在这种情况下是足够的(没有理由为什么它不能是ASSERTION,理论上每次任何架构中的表已更新):

ALTER TABLE Video ADD 
   CONSTRAINT video_limit_10_per_user_per_day
      CHECK (NOT EXISTS (
                         SELECT v1.my_user, v1.my_date
                           FROM Video AS V1
                          GROUP BY v1.my_user, v1.my_date
                         HAVING COUNT(*) > 10
                        ));

更新2:

  

谢谢,现在让我们说我们想要限制   每个用户每年100个视频   这种情况使用current_date   必要的不是吗?

再次考虑只有在更新表/模式中的数据时才会检查CHECK / ASSERTION。在约束中使用CURRENT_DATE(和其他非确定性函数)的问题在于,只需通过从一个时间段到下一个时间段的时钟滴答,但是如果数据未被更改,则业务规则可以无效在此期间,将无法检测到数据完整性故障,并且数据库将包含无效数据。

另一个考虑因素是一年的背景意味着什么。

可能是日历年(1月1日至12月31日)或企业定义的其他固定日期(例如4月1日至3月31日),在这种情况下,按年份分组,然后按用户进行计数是微不足道的。

更有趣的情况是规则限制任何 12个月期间的计数;将此扩展到过去和未来都避免了上述“非确定性”问题。

考虑一个standard approach of using an auxiliary calendar table,其中包含适用于企业的每一天的一行,只有在需要时才会延伸到过去和未来,应该只包含几千行。每行都有一个日期作为键,该日期的第二列加上一年(如有必要,您可以按一天的粒度微调“一年”的定义!)测试将涉及加入日历表,在日历日期和用户上分组并计算例如像这样的东西:

SELECT C1.dt, V1.my_user
  FROM Video AS V1
       INNER JOIN Calendar AS C1
          ON (V1.my_date BETWEEN C1.dt AND C1.dt_plus_one_year)
 GROUP 
    BY C1.dt, V1.my_user
HAVING COUNT(*) > 100;

这可以放在CHECK (NOT EXISTS (...约束中。这可能仍然是一个表级CHECK约束:因为Calendar表是一个辅助表,它只会引发不频繁的受控更新(如果需要,也可以是ASSERTION)。