sql CLR

1、什么是SQLCLR

SQL CLR (SQL Common Language Runtime) 是自 SQL Server 2005 才出现的新功能,它将.NET Framework中的CLR服务注入到 SQL Server 中,使得.NET代码可在SQL Server服务器进程中执行。

通过在 Microsoft SQL Server 中托管 CLR(称为 CLR 集成),开发人员可以在托管代码中编写存储过程、触发器、用户定义函数、用户定义类型和用户定义聚合函数, 改变了以前只能通过T-SQL语言来实现这些功能的局面。因为托管代码在执行之前会编译为本机代码,所以,在有些方案中可以大大提高性能。

2、在项目中遇到一些业务逻辑比较复杂的操作,例如对数据进行清洗,Load 100M的数据,要对这些数据每个进行多种合法性规则验证、多种筛选条件的筛选,计算派生列等等操作。如果在数据库中实现比较困难,所以采用了存储过程加CLR结合的方法。在存储过程中可以调用CLR方法,执行一些存储过程难以实现的操作,利用它们各自的优势提高性能。

3、以下是一个简化的例子,旨在说明实现步骤。

3.1 新建类库项目CLRBridge(特别注意,框架要选择3.5及以下版本,不然部署时会报版本不支持的错误)

CLR桥接器,只定义了两个存储过程

a、Hello  仅作为测试用

b、InvokeDataProcessEngine 实际用到的存储过程,输入参数提供必要的一些参数,输出参数返回执行的结果以及提示信息。

仅定义这么一个执行的接口,传入实际工作的类名称,实例化,执行,这样可以简化部署,因为可能工作类会增加删除,不用每次都部署CLRBridge Assembly

3.2 新建控制台项目DataProcess

实际执行DataProcess的项目

a、在CLR.Bridge.MainEntry中会启动进程DataProcess.exe

b、StoryBoard是一个基类,提供Create、Start等虚方法,子类可对其重写,满足自己需求。

Create主要是根据输入参数实例化StoryBoard,而真正的逻辑操作在Start里面。

c、Program的main函数接受输入参数,实例化相应的StoryBoard,并调用Start,输出结果。

3.3 新建控制台项目testCLR

testCLR:因在sql查询分析器里面不方便调试,这个项目主要是对CLRBridge进行测试的

项目结构如下图

4、代码

4.1 CLRBridge\MainEntry.cs

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;

namespace CLRBridge
{
    public class Utility
    {
        //连接字符串,
        //#if DEBUG
        //使用testCLR.exe测试时
        //public static string ConnectionString = @"Database=TestClr;Server=.;User ID=sa;[email protected];Connection Timeout=90;Asynchronous Processing = True;";
        //#else
        //使用数据库的查询分析器、或者在数据库存储过程中测试时
        public static string ConnectionString = @"Context Connection=true";
        //#endif
    }
    public class DataProcessEngineConfigurationInfo
    {
        //在数据库的Info表中存储exe的路径及名称,不写死,后续如有变动方便修改
        public string DataProcessEngineExeName { get; private set; }
        public string DataProcessEngineBaseLocation { get; private set; }
        public string DataProcessEngineExeFullPath
        {
            get
            {
                return Path.Combine(DataProcessEngineBaseLocation, DataProcessEngineExeName);
            }
        }
        public DataProcessEngineConfigurationInfo()
        {
            Get();
        }
        private void Get()
        {
            using (SqlConnection conn = new SqlConnection(Utility.ConnectionString))
            {
                conn.Open();
                using (SqlCommand cmd = new SqlCommand("select * from info", conn))
                {
                    cmd.CommandType = System.Data.CommandType.Text;

                    using (SqlDataReader sqlReader = cmd.ExecuteReader())
                    {
                        // ApplicationSettings Info
                        while (sqlReader.Read())
                        {
                            if (((string)sqlReader["Tag"]).Trim() == "DataProcessEngineExeName")
                                DataProcessEngineExeName = ((string)sqlReader["Value"]).Trim();
                            else if (((string)sqlReader["Tag"]).Trim() == "DataProcessEngineBaseLocation")
                                DataProcessEngineBaseLocation = ((string)sqlReader["Value"]).Trim();
                        }
                    }
                }
            }
        }
    }

