0

我在让我的程序通过依赖于记录中的值的数据库跟踪记录方面遇到了一点问题,简而言之,这就是我想要实现的目标。

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

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

我面临的主要问题是我无法让程序遍历每条记录,点击任何链接,然后返回到它离开的地方以确保它不会错过任何内容,我怎样才能让程序忽略记录它已经作为循环的一部分结束?

任何帮助,将不胜感激 :)

4

3 回答 3

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 = 0 表示“未处理”,“1”表示已处理,“2”表示断开的链接,“3”表示循环链接等。您可以先将所有状态列填充为 0(未处理)。

如果您无法通过在“链接”列中查找/定位来找到该记录(可能以某种方式被删除),那么您可以将状态标记为“2”。

每次点击链接时,请跟踪关注的记录。例如,您可以使用列表。在关注“链接”列中的记录之前,请检查列表。如果记录在列表中,则您有一个循环链接。停止关注,将状态标记为“3”(循环链接),清除列表,然后从 query1 中的下一条记录开始。处理循环链接很重要,否则您的程序可能会卡在它上面(永远不会结束)。

完成处理链接链后,将列表中所有记录的状态标记为“1”。

您可以利用数据库事务(开始事务...结束事务),因此当您在链接链中遇到死链接或循环链接时,您可以回滚更改的值,并相应地标记状态。

完成后,您可以查看状态栏。如果全“1”表示一切都好(已处理)。如果没有,那么您可以决定下一步该做什么。

列状态可用于过滤掉另一个后续操作中已处理的记录,因此可以更改上面的查询:

qry1.sql := 'select * from table where prev_link is null and status = 0;';

当然这是一个初步的策略,你可以修改它以适合你。

于 2012-07-25T02:18:15.560 回答
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
于 2012-07-25T08:05:44.597 回答
0

TOndrej 给了你很好的答案,但你需要很好地了解 SQL。这是值得的——正确的 SQL 将使您的数据库可靠,您的程序中的错误不会损坏数据库数据。但是你会有时间学习。此外,他的 SQL 使用了 Firebird 最新版本的功能,这个词几乎不适用于 NexusDB 或其他服务器。

这是一个更愚蠢的方法。你也可以试试。

所以,如果我理解你的任务,表格会分成一组链吗?这里你有三个链:Rec1->Rec2->Rec6Rec3->Rec4Rec5

当你添加新元素时,它总是在尾部,所以它将是Rec1->Rec2->Rec6->NewRec7,但永远不能是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 到 max(dist)-1 的每个 selected_dist

2.1) 对于每条具有这种距离的记录

从 THE_TABLE 中选择 RecordName、Link、Val1、Val2,其中 dist = :choosen_dist 并且链接不为空

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. 计算距离时必须非常小心。直到将它们全部删除并重新计算是否有问题。

它也更好:

  1. SQL 新手更容易理解

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

于 2012-07-25T08:57:49.523 回答