我一直遇到一个问题,让我的程序通过依赖于记录中的值的数据库来跟踪记录,简单地说,这就是我想要实现的目标。
这是我正在使用的表格的简化版本:
Record Name | Val1 | Val2 | Link | Prev Link |
Rec1 | 5 | 3 | Rec2 | |
Rec2 | 2 | 4 | Rec6 | Rec1 |
Rec3 | 1 | 8 | Rec4 | |
Rec4 | 1 | 1 | | Rec3 |
Rec5 | 8 | 3 | | |
Rec6 | 9 | 3 | | Rec2 |
我的程序需要通过上面的表格,保存针对一个特定记录的信息,转到它的链接记录,清除以前记录的值,然后将它们添加到新记录中(它应该继续这样做直到它到达链的末尾),这里的例子是运行我的程序后应该发生的事情。
Record Name | Val1 | Val2 | Link | Prev Link |
Rec1 | 0 | 0 | Rec2 | |
Rec2 | 0 | 0 | Rec6 | Rec1 |
Rec3 | 0 | 0 | Rec4 | |
Rec4 | 2 | 9 | | Rec3 |
Rec5 | 8 | 3 | | |
Rec6 | 16 | 10 | | Rec2 |
我正在使用的当前程序可以在以下位置找到:http://pastebin.com/A10hW0C6
我面临的主要问题是我无法让程序浏览每条记录,按照任何链接然后返回到它停止的位置以确保它不会错过任何记录,我该如何制作程序会忽略它作为循环的一部分已经结束的记录吗?
任何帮助将不胜感激:)
答案 0 :(得分:0)
嗯,你可以做的是有一个单独的查询来保存链接开始记录,例如:
qry1.sql := 'select * from table where prev_link is null;';
这给出了
Record Name | Val1 | Val2 | Link | Prev Link |
Rec1 | 5 | 3 | Rec2 | |
Rec3 | 1 | 8 | Rec4 | |
Rec5 | 8 | 3 | | |
然后,您可以跟踪结果数据集,在另一个查询(query2)上搜索/定位,并在那里应用您的处理逻辑。
当您完成结果日期设置时,您就完成了。当然,这假设您的数据是合法的,即没有断开的链接,没有循环链接等等。
一些增强功能。您可以添加名为“status”的列以反映记录的状态。例如,status = 0表示'Unprocessed','1'表示已处理,'2'表示已断开链接,'3'表示循环链接等。您可以首先将所有状态列填充为0(未处理)。
如果您无法通过在“链接”列中查找/找到该记录(可能以某种方式被删除),那么您可以将状态标记为“2”。
每次关注链接时,请跟踪跟踪记录。例如,您可以使用列表。在跟踪“链接”列中的记录之前,请检查列表。如果记录在列表中,那么您有一个循环链接。停止跟随,将状态标记为“3”(循环链接),清除列表,然后从query1中的下一条记录开始。处理循环链接很重要,否则你的程序会卡在它上面(永不结束)。
完成链接链的处理后,将状态标记为“1”到列表中的所有记录。
您可以使用数据库事务(开始事务...结束事务),因此当您在链接链上遇到死链接或循环链接时,您可以回滚更改的值,并相应地标记状态。
完成后,您可以查看状态列。如果所有'1'都表示所有好(已处理)。如果没有,那么您可以决定下一步该做什么。
列状态可用于过滤掉其他后续操作中已处理的记录,因此可以更改上面的查询:
qry1.sql := 'select * from table where prev_link is null and status = 0;';
当然这是一个初步策略,你可以修改它以适合你。
答案 1 :(得分:0)
根据您使用的数据库,这可以完全由服务器上的SQL解决。这是Firebird的一个示例(假设链接是一致的,并且没有无限递归,可以通过引用完整性约束和触发器强制执行 - 这里不包括这些)。
/* metadata */
set sql dialect 3;
create table test (
id integer not null,
val1 integer,
val2 integer,
next_id integer,
prev_id integer
);
alter table test add constraint pk_test primary key (id);
alter table test add constraint fk_test_next_id foreign key (next_id) references test (id) on update cascade;
alter table test add constraint fk_test_prev_id foreign key (prev_id) references test (id) on update cascade;
/* data */
insert into test (id, val1, val2, next_id, prev_id) values (1, 5, 3, 2, null);
insert into test (id, val1, val2, next_id, prev_id) values (2, 2, 4, 6, 1);
insert into test (id, val1, val2, next_id, prev_id) values (3, 1, 8, 4, null);
insert into test (id, val1, val2, next_id, prev_id) values (4, 1, 1, null, 3);
insert into test (id, val1, val2, next_id, prev_id) values (5, 8, 3, null, null);
insert into test (id, val1, val2, next_id, prev_id) values (6, 9, 3, null, 2);
/* update statement (could also be a stored procedure) */
execute block
as
declare variable id integer;
declare variable val1 integer;
declare variable val2 integer;
begin
for with recursive test_list (
id,
val1,
val2,
next_id
)
as (
select
t.id,
t.val1,
t.val2,
t.next_id
from test t
where (t.prev_id is null)
union all
select
t.id,
t.val1 + tl.val1,
t.val2 + tl.val2,
t.next_id
from test t
join test_list tl on (tl.id = t.prev_id)
)
select
tl.id,
iif(tl.next_id is null, tl.val1, 0) val1,
iif(tl.next_id is null, tl.val2, 0) val2
from test_list tl
order by tl.id
into
:id,
:val1,
:val2
do
update test set
val1 = :val1,
val2 = :val2
where (id = :id);
end
答案 2 :(得分:0)
TOndrej给了你非常好的答案,但你需要很好地了解SQL。 付费 - 正确的SQL会使您的数据库可靠,您的程序中的错误不会破坏数据库数据。但是你有时间学习。 此外,他的SQL使用了相当新的Firebird版本的功能,这个词几乎不适用于NexusDB或其他服务器。
这是一种更愚蠢的方法。你也可以尝试一下。
所以,如果我理解你的任务,那么这个表会分成一组链? 这里你有三个链: Rec1-> Rec2-> Rec6 和 Rec3-> Rec4 和 Rec5 。
当您添加新元素时,始终会转到尾部,因此它将是 Rec1-> Rec2-> Rec6-> NewRec7 ,但 never 可以是 NewRec7-> Rec1-> Rec2-> Rec6 , Rec1-> Rec2-> NewRec7-> Rec6 。
是这样吗?
然后你可以添加列距离从根
Record Name | Val1 | Val2 | Link | Prev Link | Dist |
Rec1 | 5 | 3 | Rec2 | | 0 |
Rec2 | 2 | 4 | Rec6 | Rec1 | 1 |
Rec3 | 1 | 8 | Rec4 | | 0 |
Rec4 | 1 | 1 | | Rec3 | 1 |
Rec5 | 8 | 3 | | | |
Rec6 | 9 | 3 | | Rec2 | 2 |
您可以在SQL触发器或程序中计算和更新它。 SQL触发器更好 - 更可靠。但你需要掌握它们。 每当您添加记录,删除记录或更改记录链接时 - 您都必须重新计算所有受影响记录的距离。例如,如果我拿Rec6,切割并重新连接到Rec1-> Rec2,Rec6-> Rec3-> Rec4,则Rec6和Rec3和Rec4的距离必须重新计算
1)从数据库中获取max(dist)。
从THE_TABLE中选择MAX(DIST)
2)对于从0到最大(dist)-1
的每个selected_dist2.1)对于每个距离
的记录SELECT RecordName,Link,Val1,Val2 FROM THE_TABLE WHERE dist =:choosen_dist AND Link IS NOT NULL
2.1.1)更新递增值的下一个链接的
更新表设置Val1 = Val1 +:PrevLinked_Val1; Val2 = Val2 +:PrevLinked_Val2 WHERE RecordName =:NextLink
2.1.2)更新所选的一个,清除值
更新表集Val1 = 0; Val2 = 0 WHERE RecordName =:CurrentRecordName
这种做法更糟糕:
同样更好:
更容易理解SQL新手
只使用非常基本的SQL,因此可以进入任何服务器:NexusDB,Firebird,SQLite,无论你选择什么