1

这是我的要求。

前端(客户端)端将根据预定义的条件(例如:客户 ID、帐号、名字、姓氏等)进行搜索。我需要从 db2 数据库中获取与此请求对应的数据并将其发送回它们(服务器)。我们使用 CICS 通道和容器在客户端和服务器之间传递请求和响应。

前端需要按以下顺序排列的数据:接收日期降序,客户ID升序,帐号升序。以 500 条记录的页面获取数据。例如,如果来自前端的搜索请求将从 db2 数据库中检索 50,000 条记录,我们需要在 500 条记录“页面”中返回此数据。对于分页概念,我们使用作为数据库主键的字段安全存款号,但排序顺序不是基于该字段。

我想知道我们是否可以在 CICS 中使用可滚动游标逻辑来实现分页。

请注意,我不喜欢使用内部数组冒泡排序来发送数据作为响应,因为它会降低性能。我喜欢通过查询逻辑来做。有什么想法吗?

示例(初始前端输入请求):

  • 客户编号:A
  • 第一次请求(确定是第一次还是下一次或上一次请求分页)
  • 首笔保证金号码:0
  • 最后保证金号码:0

由于这是第一次请求,因此该字段从前端都为零,我们需要根据保证金 > 0 的条件从数据库中检索记录

Db2 数据库:

  • 此条件有 700 条记录
  • 大型机首次响应:我们将发送前500条记录

然后前端将发送请求以获取下一组记录,其中将包含:

  • 客户编号:A
  • 下一个请求
  • 首笔保证金号码:0
  • 最后保证金号码:17980

所以对于这个细节,如果我根据保证金号码> 17980查询我的数据库,它可能会导致屏幕中再次列出重复的记录,因为我们在数据库中的排序顺序不是基于保证金号码

如何实施这个逻辑?

4

2 回答 2

6

IBM 大型机环境中的许多客户端/服务器应用程序都涉及伪会话 CICS 事务。如果您在伪会话模式下使用 CICS,则服务器在返回给客户端时不可能持有游标。因此,可滚动光标在这种环境中用处不大。因此,要回答您的基本问题:此处不能使用任何可滚动光标。

这里的“技巧”是在可重新启动的服务器中创建一个 SQL 谓词。然后它将从任何给定的陈述点以正确的顺序拾取行。当客户端调用您的服务器时,它必须将所有定位信息传递给您的服务器。

通常,在来自客户端的第一次调用中,所有定位值都设置为使游标从必须是第一行开始定位自身。然后,服务器拉入一个“页面”价值的数据并将其返回给客户端。在下一页转发请求中,客户端将这些定位值设置为它显示的最后一行,并为下一个“页面”数据调用服务器。

在您的情况下,我假设页面前进光标看起来像这样,所有以 RESTART... 为前缀的变量都是客户端必须提供给服务器以在正确位置启动光标的内容。

 DECLARE CURSOR Page-forward FOR
    SELECT Receive_Date, Customer_id, Account_Nbr, Security_Dep_Id
    FROM Table_Name
    WHERE (   (Receive_Date    < :RESTART-RCV-DT)
           OR (Receive_Date    = :RESTART-RCV-DT AND
               Customer_Id     > :RESTART-CUSTOMER-ID)
           OR (Receive_Date    = :RESTART-RCV-DT AND
               Customer_Id     = :RESTART-CUSTOMER-ID AND
               Account_Nbr     > :RESTART-ACCT-NBR)
           OR (Receive_Date    = :RESTART-RCV-DT AND
               Customer_Id     = :RESTART-CUSTOMER-ID AND
               Account_Nbr     = :RESTART-ACCT-NBR AND
               Security_Dep_Id > :RESTART-SEC-DEP-ID))
    ORDER BY 1 DESC, 2 ASC , 3 ASC, 4 ASC

对于初始呼叫,客户端将传递类似“9999-12-31”的内容作为 RESTART-RCV-DT,零用于 RESTART-CUSTOMER-ID、RESTART-ACCT-NBR 和 SEC-DEP-ID(假设这些是所有数字)。如果您仔细查看游标谓词,您可以验证在这些值之前不能有任何行 - 因此这将返回第一页数据。如果客户端在此之后需要向前翻页,它必须告诉服务器从它收到的最后一行之后的下一行开始。为此,它将使用刚刚显示的页面上最后一行的值填充 RESTART... 变量。此过程将驱动光标一次向前选择一页。

向上翻页时,过程是相反的(您需要第二个光标来支持这一点,并且客户端需要告诉您要翻页的方向:前进或后退)。客户端将需要使用从服务器收到的第一行填充 RESTART 变量。服务器上翻页请求的技巧是以相反的顺序将数据返回给客户端。您可能必须以相反的顺序填充传回客户端的数据页(即将检索到的第一行放入客户端和服务器之间共享的分页区域的最后一行)。页面向后光标看起来像:

 DECLARE CURSOR Page-backward FOR
    SELECT Receive_Date, Customer_id, Account_Nbr, Security_Dep_Id
    FROM Table_Name
    WHERE (   (Receive_Date    > :RESTART-RCV-DT)
           OR (Receive_Date    = :RESTART-RCV-DT AND
               Customer_Id     < :RESTART-CUSTOMER-ID)
           OR (Receive_Date    = :RESTART-RCV-DT AND
               Customer_Id     = :RESTART-CUSTOMER-ID AND
               Account_Nbr     < :RESTART-ACCT-NBR)
           OR (Receive_Date    = :RESTART-RCV-DT AND
               Customer_Id     = :RESTART-CUSTOMER-ID AND
               Account_Nbr     = :RESTART-ACCT-NBR AND
               Security_Dep_Id < :RESTART-SEC-DEP-ID))
    ORDER BY 1 ASC, 2 DESC , 3 DESC, 4 DESC

