3

我们有一个表格,其中包含取决于基本金额的价格。例如,如果基础金额小于或等于 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 如何解决这个问题感兴趣。

4

3 回答 3

4

end_range 列是否必要?end_range 值也可以是下一个更高的 start_range 值。如果你这样做,间隙和重叠是不可能的。

于 2012-11-20T07:40:43.310 回答
2

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

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

例如,如果您想将用户的条目数量限制在一定数量,您可以创建一个 select-count-group-by-user mat。查看,然后应用check(mv_count_column <= desired_max)约束。

但是,由于对快速刷新的 matview 有许多限制,这种方法实施和支持肯定会很棘手。我不确定它是否适用于您的情况,因为快速刷新的 MV 不支持分析功能 - 也许您可以解决它。

于 2012-11-20T08:21:45.897 回答
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语句,例如,为了在末尾插入新行,您需要插入此行(引用前一行)和更新前一行(引用新行)所有要满足的约束。这需要 anINSERT和 anUPDATE两者之间不发生约束检查,或者MERGE可以在单个语句中完成两者的语句。


如果您不想将最低和最高范围保留为未定义的范围,则只需强加一个规则,即带有 aNULL START_RANGEEND_RANGE不代表有效范围的行。但是将这些行保留在表中以允许上述结构工作。

于 2012-11-20T08:32:19.813 回答