使用 Sybase 触发器编写动态语句,使用所有新旧值创建您自己的复制事务语句日志?

Posted

技术标签:

【中文标题】使用 Sybase 触发器编写动态语句,使用所有新旧值创建您自己的复制事务语句日志?【英文标题】:Use Sybase triggers to write dynamic statement using all old and new values for creating your own replication transaction statement log? 【发布时间】:2019-04-05 00:30:37 【问题描述】:

问题总结

我必须为 Sybase ADS 和 Postgresql 11 组节点之间的 bucardo/symmetricDS 启发的自制双向复制系统编写 I/U/D 语句生成触发器,在创建的任何 Postgresql 和 Sybase DB 上使用 BEFORE 触发器根据复制源表中输入的命令插入/更新/删除命令:例如INSERT INTO PERSON (first_name,last_name,gender,age,ethnicity) Values ('John','Doe','M',42,'C') 并将它们操作到相应的 Insert 语句中,并通过获取 OLD 和 NEW 值以动态生成 UPDATE 语句进行更新,同时获取 OLD 值以生成 DELETE 命令,所有这些都以某个时间间隔在目标上按命令运行.

我知道这很困难,没有人这样做,但这是为了一份工作,我没有其他选择,也不能反对提供不同的解决方案。除了 SO 之外,我没有其他队友或人力资源可以提供帮助,比如 Codementors,这没什么帮助。我的想法/策略是在插入 OLD 和 NEW 值以生成要在目标上运行的语句/命令时复制部分 bucardo/SymmetricDS。现在,我将整个表快照到 CSV,而不是通过单个命令执行,但是通过命令和循环遍历生成和保存命令的表将使工作更容易。

一个大问题是它们来自 Sybase ADS 并且具有混合的键/索引结构(许多表没有 PK)并且在 Postgresql 中镜像,所以我正在尝试编写无 PK 语句或全列绕过 no-pk 表的命令。他们也只会复制某些表的某些列,所以我在表中有一个列供他们插入由';'分隔的列名然后将其拆分为一个数组,并将列名链接到每个语句的值,以生成 I/U/D 的完整命令,希望如此。我对其他策略持开放态度,但这是一个很大的个人项目,我已经经历了很多困难。

我大多来自 DBA,并且有一些基础编程经验,所以我主要是对每个主要序列进行伪编码,按部分搜索语法,并在遇到语言障碍时进行调整。我很感谢给予的任何帮助,因为我有点绝望和沮丧。

我尝试了什么

我必须为 Sybase ADS 和 Postgresql 做这个,但这个问题最初是关于 ADS 的,因为它更具挑战性和更老。

拥有一个“日志”表来跟踪每个复制表和记录的行更改并最终动态生成命令是两个平台的目标。我正在尝试制作如下触发语句:

CREATE TRIGGER PERSON_INSERT
ON PERSON
BEFORE 
INSERT
BEGIN
INSERT INTO Backlog (SourceTableID, TriggerType, Status, CreateTimeDate, NewValues) select ID, 'INSERT','READY', NOW(),''first_name';'last_name';'gender';'age';'ethnicity'' from __new;
END;

CREATE TRIGGER PERSON_UPDATE
   ON PERSON
   BEFORE 
   UPDATE 
BEGIN 
INSERT INTO Backlog (SourceTableID, TriggerType, Status, CreateTimeDate, NewValues) select ID, 'U','UPDATE','READY', NOW(),''first_name';'last_name';'gender';'age';'ethnicity'' from __new;
UPDATE Backlog SET OldValues=select ''first_name';'last_name';'gender';'age';'ethnicity'' from __old where SourceTableID=select ID from __old;
END;

CREATE TRIGGER PERSON_DELETE
   ON PERSON
   BEFORE 
   DELETE 
BEGIN 
INSERT INTO Backlog (SourceTableID, TriggerType, Status, CreateTimeDate, OldValues) select ID, 'D','DELETE','READY', NOW(),''first_name';'last_name';'gender';'age';'ethnicity'' from __old;
END;

但我希望 "''first_name';'last_name';'gender';'age';'ethnicity''" 来自另一个表作为使其动态的值,因为多个表将写入它们的值和语句信息到单个日志表。然后,可以将其制成一个变量,然后可能会拆分以链接到相应的值,这样就可以生成 IUD 语句,这些语句将在目标上一次执行。

尝试的不完整示例触发代码

CREATE TRIGGER PERSON_INSERT
   ON PERSON
   BEFORE 
INSERT
BEGIN
--Declare @Columns string
--@Columns=select Columns from metatable where tablename='PERSON'
--String Split(@Columns,';') into array to correspond to new and old VALUES
--@NewValues=@['@Columns='+NEW.@Columns+'']
INSERT INTO Backlog (SourceTableID, TriggerType, Status, CreateTimeDate, NewValues) select ID, 'INSERT','READY', NOW(),''first_name';'last_name';'gender';'age';'ethnicity'' from __new;
END;

