使用SQL Server2005扩展函数进行性能优化
SQL Server2005扩展函数已经不是一件什么新鲜的事了,但是我看网上的大部分都是说聚合函数,例子也比较浅,那么这里就讲讲我运用扩展函数来优化数据库性能的例子,希望和大家一起分享这个经验。如果你还不知道什么是SQLCLR,那么你可以参考:SQL Server扩展函数的基本概念。
需求说明 大家在使用SQL Server开发的时候一定会遇到这样的需求,那就是通过Table_Name1表的两个字段Column1、Column2来查询在Table_Name2表中符合这两个条件的记录,并返回Table_Name2中的字段Column3,面对这样的需求,你也许会说使用表连接就可以了,对的,没错,我也是这样想的,但是有的时候往往要面对不同的突发情况,那就是并不是一定会Column1与Column2是全匹配的查询,可能中间还需要一些逻辑的处理,比如字符串的截取后再匹配等等。 这个时候我们通常会在SQL Server中写一个函数,这个函数接收两个参数:Column1、Column2,函数体里面做一些逻辑处理,在通过处理好的参数去查询Table_Name2表,并返回相应的值。很好,那下面我们来计算下图中数据的查询情况。假设表1的数据有50W,表2的数据有4W,在表2没有索引的条件下,查询的复杂度就有50W*4W了,两个表都需要做全表扫描,表2的全表扫描就会达到50W次。 (图1:需求说明) 优化1:这一个优化,每个开发人员都知道,那就是对表2的两个查询字段分别建立索引。这样的优化和之前相比,性能将会提高N个等级。 优化2:这第二个优化方法是使用SQL Server的复合索引,在表2上创建一个复合索引,这个符合索引包括需要查询的两个字段,其实就是把两个字段的内容生成一个索引,其中索引包含了两个索引的排序。 优化3:这第三个优化方法是使用SQL Server2005之后版本才有的索引-包含性索引(Include),就是在优化2的基础上,把需要返回的字段也一起放入到索引中,这样的查询就只需要查询索引就够了,不需要再读取数据页了,减少磁盘的IO消耗。不过这个方法也不是万能,因为有时可能返回的字段会比较多,有时几个字段加起来的长度有可能超出了900个字符(索引大小范围),如果想了解可以进入:SQL Server 索引中include的魅力(具有包含性列的索引) 优化4:在不考虑一些分区、分表、分到不同的磁盘等优化方式的情况下,我们是否还能进一步优化我们的查询呢?这就是这篇文章想要告诉你的,因为我们的回答是:有的。那就是通过SQLCLR的UDT,把表2的数据一次性加载到内存,那么在进行表1查询的时候,我们不需要通过B+树来查询数据了,直接到内存中查询,这样之所以快是因为操作内存要比操作磁盘要快得多。这其中会有些局限性和缺点,具体见下面的缺点描述。 设计思路
测试结果 测试数据:表2有4.6732万条记录,表1有54.2524万条记录。 经过测试:
代码 using System;using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Collections; using System.Collections.Generic; public partial class UserDefinedFunctions { //经过测试发现:使用Hashtable和SortedList没有使用IDictionary的性能好. //IDictionary<string,string>中使用string比SqlString的性能要高. private static readonly IDictionary<string,string> resultCollectionDic = new Dictionary<string,string>(); static UserDefinedFunctions() { GetTableFromDB(resultCollectionDic); } /// <summary> /// 从数据库中获取某个表的数据. /// </summary> /// <param name="resultCollection"></param> private static void GetTableFromDB(IDictionary<string,string> resultCollectionDic) { using (SqlConnection connection = new SqlConnection("context connection=true")) { connection.Open(); using (SqlCommand selectMGT = new SqlCommand("SELECT NS,NP,HLR FROM dbo.zh_mgt ORDER BY NS,NP",connection)) { using (SqlDataReader zhmgtReader = selectMGT.ExecuteReader()) { while (zhmgtReader.Read()) { string NS = zhmgtReader["NS"].ToString(); string NP = zhmgtReader["NP"].ToString(); string HLR = zhmgtReader["HLR"].ToString(); string key = NS + "+" + NP; if (!resultCollectionDic.ContainsKey(key)) { resultCollectionDic.Add(key,HLR); } } } } connection.Close(); } } /// <summary> /// 暴露给SQL Server调用的函数. /// </summary> /// <param name="NS">参数1</param> /// <param name="NP">参数2</param> /// <returns></returns> [SqlFunction(DataAccess = DataAccessKind.Read)] public static SqlString FunctionImsi2HLR2(string NS,int NP) { string result = null;//这里设置为null是为了在方法IMSI2HLR2中判断继续循环. string key = NS + "+" + NP.ToString();//使用特殊符号+连接两个列作为key值. if (resultCollectionDic.ContainsKey(key)) result = resultCollectionDic[key].ToString(); return new SqlString(result); } }; 调用方式对比 --1:这个是在NP和NS字段中分别建立索引SELECT @rc=HLR FROM zh_mgt WHERE NP=7 and NS=@mgt --2:这个是在NP、NS、HLR字段中建立了一个包含性索引(Include) SELECT @rc=HLR FROM zh_mgt WHERE NS=@mgt and NP=7 --3:这是使用SQLCLR扩展函数的调用方法 SELECT @rc= dbo.FunctionImsi2HLR2(@mgt,7) 优点
缺点
疑问
总结 虽然这样的方式比较难在现实的运用中被使用,因为有很多局限性和缺点,但是我写这篇文章的初衷就是想让大家知道在特殊的情况下,还有这样一种优化的方法可以使用。 作者:听风吹雨出处:http://gaizai.cnblogs.com/(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |