在下面的示例中,有三个表存储着销售代表和客户之间的交互信息:Customers、Contacts 和 SalesReps。每个销售代表都向若干个客户销售产品。某些客户只有一个联系人,而另一些客户则有多个联系人。
有关这些表的详细说明,请参见Contacts、Customers 和 SalesReps 表的说明。
一个销售代表预订了一个发布,该发布提供了一份 SalesRep 表、一份含有指派给这些销售代表的客户的详细信息的 Customers 表以及一份含有对应于他们的客户的联系人详细信息的 Contacts 表。例如,每个销售代表都预订了以下的发布:
CREATE PUBLICATION SalesRepData ( TABLE SalesReps TABLE Customers SUBSCRIBE BY rep_key TABLE Contacts SUBSCRIBE BY (SELECT rep_key FROM Customers WHERE Contacts.cust_key = Customers.cust_key ) ); |
有关此发布的详细说明,请参见使用无交集数据分区。
这种在预订者之间重新指派行的做法有时称为地域调整,因为它是销售人员自动应用程序的常见功能,在这种功能中客户被定期重新指派给销售代表。
在统一数据库中,将某客户重新指派给新的销售代表时,就会相应更新 Customers 表中的 rep_key 值。
以下语句会将客户 cust1 重新指派给另一个销售代表 rep2。
UPDATE Customers SET rep_key = 'rep2' WHERE cust_key = 'cust1'; |
此更新以下面的方式被复制:
作为一个 DELETE 语句复制到以前的销售代表的远程数据库的 Customers 表中。
作为一个 INSERT 语句复制到新销售代表的远程数据库的 Customers 表中。
Contacts 表不发生改变。在统一数据库的事务日志中没有关于 Contacts 表的条目。这样,远程数据库中的 SQL Remote 无法重新指派 Contacts 表的 cust_key 行。这将导致产生以下参照完整性问题:以前的销售代表的远程数据库中的 Contacts 表包含一个不再有客户的 cust_key 值。
解决方法是使用 BEFORE UPDATE 触发器。BEFORE UPDATE 触发器不会对数据库表进行任何更改,但会在统一数据库的事务日志中创建一个条目。
触发 BEFORE UPDATE 触发器必须满足以下条件:
在运行 UPDATE 语句前触发。以便计算行的 BEFORE 值并将其添加到事务日志。
针对每行,而不是针对每个语句。由触发器提供的信息必须是新的预订表达式。
例如,下面的语句将创建一个 BEFORE UPDATE 触发器。
CREATE TRIGGER "UpdateCustomer" BEFORE UPDATE OF "rep_key" // only fire the trigger when we modify rep_key, not any other column ORDER 1 ON "Cons"."Customers" /* REFERENCING OLD AS old_name NEW AS new_name */ REFERENCING NEW AS NewRow OLD AS OldRow FOR EACH ROW BEGIN // determine the new subscription expression // for the Customers table UPDATE Contacts PUBLICATION SalesRepData OLD SUBSCRIBE BY ( OldRow.rep_key ) NEW SUBSCRIBE BY ( NewRow.rep_key ) WHERE cust_key = NewRow.cust_key; END ; |
SQL Remote 使用事务日志中的信息来确定哪些预订者接收哪些行。
执行了该语句后,统一数据库事务日志中将包含两个条目:
由 BEFORE UPDATE 触发器生成的用于 Contacts 表的 INSERT 和 DELETE 语句。
--BEGIN TRIGGER-1029-0000461705 --BEGIN TRANSACTION-1029-0000461708 BEGIN TRANSACTION go --UPDATE PUBLICATION-1029-0000461711 Cons.Contacts --PUBLICATION-1029-0000461711-0002-NEW_SUBSCRIBE_BY-rep2 --PUBLICATION-1029-0000461711-0002-OLD_SUBSCRIBE_BY-rep1 --NEW-1029-0000461711 --INSERT INTO Cons.Contacts(contact_key,name,cust_key) --VALUES ('5','Joe','cust1') go --OLD-1029-0000461711 --DELETE FROM Cons.Contacts -- WHERE contact_key='5' go --END TRIGGER-1029-0000461743 |
执行的原始 UPDATE 语句以及分别获得行或丢失行的这些用户的 INSERT 和 DELETE 语句。
--PUBLICATION-1029-0000461746-0002-NEW_SUBSCRIBE_BY-rep2 --PUBLICATION-1029-0000461746-0002-OLD_SUBSCRIBE_BY-rep1 --NEW-1029-0000461746 --INSERT INTO Cons.Customers(cust_key,name,rep_key) --VALUES ('cust1','company1','rep2') go --OLD-1029-0000461746 --DELETE FROM Cons.Customers -- WHERE cust_key='cust1' go --UPDATE-1029-0000461746 UPDATE Cons.Customers SET rep_key='rep2' VERIFY (rep_key) VALUES ('1') WHERE cust_key='cust1' go --COMMIT-1029-0000461785 COMMIT WORK |
SQL Remote 会对事务日志进行扫描以查找 BEFORE 和 AFTER 标记。根据此信息,它可以确定哪些远程用户得到了 INSERT、UPDATE 或 DELETE 语句。
当用户在 BEFORE 列表中而不在 AFTER 列表中时,会将 DELETE 语句发送到 Contacts 表中。
当用户在 AFTER 列表中而不在 BEFORE 列表中时,会将 INSERT 语句发送到 Contacts 表中。
如果用户既在 BEFORE 列表中又在 AFTER 列表中,则不会对 Contacts 表执行任何操作,但是会发送 Customers 表中的 UPDATE 语句。
如果 BEFORE 和 AFTER 列表相同,则远程用户已具有该行,并将发送 UPDATE 语句。
在以下示例中,必须使用 BEFORE UPDATE 触发器。在其它上下文中,BEFORE DELETE 和 BEFORE INSERT 都是必需的。
UPDATE table-name PUBLICATION pub-name SUBSCRIBE BY sub-expression WHERE search-condition; |
在本示例中,使用 BEFORE 触发器。
UPDATE table-name PUBLICATION publication-name OLD SUBSCRIBE BY old-subscription-expression NEW SUBSCRIBE BY new-subscription-expression WHERE search-condition; |
UPDATE 语句列出了受影响的发布和表。语句中的 WHERE 子句说明了受影响的行。此 UPDATE 语句没有更改表中的数据,它只在事务日志中生成条目。
在本示例中,预订表达式返回单个值。但也可以使用返回多个值的子查询。预订表达式的值必须是更新后的值。
在本示例中,该行的唯一预订者是新销售代表。有关具有现有预订者和新预订者的行的示例,请参见使用有交集分区。
Copyright © 2009, iAnywhere Solutions, Inc. - SQL Anywhere 11.0.1 |