一尘不染

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

sql

为了做出以下类型的断言

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

阅读 161

收藏
2021-05-23

共1个答案

一尘不染

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

CHECK定义应在括号中。

CURRENT_DATE 没有括号。

USER并且DATE是保留字。

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...)),后者的,我觉得更容易编写。


更新:

我应该如何使用CHECK NOT EXISTS编写断言?

就像我在上面说的那样,您的逻辑似乎有缺陷,因此很难正确实施;)

假设规则是将视频限制为每位用户每天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个月内的累计次数;将其扩展到过去和将来可以避免上述“不确定性”问题。

考虑一种使用辅助日历表标准方法,该辅助日历表每天包含一行(适用于企业),并且仅在需要时才扩展到过去和将来,但仍应仅包含几千行。每行将日期作为关键字,该日期的第二列加上一年(如果需要,您可以按一天的间隔对“一年”的定义进行微调!)的测试将涉及加入日历表格,对日历日期和用户进行分组并进行计数,例如:

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如果需要的话,也可能是一个更新)。

2021-05-23