正如在其他答案中所指出的那样,这种类型的分页过程不会管理或检测在分页事务期间可能发生的对数据库的并发更新。这是另一天的另一个话题......

开发可重启游标

构建分页服务器的关键是开发一个游标,该游标可根据从客户端事务接收的一组值重新启动。这将光标定位和方向的控制权交给客户端。这也意味着客户必须从服务器接收所有关键定位数据,即使客户实际上可能不会将这些数据用于任何其他目的(例如,从您的问题中,我得到的印象是客户可能不需要保证金 ID,除非提供作为服务器的定位参数)

要构建分页服务器,您需要知道所需的数据排序顺序是什么(例如,接收日期降序,然后客户 ID 升序,然后帐号升序)。您还需要知道唯一标识游标返回的行的数据集。在您的情况下,这将是安全存款 ID(这是您从中选择的表的主键,因此该表中的每一行都必须是唯一的)。知道这一点后,您就可以构建一个游标谓词(WHERE 子句中的内容),它将以所需的排序顺序返回客户端所需的数据,其中还包括完整的定位键(即安全存款 ID)。如果两个或更多返回的行可能包含相同的数据,如果最终定位键被消除,那么将定位键作为排序条件包括在内是很重要的。

可以遵循一个相当简单的公式来为支持分页服务器所需的可重新启动的游标构建谓词。基本上,这是连接一系列“AND”子句的“OR”子句的级联,这些子句按照客户端要求的排序顺序逐渐变得更有选择性,最终得到定位键。

要了解这是如何工作的,请考虑如何开发您的服务器的查询......

从排序顺序中更改频率最低的列开始...

SELECT ... 
FROM ... 
WHERE Receive_Date < restart value

这将检索指定重新开始接收日期之前的所有行,而不管其他列重新开始值是什么(例如,客户 ID 的范围可以从最小值到最大值,只要接收日期小于到目前为止“看到”的任何接收日期)。由于此列仅在所有从属排序列的值被耗尽后才更改值,因此您可以确保这不会在完全重新启动键之前拾取任何行。但是那些与重新启动请求在同一日期出现但具有更大客户 ID 的行呢?这些可以用......

SELECT ... 
FROM ... 
WHERE Receive_Date = restart value AND
      Customer_id > restart value

那些接收日期和客户 ID 与重启密钥相同但帐号更大的情况呢?这些可以用...

SELECT ... 
FROM ... 
WHERE Receive_Date = restart value AND
      Customer_Id = restart value AND
      Account_Nbr > restart value

继续此模式,直到处理完完整的重新启动密钥。请注意,不等号由排序顺序决定。<当列按降序和升序排序时使用>。另请注意,每个查询的SELECTandFROM子句完全相同 - 这意味着您可以使用 OR 连接将它们全部放在一起......

SELECT Receive_Date, Customer_id, Account_Nbr, Security_Dep_Id
FROM Table_Name
WHERE (   (Receive_Date    < :RESTART-RCV-DT)
       OR (Receive_Date    = :RESTART-RCV-DT AND
           Customer_Id     > :RESTART-CUSTOMER-ID)
       OR (Receive_Date    = :RESTART-RCV-DT AND
           Customer_Id     = :RESTART-CUSTOMER-ID AND
           Account_Nbr     > :RESTART-ACCT-NBR)
       OR (Receive_Date    = :RESTART-RCV-DT AND
           Customer_Id     = :RESTART-CUSTOMER-ID AND
           Account_Nbr     = :RESTART-ACCT-NBR AND
           Security_Dep_Id > :RESTART-SEC-DEP-ID))
ORDER BY 1 DESC, 2 ASC , 3 ASC, 4 ASC

你去...一个用于向前分页的可重新启动光标。向后分页光标的构造遵循类似的模式,只需翻转排序顺序并重复。

于 2014-02-17T20:30:58.507 回答
4

一种简单的方法:编写 SQL 以按照您指定的排序顺序根据您的条件检索数据。然后只检索您想要的行的键。将密钥保存在您在后续调用事务时可以访问的地方。查看 DB2 中的多行选择。还了解 CICS 中的伪对话编程技术。

现在我们得到比尔伍德格提到的设计含义,你没有在你的问题中具体说明,这就是我刚刚达到简单方法的高点的原因。

如果您的结果集在一次调用和下一次调用之间发生更改,您的结果将不会反映这些更改。您必须决定这是否重要。

您提到了“前端”,但没有具体说明它是什么。如果它是 BMS 应用程序,您可以将密钥保存在您的 commarea 或容器中。如果您的前端是一个分布式应用程序,通过 CICS Web 服务或 CICS Web 支持或 MQ 或原始套接字或其他方式调用您的事务,您必须设计一种机制来存储这些密钥,以便您可以唯一地检索它们——也许通过发送一个人为的密钥返回到它必须在后续调用时提供的分布式应用程序。然后你必须有一些过程来清理你的密钥库。

为您的问题创建一个在您的 IT 商店中独一无二的解决方案并不是孤立地完成的事情。您必须让负责维护您的应用程序的其他人参与进来,您的项目外部可能有一个小组负责做出此类决定,您的解决方案可能存在基础架构问题。

因此,这与其说是对您的问题的回答,不如说是详细说明了为什么您可能无法获得答案,或者至少是您似乎想要的答案。

于 2014-02-16T14:42:11.737 回答