CREATE TRIGGER PERSON_UPDATE
   ON PERSON
   BEFORE 
   UPDATE 
BEGIN 
--Declare @Columns string
--@Columns=select Columns from metatable where tablename='PERSON'
--String Split(@Columns,';') into array to correspond to new and old VALUES
--@NewValues=@['@Columns='+NEW.@Columns+'']
--@OldValues=@['@Columns='+OLD.@Columns+'']
INSERT INTO Backlog (SourceTableID, TriggerType, Status, CreateTimeDate, NewValues) select ID, 'U','UPDATE','READY', NOW(),''first_name';'last_name';'gender';'age';'ethnicity'' from __new;
UPDATE Backlog SET OldValues=select ''first_name';'last_name';'gender';'age';'ethnicity'' from __old where SourceTableID=select ID from __old;
END;

CREATE TRIGGER PERSON_DELETE
   ON PERSON
   BEFORE 
   DELETE 
BEGIN 
--Declare @Columns string
--@Columns=select Columns from metatable where tablename='PERSON'
--String Split(@Columns,',') into array to correspond to new and old VALUES
--@OldValues=@['@Columns='+OLD.@Columns+'']
INSERT INTO Backlog (SourceTableID, TriggerType, Status, CreateTimeDate, OldValues) select ID, 'D','DELETE','READY', NOW(),''first_name';'last_name';'gender';'age';'ethnicity'' from __old;
END;

结论

对于插入、更新或删除的每一行;在日志表的 COMMAND 列中,我正在尝试生成相应的 'INSERT INTO PERSON ('+@Columns+') VALUES ('+@NewValues+')' 类型语句,或者 UPDATE 或 DELETE。然后一个 Foreach 服务将运行每个按创建时间排序的命令值,作为主要的复制服务。

为了清楚起见,我试图让我的示例代码触发器以动态方式将所有旧值和新值写入列,而不对每个触发器中的列进行硬编码,因为它将用于多个表,并写入值到由逗号或分号分隔的单个列中。

这背后更大的愿望或目标是找到一种方法来保存/编写每个 IUD 命令,然后能够在订阅服务器上运行它们。postgresql 和 Sybase 平台的数据库,因此我自己从日志复制

【问题讨论】:

那么,您的问题是什么?你很好地描述了你的背景,但真正的问题是什么?您需要帮助编写 SQL 代码吗? 是的,帮助编写一个触发器,该触发器可以获取它所属的触发表的所有旧值或新值,并将它们写入日志表中以逗号或类似内容分隔的单个列值中。跨度> 【参考方案1】:

这是一个复杂但可以解决的问题,需要时间和仔细的计划来编写。我认为您正在寻找的是 ADS SQL 语法中的“立即执行”命令。使用此命令,您可以创建一个动态语句,然后在 SQL 语句的构造终止后执行该语句。通过小心地将语句构造为字符串,将每个所需的列值保存到临时表中,然后使用 Execute Immediate 执行它。例如:

DECLARE TableColumns Cursor ; 
DECLARE FldName Char(100) ;
...
OPEN TableColumns AS SELECT * 
                       FROM system.columns 
                      WHERE parent = @cTableName 
                        AND field_type < 21 //ADS_ROWVERSION
                        AND field_type <> 6 //ADS_BINARY 
                        AND field_type <> 7; //ADS_IMAGE

While Fetch TableColumns DO

    FldName = Trim( TableColumns.Name) ;

    StrSql = 'SELECT New.[' + Trim( FldName ) + '] newVal' +
             'INTO #myTmpTable FROM ___New n' ;

将语句构造为字符串后,可以像这样执行:

EXECUTE IMMEDIATE STRSQL ; 

您可以从 __old 和 __new 临时表中获取新旧值,这些临时表始终可用于触发器。将值插入临时表 myTmpTable,然后使用它来更新目标。请记住在最后删除 myTmpTable。

此外,我认为您可以在 DD 上创建一个函数,该函数实际上可以从您要跟踪的表上的每个触发器调用,而不是为每个表编写一个长触发器,并且 cTableName 可以是发送到的参数功能。这样维护起来会容易一些。

【讨论】:

以上是关于使用 Sybase 触发器编写动态语句,使用所有新旧值创建您自己的复制事务语句日志?的主要内容,如果未能解决你的问题,请参考以下文章

声明的表变量上的动态 Select 语句 - SYBASE

sybase创建触发器

Perl DBI Sybase Asanywhere 绑定变量问题

如何识别与 sybase 数据库中的表关联的触发器?

Sybase IQ使用过程中注意事项

sybase数据库 如何使用sql语句查询,数据库容量大小和数据库使用量大小