有关详细信息,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
如果需要也可以是一个)。