    public class MainEntry
    {
        [Microsoft.SqlServer.Server.SqlProcedure]
        public static int Hello()
        {
            return 10;
        }

        [Microsoft.SqlServer.Server.SqlProcedure]
        public static int InvokeDataProcessEngine(string storyboardName, short parameterCategory, short parameterDataType, string parameterValue, string outputParameterName, out string outputParameterValue, out string outputMessage)
        {
            int resultCode = -1;
            ProcessStartInfo start = new ProcessStartInfo();
            //start.FileName = @"C:\zxc\CLRBridge\DataProcess\bin\Debug\DataProcess.exe";
            DataProcessEngineConfigurationInfo config = new DataProcessEngineConfigurationInfo();
            start.FileName = config.DataProcessEngineExeFullPath; //
            start.Arguments = string.Format("\"{0}\" \"{1}\" \"{2}\" \"{3}\"", storyboardName, parameterCategory, parameterDataType, parameterValue);
            start.UseShellExecute = false;
            start.RedirectStandardOutput = true;
            //启动外部进程,外部进程根据输入的参数实例化相应的类,执行相关费时且逻辑复杂的操作
            using (Process p = new Process())
            {
                p.StartInfo = start;
                p.Start();
                p.WaitForExit();
                using (StreamReader reader = p.StandardOutput)
                {
                    string result = reader.ReadToEnd();
                    Match m = Regex.Match(result, "Result code:(\\d+),");
                    Match m2 = Regex.Match(result, string.Format("OutputParameter {0}:(\\d+),", outputParameterName));
                    int.TryParse(m.Groups[1].Value, out resultCode);
                    outputParameterValue = m2.Groups[1].Value.Trim();

                    Match m3 = Regex.Match(result, "OutputMessage: (.+),");
                    outputMessage = m3.Groups[1].Value.Trim();
                }
            }
            return resultCode;
        }
    }
}

  

4.2 DataProcess

