SqlServer调用外部程序实现数据同步
首先创建两个数据库:SyncA是数据源,SyncB是对SyncA进行同步的数据库。

在SyncA和SyncB中分别创建Source表和Target表,实际业务中,两张表的结构大多不相同。

然后创建一个类库的项目:MySync(注意项目的版本,Sql08不支持的.net 4.0及更高版本)

下面是同步程序代码:
using System;
using System.Data;
using System.Data.Sql;
using Microsoft.SqlServer.Server;
using System.Data.SqlClient;
using System.Data.SqlTypes; namespace MySync
{
public class SyncDataBase
{
[SqlFunction(SystemDataAccess = SystemDataAccessKind.Read, DataAccess = DataAccessKind.Read)]
public static string Sync(string strSql)
{
string result = "true"; string strConn = @"Data Source=localhost;Initial Catalog=SyncB;User ID=sa;Password=123@abc;";
try
{
using (SqlConnection connection = new SqlConnection(strConn))
{
connection.Open();
SqlCommand command = new SqlCommand(strSql, connection);
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();
connection.Close();
}
}
catch (Exception ex)
{
result = "false:" + ex.ToString();
} return result;
}
}
}
接下来要对类库项目进行签名,签名后编译【项目】:


启用CLR功能:默认情况下,Sql Server中的CLR是关闭的,所以我们要执行如下命令打开SyncA数据库的CLR。
exec sp_configure 'clr enabled',1
reconfigure
go

注册DLL:
为了调用我们写的那个方法,需要在SQL Server中注册我们刚刚编译好的那个DLL。在此之前,要知道在这个项目中如果要访问服务器之外的资源是要配置权限的。如果不配置,后面操作中会出现类似下面的错误。我找到的关于授权配置的内容:连接。

创建登录名和密钥,如果程序集有变更,要删除密钥和登录名重新创建:
USE master;
GO CREATE ASYMMETRIC KEY SQLCLRSyncKey FROM EXECUTABLE FILE = 'C:\MySync.dll'
CREATE LOGIN SQLCLRSyncLogin FROM ASYMMETRIC KEY SQLCLRSyncKey
GRANT EXTERNAL ACCESS ASSEMBLY TO SQLCLRSyncLogin;
GO
DROP LOGIN SQLCLRSyncLogin
DROP ASYMMETRIC KEY SQLCLRSyncKey
创建程序集,DLL变更后要删除重新创建:
USE SyncA;
GO create ASSEMBLY MySync
FROM 'C:\MySync.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS;
GO
然后创建一个函数用于调用这个DLL:
CREATE FUNCTION dbo.fun_sync
(
@strSql nvarchar(max)
)
RETURNS nvarchar(max)
AS EXTERNAL NAME [MySync].[MySync.SyncDataBase].[Sync]
先来测试一下,在SyncA中执行查询:
SELECT dbo.fun_sync('insert into Target(Id,Name,SyncTime) values (null,null,getdate())')

SyncB中添加了一条数据:

