以下记录

时间:2012-07-24 22:14:37

标签: database delphi loops records

我一直遇到一个问题,让我的程序通过依赖于记录中的值的数据库来跟踪记录,简单地说,这就是我想要实现的目标。

这是我正在使用的表格的简化版本:

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

我面临的主要问题是我无法让程序浏览每条记录,按照任何链接然后返回到它停止的位置以确保它不会错过任何记录,我该如何制作程序会忽略它作为循环的一部分已经结束的记录吗?

任何帮助将不胜感激:)

3 个答案:

答案 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_dist

2.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

这种做法更糟糕:

  1. 不是那么快 - 重复多个单独的语句而不是捆绑到一个过程中并调用一次;
  2. 不是那么快,也不那么可靠 - 如果你从一个接一个的程序中调用它,那么数据将通过网络从服务器飞到程序并返回,而不是服务器内的所有计算
  3. 计算距离时,你必须非常小心。一直到删除它们 ALL 并重新计算是否有东西坏了。
  4. 同样更好:

    1. 更容易理解SQL新手

    2. 只使用非常基本的SQL,因此可以进入任何服务器:NexusDB,Firebird,SQLite,无论你选择什么