4.2.1 DataProcess\Storyboard.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataProcess
{
    public abstract class Storyboard
    {
        public string ParameterName { get; set; }
        public object ParameterId { get; set; }
        private string _name;
        public virtual string OutputParameterName { get; protected set; }
        public virtual string OutputParameterValue { get; protected set; }
        public abstract void Start();
        public virtual void PreStart()
        {
            // Nothing to do.
        }
        public virtual void PostStart()
        {
            // Nothing to do.
        }
        public virtual void OnStoryboardCompleted()
        {
            // Nothing to do.
        }
        public virtual void OnStoryboardSuccessful()
        {
            // Nothing to do.
        }
        public virtual void OnStoryboardError()
        {
            // Nothing to do.
        }
        public Storyboard()
        {
            string fullTypename = this.GetType().FullName;
            int lastIndexOfDot = fullTypename.LastIndexOf(‘.‘);
            _name = fullTypename.Substring(lastIndexOfDot + 1, fullTypename.Length - lastIndexOfDot - 1);
        }
        public virtual string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }
        public string CurrentOperand { get; set; }

        public static Storyboard Create(string storyboardName, ParameterResolver paramResolver)
        {
            //使用配置文件配置需要实例化的类名称及命名空间,运行时依赖注入,
            //1、这样避免写很多if else,或者select case (如下注释掉的部分)
            //2、解耦,在增加storybord类型时,只需要在配置文件中添加即可
            //StoryboradConfigNode sbConfig = sbNameOperandMap[storyboardName.ToLowerInvariant()];
            //Type t = Type.GetType(string.Format("{0}.{1}", sbConfig.Namespace, sbConfig.Name));
            Type t = Type.GetType(string.Format("{0}.{1}", "DataProcess", "LVNStoryBoard"));
            object parameterId = paramResolver.Resolve();
            Storyboard sb = (Storyboard)Activator.CreateInstance(t, parameterId);
            sb.CurrentOperand = storyboardName;

            //sb.StoryboardConfig = sbConfig;
            //sb.BindStoryboardConfig();
            //if (storyboardName.ToLowerInvariant() == "loadandvalidate")
            //{
            //    Guid fileDefinitionId = (Guid)paramResolver.Resolve();
            //    sb = new LNVStoryboard(fileDefinitionId);
            //    IStoryboardConfig storyboardConfig = (IStoryboardConfig)EngineConfigurationManager.Instance.GetConfig(sb.Name);
            //    sb.StoryboardConfig = storyboardConfig;
            //    MessageLoggerManager.GetLogger().DebugVerbose("Leaving Storyboard.Create...");
            //}
            //else if (storyboardName.ToLowerInvariant() == "viewandupdatefilter")
            //{
            //    Guid viewAndUpdateId = (Guid)paramResolver.Resolve();
            //    sb = new ViewAndUpdateStoryboard(viewAndUpdateId);
            //    IStoryboardConfig storyboardConfig = (IStoryboardConfig)EngineConfigurationManager.Instance.GetConfig(sb.Name);
            //    sb.StoryboardConfig = storyboardConfig;
            //    MessageLoggerManager.GetLogger().DebugVerbose("Leaving Storyboard.Create...");
            //}

            if (sb != null)
            {
                   return sb;
            }
            else
                throw new Exception(string.Format("Storyboard {0} is not supported...", storyboardName));
        }

    }

    public class ParameterResolver
    {
        public short ParameterCategory { get; set; }
        public short ParameterDataType { get; set; }
        public string ParameterValue { get; set; }

        public virtual object Resolve()
        {
            throw new NotImplementedException();
        }

        protected ParameterResolver() { }

        public static ParameterResolver CreateParameterResolver(short parameterCategory, short parameterDataType, string parameterValue)
        {
             if (parameterCategory == 0)
            {
                SimpleParameterResolver paramResolver = new SimpleParameterResolver();
                paramResolver.ParameterCategory = parameterCategory;
                paramResolver.ParameterDataType = parameterDataType;
                paramResolver.ParameterValue = parameterValue;
                return paramResolver;
            }
            else
                throw new Exception(string.Format("ParameterResolver {0} not support yet...", parameterCategory));
        }
    }

    public class SimpleParameterResolver : ParameterResolver
    {
        public override object Resolve()
        {
            switch (this.ParameterDataType)
            {
                case 0: return ParameterValue;
                case 1: return decimal.Parse(ParameterValue);
                case 2: return int.Parse(ParameterValue);
                case 3: return short.Parse(ParameterValue);
                case 4: return new Guid(ParameterValue);
                case 5: return char.Parse(ParameterValue);
                default: return null;
            }
        }
    }
}

  4.2.2  DataProcess\LVNStoryBoard.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataProcess
{
    class LVNStoryBoard:Storyboard
    {
        //fileDefinitionId为输入参数,可能在SolveData的时候需要,但是有时候可能并不需要输入参数
        public LVNStoryBoard(int fileDefinitionId)
        {
            this.ParameterId = fileDefinitionId;
            this.ParameterName = "FileDefinitionId";
        }

        public override string OutputParameterName
        {
            get
            {
                return "DataJobId";
            }
        }

        public override void Start()
        {
            //会有很多个串行或者并行的操作在这里执行
           string result= SolveData();
           this.OutputParameterValue = result;
        }

        public string SolveData()
        {
            //例如对数据库中的数据进行运算
            //例如LoadDataAndValid,包括以下Action,
            //FetchCommonPropertiesAction、CreateDataJobAction、FetchDataJobPropertiesAction、GetFilterInfoAction、FetchImportStatusAction等等
            //每个Action也有各自的业务逻辑,这些业务逻辑在存储过程中实现比较困难
            //数据操作后最终的结果会存入数据库中
            return "1000";
        }

    }
}

  4.2.3 DataProcess\Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataProcess
{
    class Program
    {
        static void Main(string[] args)
        {
            string storyboardName = string.Empty;
            short parameterCategory = -1;
            short parameterDataType = -1;
            string parameterValue = string.Empty;
            int errorCode = -900000;
            string outputMessage = "";
            bool IsDebug = false;  //也可以写在配置文件里面
            if (IsDebug)
            {
                //也可以写在配置文件里面
                storyboardName = "LVNStoryBoard";
                parameterCategory = 0;
                parameterDataType = 2;  //int
                parameterValue = "1";
            }
            else
            {
                storyboardName = args[0].Trim();
                parameterCategory = short.Parse(args[1].Trim());
                parameterDataType = short.Parse(args[2].Trim());
                parameterValue = args[3].Trim();
            }
            ParameterResolver paramResolver = ParameterResolver.CreateParameterResolver(parameterCategory, parameterDataType, parameterValue);

            Storyboard sb = null;
            try
            {
                sb = Storyboard.Create(storyboardName, paramResolver);
                sb.PreStart();
                sb.Start();
                sb.PostStart();
                errorCode = 0;
                outputMessage = "Successful";
                sb.OnStoryboardSuccessful();
            }
            catch (Exception ex)
            {
                outputMessage = "Error: " + ex.Message;
                if (sb != null)
                    sb.OnStoryboardError();

            }
            string outputParameterName = string.Empty;
            string outputParameterValue = string.Empty;
            if (sb != null)
            {
                outputParameterName = sb.OutputParameterName;
                outputParameterValue = sb.OutputParameterValue;
            }
            if (errorCode != 0)
            {
                Console.WriteLine(string.Format("Result code:{0}, (DataProcessEngine failed)...", errorCode));
                Console.WriteLine(string.Format("OutputParameter {0}:{1}, ...", outputParameterName, outputParameterValue));
                Console.WriteLine(string.Format("OutputMessage: {0},", PostProcessOutputMessage(outputMessage)));
            }
            else
            {
                Console.WriteLine(string.Format("Result code:{0}, (DataProcessEngine successfully)...", errorCode));
                Console.WriteLine(string.Format("OutputParameter {0}:{1}, ...", outputParameterName, outputParameterValue));
                Console.WriteLine(string.Format("OutputMessage: {0},", PostProcessOutputMessage(outputMessage)));
            }
            if (sb != null)
                sb.OnStoryboardCompleted();

        }

        private static string PostProcessOutputMessage(string outputMessage)
        {
            return outputMessage.Replace(Environment.NewLine, " ");
        }
    }
}

  4.3 testCLR\Program.cs

