我们有一个表格,其中包含取决于基本金额的价格。举个例子,假如基本金额小于或等于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如何解决这个问题感兴趣。
答案 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
语句,因为为了例如在末尾插入新行,您需要同时插入此行(引用上一行),并更新上一行(要引用新行)以满足要满足的所有约束。这需要INSERT
和UPDATE
,两者之间不进行约束检查,或者MERGE
语句可以在单个语句中完成。
如果您不希望保留带有未定义边界的最低和最高范围,则只需强加一条规则,即NULL
START_RANGE
或END_RANGE
的行不代表有效范围。但是将这些行保留在表中以允许上述结构起作用。
答案 2 :(得分:1)
我已经看到了使用快速刷新的物化视图的表级(或集合级)约束执行方法的概念。
我们的想法是将集合级需求转换为MV查询中的行级需求,然后将常规的基于行的检查约束应用于物化视图行。
例如,如果要将用户的条目数限制为特定数量,则可以创建按用户选择计数的席子。查看,然后应用check(mv_count_column <= desired_max)
约束。
然而,由于对快速刷新的matviews的许多限制,这种方法实现和支持肯定是棘手的。我不确定它在你的情况下是否会起作用,因为快速刷新的MV不支持分析函数 - 也许你可以解决它。