下面使用触发器自动的从SyncA中将数据同步到SyncB中,其中的tt表是我临时创建的,用于保存触发器调用返回的结果:
create Trigger tr_source
on [Source]
for INSERT AS
begin
declare @strSql nvarchar(max)
select @strSql='insert into Target(Id,Name,SyncTime) values ('''+cast(Id as nvarchar)+''','''+Title+''',getdate())' from inserted --执行
declare @result nvarchar(max)
select @result=dbo.fun_sync(@strSql) insert into tt(tt) values (@result)
end
直接执行函数没有问题,但是触发器去调用函数执行却出现异常:
false:System.Data.SqlClient.SqlException: 其他会话正在使用事务的上下文。
在 System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
在 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
在 System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
在 System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
在 System.Data.SqlClient.SqlInternalConnectionTds.PropagateTransactionCookie(Byte[] cookie)
在 System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
在 System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
在 System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
在 System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
在 System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
在 System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
在 System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
在 System.Data.SqlClient.SqlConnection.Open()
在 MySync.SyncDataBase.Sync(String strSql)
这个错误中包含了一个false值,说明触发器调用时已经可以走到DLL这一步了。考虑到在查询中直接执行函数,走到DLL这一步是没有错误的。那么错误就发生在触发器和DLL调用产生的冲突,冲突在访问数据库上面,再深入的原因,我也没有找到。
下面使用另外一种方式实现同步,因为错误是触发器和DLL的数据库访问冲突,那么我就绕过数据库的访问。将触发器产生的SQL脚本保存到某个目录下面,然后通过其他程序监听这个目录,执行脚本文件,实现同步。
类库代码
using System;
using System.Data;
using System.Data.Sql;
using Microsoft.SqlServer.Server;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO; namespace MySync
{
public class SyncDataBase
{
[SqlFunction(SystemDataAccess = SystemDataAccessKind.Read, DataAccess = DataAccessKind.Read)]
public static string Sync(string strSql)
{
string result = "true"; try
{
if (!Directory.Exists("c:\\SyncLog"))
{
Directory.CreateDirectory("c:\\SyncLog");
}
string fileName = @"c:\\SyncLog\\" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt";
if (File.Exists(fileName))
File.Delete(fileName); using (StreamWriter sw = File.CreateText(fileName))
{
sw.WriteLine(strSql);
}
}
catch (Exception ex)
{
result = "false:" + ex.ToString();
} return result;
}
}
}
另外创建一个监听程序:MyListen
using System;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Configuration;
using System.Threading;
using System.IO; namespace MyListen
{
class Program
{
static void Main(string[] args)
{
string connSync = ConfigurationManager.ConnectionStrings["connSync"].ToString();
string filePath = ConfigurationManager.AppSettings["filePath"];
while (true)
{
//所有txt文件
string[] fileList = DirFile.GetFileNames(filePath, "*.txt", true);
foreach (var f in fileList)
{
string strSql = "";
using (StreamReader sr = new StreamReader(f))
{
string line;
while ((line = sr.ReadLine()) != null)
{
strSql += line + " ";
}
sr.Close();
}
try
{
using (SqlConnection connection = new SqlConnection(connSync))
{
connection.Open();
SqlCommand command = new SqlCommand(strSql, connection);
command.CommandType = CommandType.Text;
command.ExecuteNonQuery();
connection.Close();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
File.Delete(f);
}
//每10秒扫描一次
Thread.Sleep( * );
}
}
}
}
只要将监听程序打开,就可以实现对数据的同步。项目和数据库下载。
参考:
http://blog.sina.com.cn/s/blog_59c41d0d0100esjn.html
http://www.cnblogs.com/wshcn/archive/2011/12/02/2271630.html
http://www.cnblogs.com/edong/archive/2010/03/10/1682172.html
http://www.cnblogs.com/hsrzyn/archive/2013/05/28/1976555.html
SqlServer调用外部程序实现数据同步的更多相关文章
- Sqlserver事务发布实现数据同步
事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案,所以有机会再次研究一下它以及快照等,发现还是有很多不错的功能和改进的.这 里以sqlserver2008的事务发布功能为例,对发 ...
- 使用Sqlserver事务发布实现数据同步
事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案,所以有机会再次研究一下它以及快照等,发现还是有很多不错的功能和改进 的.这里以sqlserver2008的事务发布功能为例,对发 ...
- SQLServer与Oracle的数据同步(触发器trigger)
说到同步,其实是靠"作业"定时调度存储过程来操作数据,增,删,改,全在里面,结合触发器,游标来实现,关于作业调度,使用了5秒运行一次来实行"秒级作业",这样基本 ...
- 使用Sqlserver事务发布实现数据同步(zhuanqian)
事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案,所以有机会再次研究一下它以及快照等,发现还是有很多不错的功能和改进的.这里以sqlserver2008的事务发布功能为例,对发布 ...
- 使用Sqlserver事务发布实现数据同步(sql2008)_Mssq l数据库教程
事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案,所以有机会再次研究一下它以及快照等,发现还是有很多不错的功能和改进的.这里以sqlserver2008的事务发布功能为例,对发布 ...
- 使用Sqlserver事务发布实现数据同步(转)
出处:http://www.cnblogs.com/daizhj/archive/2009/11/18/1605293.html 事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案 ...
- SQLServer 2012之AlwaysOn —— 指定数据同步链路,消除网络抖动导致的提交延迟问题
事件起因:近期有研发反应,某数据库从08切换到12环境后,不定期出现写操作提交延迟的问题: 事件分析:在排除了系统资源争用等问题后,初步分析可能由于网络抖动导致同步模式alwayson节点经常出现会话 ...
- Sqlserver如何递归查询层级数据将父级字段和本级某个字段合并?如何自定义用户函数并调用?
开门见山,首先说下遇到的问题:前期系统地区字典表中,每个省市县只存了本级名称,没存完整的字段.如:肥西县隶属安徽省合肥市,表中就存了一个肥西县.现有需求需要将完整字段显示,由于系统已在线上运营,无法做 ...
- C#调用API向外部程序发送数据
C#调用API向外部程序发送数据 最近有可能要做一个项目.在项目中有这么一个功能,在A程序中调用B程序,同时在A程序中进行登陆后,要将A程序的登录名和密码自动填充到B程序的登陆对话框中,这样B程序就不 ...
随机推荐
- poj3553 拓扑序+排序贪心
题意:有多个任务,每个任务有需要花费的时间和最后期限,任务之间也有一些先后关系,必须先完成某个才能开始某个,对于每个任务,如果没有越期,则超时为0,否则超时为结束时间-最后期限,求总超时时间最小的任务 ...
- 越狱Season 1-Episode 6: Riots, Drills and the Devil: Part 1
Season 1, Episode 6: Riots, Drills and the Devil: Part 1 - Diamond: Just a few more rides. 就再多玩几次吧 O ...
- Qt GUI@学习日志
day 1: Qt中类: 理解一个类最好还是从其类代码实现上看. 由此图可看出需要好好研究那几个重要的类:Qt/QEvent/QObject/QWidget/. QApplication: (比较复杂 ...
- 嵌入式系统Linux内核开发工程师必须掌握的三十道题(转)
嵌入式系统Linux内核开发工程师必须掌握的三十道题 如果你能正确回答以下问题并理解相关知识点原理,那么你就可以算得上是基本合格的Linux内核开发工程师,试试看! 1) Linux中主要有哪几种内核 ...
- Linux常用配置文件解析
网络相关配置文件 1./etc/sysconfig/network-scripts/ifcfg-ethx 网络参数IP/子网掩码/广播地址等的配置(重启有效) 2./etc/resolv.conf ...
- 论文阅读之 Inferring Analogous Attributes CVPR 2014
Inferring Analogous Attributes CVPR 2014 Chao-Yeh Chen and Kristen Grauman Abstract: The appear ...
- JNI学习2:android 调用C语言方法与C语言调用android方法
#include <jni.h> #include <stdio.h> #include <stdlib.h> #include <jni.h> #in ...
- 《Python自然语言处理》中文版-纠错【更新中。。。】
最近在看<Python自然语言处理>中文版这本书,可能由于是从py2.x到py3.x,加上nltk的更新的原因,或者作者的一些笔误,在书中很多代码都运行不能通过,下面我就整理一下一点有问题 ...
- #linux包之lsof之lsof命令
2015/3/18查漏补缺,反复练习命令,有不明白或疑问的地方直接看man手册页,英文解释的比较清楚 man lsof 已阅 概述 [root@localhost ~]# rpm -qa|grep l ...
- Log4j 使用总结
在实际编程时,要使Log4j真正在系统中运行事先还要对配置文件进行定义.定义步骤就是对Logger.Appender及Layout的分别使用.Log4j支持两种配置文件格式,一种是XML格式的文件,一 ...