从SQL Server更新链接MySQL表的查询

时间:2009-07-30 11:58:09

标签: mysql sql-server

我有一个带有链接MySQL服务器的MS SQL Server。我需要在两个服务器之间部分同步一个表。这是通过三个步骤完成的,并基于以下条件:

  1. 删除MySQL表中不满足条件的所有行

  2. 在MySQL表中插入满足条件的所有新行

  3. 更新MySQL服务器中满足条件并且MySQL和SQL Server之间具有不同数据的所有行

  4. 步骤1和2始终没有问题。但如果有任何更新,第3步将无法运行。查询失败,但出现以下异常:行集使用了乐观并发,并且在上次提取或重新同步包含行后,列的值已更改。]。

    这是执行的查询:

    update mysqlserver...subscribers
    set Firstname = Voornaam, 
      Middlename = Tussenvoegsel, 
      Surname = Achternaam, 
      email = e-mail 
    from mysqlserver...subscribers as b, tblkandidaat 
    where (b.kandidaatid = tblkandidaat.kandidaatid) and
      (tblkandidaat.kandidaatid in (
        select subsc.kandidaatid
        from mysqlserver...subscribers subsc inner join tblKandidaat 
          on (subsc.kandidaatid=tblKandidaat.kandidaatid) 
        where (subsc.list=1) and
          ((subsc.firstname COLLATE Latin1_General_CI_AI <> Voornaam 
          or (subsc.middlename COLLATE Latin1_General_CI_AI <> Tussenvoegsel) 
          or (subsc.surname COLLATE Latin1_General_CI_AI <> tblKandidaat.Achternaam) 
          or (subsc.email COLLATE Latin1_General_CI_AI <> tblKandidaat.e-mail))
      ));
    

    有人知道如何防止这种情况吗?

7 个答案:

答案 0 :(得分:2)

请尝试此查询:

update b
set
   Firstname = Voornaam, 
   Middlename = Tussenvoegsel, 
   Surname = Achternaam, 
   email = e-mail 
from
   mysqlserver...subscribers b
   inner join tblkandidaat k on b.kandidaatid = k.kandidaatid
where
   b.list=1
   and (
      b.firstname COLLATE Latin1_General_CI_AI <> k.Voornaam 
      or b.middlename COLLATE Latin1_General_CI_AI <> k.Tussenvoegsel
      or b.surname COLLATE Latin1_General_CI_AI <> k.Achternaam
      or b.email COLLATE Latin1_General_CI_AI <> k.e-mail
   )
  1. 最佳做法是使用ANSI连接并从WHERE条件中正确分离JOIN条件。

  2. 在整个查询中,为所有表使用别名而不是长表名称更具可读性。

  3. 最好为所有列引用使用别名,而不是将它们留空。它不仅是一个好习惯,而且使事情变得更加清晰,它可以避免内部和外部表引用中的一些非常讨厌的错误。

  4. 如果性能也是一个问题:链接服务器连接有时会转移到DB数据提供程序引擎中的逐行处理。我发现了一种情况,即将链接服务器上的复杂连接的一部分分解为常规连接,然后交叉应用,大大减少了不需要的行被提取并大大提高了性能。 (这基本上是做书签查找,也就是非聚集索引扫描,然后是使用这些值的聚集索引查找)。虽然这可能与MySql的工作原理不完全相符,但值得尝试。如果您可以执行任何类型的跟踪以查看在MySql端执行的实际查询,您可能会了解其他方法以提高性能。

  5. 另一个提高性能的想法是将远程数据本地复制到临时表,并添加ActionRequired列。然后更新临时表,使它看起来应该如此,在ActionRequired中放置'U','I'或'D',然后使用ActionRequired在主键上使用简单的等值连接在链接服务器上执行合并/ upsert。仔细关注可能在处理过程中更新远程数据库的竞争条件。

  6. 小心空值...是你所比较的那些不可空的列吗?

答案 1 :(得分:0)

您可以尝试在mysql中创建第二个表,从sql-server插入所有更改行的空表并在两个mysql表之间执行第3步。

答案 2 :(得分:0)

尝试不在where语句中使用子查询。子查询可能会返回多行,然后您就会收到错误。

答案 3 :(得分:0)

尝试创建一个视图,其中包含source,destination和has_changed列,并且链接的表已连接。然后你可以发出查询

更新vi_upd_linkedtable set destination = source其中has_changed = 1

答案 4 :(得分:0)

这是一个黑暗的镜头,但尝试将FOR UPDATELOCK IN SHARE MODE添加到您的子选择查询的末尾。这将告诉MySQL您正在尝试为事务中的更新选择内容,并应在select期间而不是update期间创建行级写锁定。

来自13.2.8.3. SELECT ... FOR UPDATE and SELECT ... LOCK IN SHARE MODE Locking Reads

  

SELECT ... LOCK IN SHARE MODE设置a   读取的行上的共享模式锁定。一个   共享模式锁启用其他   会话来读取行而不是   修改它们。读取的行是   最新的,如果他们属于   还没有的另一笔交易   已提交,读取块直到那个   交易结束。

答案 5 :(得分:0)

对于名称相同的行,update是无操作。

您不会通过尝试过滤掉它们相同的行来保存任何工作,因为仍需要在链接中比较数据。所以我看不到子查询有什么好处。

因此可以简化查询:

update mysqlserver...subscribers
set Firstname = Voornaam, 
  Middlename = Tussenvoegsel, 
  Surname = Achternaam, 
  email = e-mail 
from mysqlserver...subscribers as b join tblkandidaat 
  on b.kandidaatid = tblkandidaat.kandidaatid;
where b.list = 1;

消除子查询可能会使锁定问题消失。 MySQL在给定查询中的同一个表上确实存在将selectupdate组合在一起的问题。

答案 6 :(得分:0)

试试这个。我今天写了几篇。

update b
set
   Firstname = Voornaam, 
   Middlename = Tussenvoegsel, 
   Surname = Achternaam, 
   email = e-mail 
from
   mysqlserver...subscribers b
   inner join tblkandidaat k on b.kandidaatid = k.kandidaatid
where
   b.list=1
   and (
      ISNULL(b.firstname,'') COLLATE Latin1_General_CI_AI <> ISNULL(k.Voornaam,'')
      or ISNULL(b.middlename,'') COLLATE Latin1_General_CI_AI <> ISNULL(k.Tussenvoegsel,'')
      or ISNULL(b.surname,'') COLLATE Latin1_General_CI_AI <> ISNULL(k.Achternaam,'')
      or ISNULL(b.email,'') COLLATE Latin1_General_CI_AI <> ISNULL(k.e-mail,'')
   )

使用ISNULL可以使列无效。