我有一个带有链接MySQL服务器的MS SQL Server。我需要在两个服务器之间部分同步一个表。这是通过三个步骤完成的,并基于以下条件:
删除MySQL表中不满足条件的所有行
在MySQL表中插入满足条件的所有新行
更新MySQL服务器中满足条件并且MySQL和SQL Server之间具有不同数据的所有行
步骤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))
));
有人知道如何防止这种情况吗?
答案 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
)
最佳做法是使用ANSI连接并从WHERE条件中正确分离JOIN条件。
在整个查询中,为所有表使用别名而不是长表名称更具可读性。
最好为所有列引用使用别名,而不是将它们留空。它不仅是一个好习惯,而且使事情变得更加清晰,它可以避免内部和外部表引用中的一些非常讨厌的错误。
如果性能也是一个问题:链接服务器连接有时会转移到DB数据提供程序引擎中的逐行处理。我发现了一种情况,即将链接服务器上的复杂连接的一部分分解为常规连接,然后交叉应用,大大减少了不需要的行被提取并大大提高了性能。 (这基本上是做书签查找,也就是非聚集索引扫描,然后是使用这些值的聚集索引查找)。虽然这可能与MySql的工作原理不完全相符,但值得尝试。如果您可以执行任何类型的跟踪以查看在MySql端执行的实际查询,您可能会了解其他方法以提高性能。
另一个提高性能的想法是将远程数据本地复制到临时表,并添加ActionRequired列。然后更新临时表,使它看起来应该如此,在ActionRequired中放置'U','I'或'D',然后使用ActionRequired在主键上使用简单的等值连接在链接服务器上执行合并/ upsert。仔细关注可能在处理过程中更新远程数据库的竞争条件。
小心空值...是你所比较的那些不可空的列吗?
答案 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 UPDATE
或LOCK 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在给定查询中的同一个表上确实存在将select
和update
组合在一起的问题。
答案 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可以使列无效。