我有一个包含开始日期范围,结束日期范围和其他一些其他列的表格。在输入新记录时,我想自动调整任何重叠的日期范围(缩小它们,拆分它们或删除它们以允许新的输入 - 参见下面的算法)。我还想确保不会意外地将重叠记录插入此表中。
我正在使用Oracle和Java作为我的应用程序代码。我应该如何强制防止重叠日期范围,还允许自动调整重叠范围?我应该创建一个AFTER INSERT触发器,使用dbms_lock来序列化访问,以防止重叠数据。然后在Java中,应用逻辑来自动调整所有内容?或者该部分应该在存储过程调用中的PL / SQL中?这是我们对其他几个表所需要的东西,所以摘要很好。
如果有人已经写过这样的内容,请分享:)
我确实找到了这个参考:http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:474221407101
以下是一个示例,说明如何处理4个重叠案例中的每一个以便在插入时进行调整:
= Example 1 =
In DB (Start, End, Value):
(0, 10, 'X')
**(30, 100, 'Z')
(200, 500, 'Y')
Input
(20, 50, 'A')
Gives
(0, 10, 'X')
**(20, 50, 'A')
**(51, 100, 'Z')
(200, 500, 'Y')
= Example 2 =
In DB (Start, End, Value):
(0, 10, 'X')
**(30, 100, 'Z')
(200, 500, 'Y')
Input
(40, 80, 'A')
Gives
(0, 10, 'X')
**(30, 39, 'Z')
**(40, 80, 'A')
**(81, 100, 'Z')
(200, 500, 'Y')
= Example 3 =
In DB (Start, End, Value):
(0, 10, 'X')
**(30, 100, 'Z')
(200, 500, 'Y')
Input
(50, 120, 'A')
Gives
(0, 10, 'X')
**(30, 49, 'Z')
**(50, 120, 'A')
(200, 500, 'Y')
= Example 4 =
In DB (Start, End, Value):
(0, 10, 'X')
**(30, 100, 'Z')
(200, 500, 'Y')
Input
(20, 120, 'A')
Gives
(0, 10, 'X')
**(20, 120, 'A')
(200, 500, 'Y')
算法如下:
given range = g; input range = i; output range set = o
if i.start <= g.start
if i.end >= g.end
o_1 = i
else
o_1 = i
o_2 = (i.end + 1, g.end)
else
if i.end >= g.end
o_1 = (g.start, i.start - 1)
o_2 = i
else
o_1 = (g.start, i.start - 1)
o_2 = i
o_3 = (i.end + 1, g.end)
答案 0 :(得分:2)
我一般都会看到数据模型,其中范围的起点是唯一被跟踪的模型,其中结束点是隐含的。所以它是
CREATE TABLE MY_TABLE
(START_AT NUMBER,
VALUE NUMBER,
CONSTRAINT MY_TABLE_PK (START_AT)
);
如果您需要以现有格式显示值,则可以使用分析和物化视图,使用LEAD(START_AT) OVER (ORDER BY START_AT)
(我认为这是正确的,但未经测试)来获取已解释的结束值。
答案 1 :(得分:0)
AskTom的文章提供了一个很好的示例,说明如何完成它,但请注意,此示例会锁定整个表,这会严重影响应用程序的并发性。
如果并发是你的事,你应该只添加一个序列列(ORDER
选项,如果你使用RAC
)并写一个这样的查询:
SELECT *
FROM (
SELECT *, rownum AS rn
FROM mytable
WHERE start_date <= :date
AND end_date >= :date
ORDER BY
seq DESC
)
WHERE rn = 1
找出给定日期的有效范围(和其他数据)。
这将返回包含给定日期的最后一个插入范围。
通过运行维护过程可以使查询更有效率,该过程可以及时消除重叠范围(如帖子中所述)并重写查询,如下所示:
SELECT *
FROM (
SELECT *, rownum AS rn2
FROM (
SELECT *
FROM (
SELECT *, rownum AS rn
FROM mytable
WHERE seq <= :lseq
AND start_date <= :date
AND end_date >= :date
ORDER BY
start_date DESC
)
WHERE rn = 1
UNION ALL
SELECT *
FROM (
SELECT *, rownum AS rn
FROM mytable
WHERE seq > :lseq
AND start_date <= :date
AND end_date >= :date
ORDER BY
seq DESC
)
WHERE rn = 1
)
ORDER BY
seq DESC
)
WHERE rn2 = 1
在start_date
和seq
上创建索引,以便快速开始工作。
后一个查询将从处理范围(已知非重叠)中选择第一个匹配范围,未处理范围(少数几个)的第一个匹配范围和两个记录中的第一个匹配范围将选择一个最高seq
。