using CLRBridge;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace testCLR
{
    class Program
    {
        static void Main(string[] args)
        {
            //InvokeDataProcessEngine(string storyboardName, short parameterCategory, short parameterDataType,
                                    //string parameterValue, string outputParameterName, out string outputParameterValue,
                                    //out string outputMessage)
            string s;
            string msg;
            MainEntry.InvokeDataProcessEngine("LVNStoryBoard", 0, 2, "1", "DataJobId", out s, out msg);

        }
    }
}

5、新建数据库TestCLR,并新建表

USE [TestCLR]
GO

/****** Object:  Table [dbo].[Info]    Script Date: 12/30/2015 10:13:24 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Info](
	[Tag] [nvarchar](50) NULL,
	[Value] [nvarchar](50) NULL
) ON [PRIMARY]

GO
DELETE Info--项目DataProcess.exe所在的实际路径及名称
INSERT [Info] ([Tag],[Value]) VALUES ( ‘DataProcessEngineBaseLocation‘,‘C:\CLRBridge\DataProcess\bin\Debug\‘)
INSERT [Info] ([Tag],[Value]) VALUES ( ‘DataProcessEngineExeName‘,‘DataProcess.exe‘)

  

6、部署CLR

--show clr state
sp_configure ‘clr enabled‘

--enable clr --1,enable clr\0,disable clr
exec sp_configure ‘clr enabled‘,1  

--TRUSTWORTHY 数据库属性用于指明 SQL Server 实例是否信任该数据库以及其中的内容
ALTER DATABASE TESTCLR SET TRUSTWORTHY ON;

--create a Assmbies
create ASSEMBLY [CLRBridge]
FROM ‘C:\zxc\CLRBridge\CLRBridge\bin\Debug\CLRBridge.dll‘
WITH PERMISSION_SET = UNSAFE  

GO

CREATE PROCEDURE [dbo].[usp_InvokeDataProcessEngine]
	@storyboardName [nvarchar](100),
	@parameterCategory [smallint],
	@parameterDataType [smallint],
	@parameterValue [nvarchar](4000),
	@outputParameterName [nvarchar](100),
	@outputParameterValue [nvarchar](4000) OUTPUT,
	@outputMessage [nvarchar](4000) OUTPUT
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [CLRBridge].[CLRBridge.MainEntry].[InvokeDataProcessEngine]

GO

CREATE PROCEDURE [dbo].[Hello]
   -- @name [nvarchar](4000) OUTPUT
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [CLRBridge].[CLRBridge.MainEntry].[Hello]
GO

部署后在sql中会有两个存储过程Hello,usp_InvokeDataProcessEngine及新建的Assembly CLRBridge

 

 7、在sql中进行测试

DECLARE	@return_value int
 DECLARE @outputval BIGINT, @message NVARCHAR(4000) 

 EXEC @return_value = usp_InvokeDataProcessEngine ‘LVNStoryBoard‘, 0, 2, ‘810‘, ‘DataJobId‘, @outputval OUTPUT, @message OUTPUT

 PRINT @return_value
 PRINT @outputval
 PRINT @message

 DECLARE	@return_value1 int
 EXEC @return_value1=hello
 PRINT @return_value1

  

8、在部署过程中可能出现的问题

create ASSEMBLY [CLRBridge]
FROM ‘C:\CLRBridge\CLRBridge\bin\Debug\CLRBridge.dll‘
WITH PERMISSION_SET = UNSAFE
报错 1
Could not obtain information about Windows NT group/user
解决
ALTER DATABASE TESTCLR SET TRUSTWORTHY ON;
原因:TRUSTWORTHY 数据库属性用于指明 SQL Server 实例是否信任该数据库以及其中的内容

http://www.cnblogs.com/chendaoyin/archive/2013/12/23/3487182.html
http://blog.csdn.net/chen_xizhang/article/details/5952608

报错2 :System.Security.SecurityException

解决:

create ASSEMBLY [CLRBridge]
FROM ‘C:\zxc\CLRBridge\CLRBridge\bin\Debug\CLRBridge.dll‘
WITH PERMISSION_SET = UNSAFE

http://stackoverflow.com/questions/28268373/deploying-sql-clr-project-fails-when-creating-assembly-on-database

报错3:在数据库中执行

EXEC @return_value = usp_InvokeDataProcessEngine ‘LVNStoryBoard‘, 0, 2, ‘810‘, ‘DataJobId‘, @outputval OUTPUT, @message OUTPUT

总是报 Press Acess Deny的错误system.ComponentModel.Win32Expcetion: Access is denied

后来把DataProcess.exe的路径换了地方就好了,开始放在VS2013默认project路径下。这个路径下的文件好像是受保护的,我有时候手动都不能删除。

9、源码下载

参考:

SQL Server CLR全功略之一---CLR介绍和配置

SQLCLR

时间: 2024-10-08 19:10:54

sql CLR的相关文章

SQL CLR学习

SQL CLR (SQL Common Language Runtime) 是自 SQL Server 2005 才出现的新功能,它将.NET Framework中的CLR服务注入到 SQL Server 中,让 SQL Server 的部分数据库对象可以使用 .NET Framework 的编程语言开发(目前只支持VB.NET和C#),包括预存程序.用户自定义函数.触发程序.用户自定义类型以及用户自定义汇总函数等功能[1]. 目录 1 架构 2 安全性 3 示例 4 参考资料 架构 SQL C

SQL Server Assembly (SQL CLR) 还原数据库后的问题

最近弄项目迁移的时候遇到还原数据库(SQL Server 2008)后遇到的一个问题: 消息 10314,级别 16,状态 11,第 1 行 在尝试加载程序集 ID 65536 时 Microsoft .NET Framework 出错.服务器可能资源不足,或者不信任该程序集,因为它的 PERMISSION_SET 设置为 EXTERNAL_ACCESS 或 UNSAFE.请重新运行查询,或检查有关的文档了解如何解决程序集信任问题.有关此错误的详细信息: System.IO.FileLoadEx

c# SQL CLR 之一

CLR就是公共运行时,本文就对c#编写SQL StoredProcedures的过程进行简单讲解. [步骤] 2. 3. 7.打开设置 8. 注意删除方式:注意删除Assembly时,一定要先把引用此Assembly的所有东西删除drop proc QueryQueueSPgo drop assembly QueryQueue 最后说一下CLR存储过程的部署: Create proc 存储过程名 as EXTERNAL NAME 数据库中Assembly名称.程序集中Assembly名称(类名)

SQL CLR 储存过程与函数

sql clr项目注意

1.如果引用了其他第三方的dll没有在系统里注册的话会报错,需要手工引用,引用的时候可能需要不安全的使用授权,如果没有权限则使用以下语句获取 alter database Class01New_Cache set trustworthy on Alter AUTHORIZATION ON database::Class01New_Cache TO sa

deploy sql clr

http://umashanthan.blogspot.com/2014/03/how-to-create-and-deploy-clr-in-sql.html http://blogs.msdn.com/b/dataaccesstechnologies/archive/2011/10/29/deploying-sql-clr-assembly-using-asymmetric-key.aspx

【转】SQL SERVER CLR存储过程实现

最近做一个项目,需要做一个SQL SERVER 2005的CLR的存储过程,研究了一下CLR的实现.为方便以后再使用,在这里总结一下我的实现流程,也供对CLR感兴趣但又不知道如何实现的朋友们做一下参考,如果有不同意见,望多指教.在这里先以实现CLR存储过程为例子来进行说明. 1.首先,启用数据库的CLR功能 sp_configure 'clr enabled', 1;          GO          RECONFIGURE;          GO 2.即然是要创建CLR存储过程,那么

SQL Server CLR 使用 C# 自定义存储过程和触发器

这一篇博客接着上一篇博客继续介绍 SQL CLR Stored Procedure 和 CLR Trigger, 上一篇博客介绍了 SQL CLR Function 的使用,以及 CLR 程序集的注册和 CLR Function 的注册. 我的上一篇博客:SQL Server CLR 使用 C# 自定义函数 四.CLR Stored Procedure 接下来在之前的项目选择添加新项,选择 SQL CLR C# 存储过程. public partial class StoredProcedure

SQL Server CLR 使用 C# 自定义函数

原文:SQL Server CLR 使用 C# 自定义函数 一.简介 Microsoft SQL Server 2005之后,实现了对 Microsoft .NET Framework 的公共语言运行时(CLR)的集成.CLR 集成使得现在可以使用 .NET Framework 语言编写代码,从而能够在 SQL Server 上运行,现在就可以通过 C# 来编写 SQL Server 自定义函数.存储过程.触发器等.我最初的目的是因为在 SQL Server 数据库中遇到数字的十进制与十六进制的