需求说明
大家在使用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+树来查询数据了,直接到内存中查询,这样之所以快是因为操作内存要比操作磁盘要快得多。这其中会有些局限性和缺点,具体见下面的缺点描述。
设计思路
@H_502_33@测试结果
测试数据:表2有4.6732万条记录,表1有54.2524万条记录。
经过测试:
@H_502_33@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);
}
};
调用方式对比
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)
优点
@H_502_33@缺点
@H_502_33@疑问
@H_502_33@总结
虽然这样的方式比较难在现实的运用中被使用,因为有很多局限性和缺点,但是我写这篇文章的初衷就是想让大家知道在特殊的情况下,还有这样一种优化的方法可以使用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。