实现表级检查约束

时间:2012-11-20 07:21:27

标签: sql oracle relational-database constraints range

我们有一个表格,其中包含取决于基本金额的价格。举个例子,假如基本金额小于或等于100则价格为10但如果基本金额大于100但小于或等于1000则价格为20,最后如果基数大于1000然后价格是30.我们的表的简化版本应该是这样的:

PRICE_CODE START_RANGE     END_RANGE  PRICE_AMOUNT 
100               0,00        100,00         10,00           
100             100,01       1000,00         20,00          
100            1000,01   99999999,99         30,00           
110               0,00   99999999,99         15,00 

使用列级检查约束,您可以轻松确保每条记录包含有效的范围信息。问题是我们还需要某种表级检查约束,以确保每个价格代码的范围信息不包含任何重叠或间隙,如下例所示:

PRICE_CODE START_RANGE     END_RANGE  PRICE_AMOUNT 
100               0,00        200,00         10,00           
100             100,01       1000,00         20,00          
100            1100,01   99999999,99         30,00           

我已经创建了一个有效的验证程序,但问题是我没有在数据库中找到任何地方来调用验证逻辑。当然,您不能放置在记录级别触发器中,但是当可以执行单独的插入,更新和删除时,语句级别触发器也不会起作用,并且只应针对最终结果验证范围。验证逻辑应该是这样的:

SELECT * FROM (              
SELECT price_code, start_range, end_range, price_amount
     , lag (end_range) OVER (PARTITION BY price_code ORDER BY end_range) prev_end
     , lead (start_range) OVER (PARTITION BY price_code ORDER BY start_range) next_start
  FROM my_test
ORDER BY price_code, start_range, end_range) 
 WHERE start_range <= prev_end
    OR end_range >= next_start
    OR (next_start - end_range) > 0.01
    OR (start_range - prev_end) > 0.01

当然,一种方法是将验证逻辑放在数据访问层中,但是仍然可以通过直接使用SQL来绕过验证。我感兴趣的是,如果有人有一些想法如何在数据库中实现这种“表级约束”,以确保没有人能够提交无效的范围数据。我们正在使用Oracle,因此我对基于Oracle的解决方案感兴趣,但我也对其他任何RDBMS如何解决这个问题感兴趣。

3 个答案:

答案 0 :(得分:4)

是否需要end_range列? end_range值也可以是下一个更高的start_range值。如果你这样做,就不可能出现差距和重叠。

答案 1 :(得分:2)

实现此目的的一种方法是相互引用外键。

为此,您需要一个支持MERGE语句或延迟约束的数据库,并且UNIQUE约束只允许一个NULL(或某些解决方法)

您所做的是首先使用半开放间隔切换到表示范围。这样做是为了使一个间隔的结束可以是另一行的外键引用,反之亦然。

如果我在任何地方使用方言,它可能是TSQL,而不是Oracle,因为这是我习惯的,但应该应用相同的概念

您创建一个如下所示的表:

CREATE TABLE T (
    PRICE_CODE int not null,
    START_RANGE decimal(10,2) null,
    END_RANGE decimal(10,2) null,
    constraint UQ_T_START UNIQUE (PRICE_CODE,START_RANGE),
    constraint UQ_T_END UNIQUE (PRICE_CODE,END_RANGE),
    constraint FK_T_PREV FOREIGN KEY (PRICE_CODE,START_RANGE) references T (PRICE_CODE,END_RANGE),
    constraint FK_T_NEXT FOREIGN KEY (PRICE_CODE,END_RANGE) references T (PRICE_CODE,START_RANGE),
    constraint CK_T_SANERANGE CHECK (START_RANGE < END_RANGE)
)

只允许一行拥有NULL START_RANGE,只有一行可以代表最低范围。同样,对于END_RANGE和最高范围。中间的所有行都必须引用它们的上一行和下一行范围。

您需要延迟约束或MERGE语句,因为为了例如在末尾插入新行,您需要同时插入此行(引用上一行),并更新上一行(要引用新行)以满足要满足的所有约束。这需要INSERTUPDATE,两者之间不进行约束检查,或者MERGE语句可以在单个语句中完成。


如果您不希望保留带有未定义边界的最低和最高范围,则只需强加一条规则,即NULL START_RANGEEND_RANGE的行不代表有效范围。但是将这些行保留在表中以允许上述结构起作用。

答案 2 :(得分:1)

我已经看到了使用快速刷新的物化视图的表级(或集合级)约束执行方法的概念。

我们的想法是将集合级需求转换为MV查询中的行级需求,然后将常规的基于行的检查约束应用于物化视图行。

例如,如果要将用户的条目数限制为特定数量,则可以创建按用户选择计数的席子。查看,然后应用check(mv_count_column <= desired_max)约束。

然而,由于对快速刷新的matviews的许多限制,这种方法实现和支持肯定是棘手的。我不确定它在你的情况下是否会起作用,因为快速刷新的MV不支持分析函数 - 也许你可以解决它。