SQL Server中无循环/游标实现vwValues视图ExamObjID匹配查询的优化方案咨询
SQL Server中无循环/游标实现vwValues视图ExamObjID匹配查询的优化方案咨询
大家好,我在SQL Server 2008里遇到一个性能瓶颈,想请教有没有办法把游标+标量函数的逻辑改成无循环的代码,先给大家说说我的场景:
视图结构与数据情况
我有一个视图vwValues,结构如下:
| 列名 | 数据类型 |
|---|---|
| ExamObjID | uniqueidentifier |
| Locus | varchar(10) |
| ValOrder | tinyint |
| Value | varchar(5) |
| IndexType | char(1) |
| PersCount | tinyint |
每个ExamObjID对应的Locus数量在6到27之间,每个Locus最多关联4个Value(用ValOrder排序);而且同一个ExamObjID的IndexType和PersCount是唯一的。样本数据示例:
ExamObjID | Locus | ValOrder | Value | IndexType | PersCount f9576b7b-20a1-4d63-8c80-b8ffd68aefc5 | L1T1234 | 1 | 12 | C | 1 f9576b7b-20a1-4d63-8c80-b8ffd68aefc5 | L1T1234 | 2 | 12 | C | 1 f9576b7b-20a1-4d63-8c80-b8ffd68aefc5 | L2T54332 | 1 | 14 | C | 1 f9576b7b-20a1-4d63-8c80-b8ffd68aefc5 | L2T54332 | 2 | 15 | C | 1 f9576b7b-20a1-4d63-8c80-b8ffd68aefc5 | L3R12 | 1 | 11 | C | 1 f9576b7b-20a1-4d63-8c80-b8ffd68aefc5 | L3R12 | 2 | 17 | C | 1
现有逻辑与性能问题
我有一个存储过程,逻辑是:
- 接收指定的
ExamObjID,创建表变量@Target(结构和vwValues类似,去掉IndexType和PersCount),并填充该ExamObjID对应的所有记录。 - 遍历视图中所有
PersCount = 1的其他ExamObjID,用游标配合标量函数逐个对比@Target与这些ExamObjID的记录,判断是否为“匹配项”。
匹配成功的结果会写入SearchResults表,结构如下:
| 列名 | 数据类型 | 说明 |
|---|---|---|
| SearchResultID | uniqueidentifier | 主键 |
| ExamObjID | uniqueidentifier | 目标ExamObjID |
| MatchObjID | uniqueidentifier | 匹配的ExamObjID |
| MisMatchCount | tinyint | 不匹配的Locus数量(取值0-2) |
现在这个方案速度特别慢,数据库里有几十万条记录时,单次搜索要耗时15分钟左右,所以想换成无循环、无游标的实现方式。
详细匹配规则
两个ExamObjID被认定为匹配,需要同时满足:
- 共同Locus数量≥6:两者必须至少有6个相同的
Locus,只有这些共同的Locus才会参与后续判断。 - 最多2个Locus不匹配:在所有共同的
Locus中,最多只能有2个Locus不满足“单Locus匹配规则”,即至少有(共同Locus数-2)个Locus是匹配的。
单Locus匹配规则
如果两个ExamObjID的同一个Locus下,至少存在一个相同的Value,则该Locus视为匹配。比如:
ExamObjID = f9576b7b-20a1-4d63-8c80-b8ffd68aefc5的L1T1234有Value12、14ExamObjID = e1185832-a6b7-4b08-913c-fb75be0f8588的L1T1234有Value14、15
这两个Locus就算匹配,因为它们共享Value=14。
我尝试的代码(有问题)
自己写了一段无循环的代码,但逻辑不对,返回了所有其他ExamObjID,且都显示0个不匹配,代码如下:
DECLARE @ExamObjID UNIQUEIDENTIFIER = '62DA5C53-E70A-473A-923B-388232B79AFF' DECLARE @Target TABLE ( ExamObjID UNIQUEIDENTIFIER, Locus VARCHAR(15), Value VARCHAR(5) ); DECLARE @Result TABLE (ExamObjID uniqueidentifier, MismatchCount tinyint); INSERT INTO @Target SELECT ExamObjID, Locus, Value FROM vwValues WHERE ExamObjID = @ExamObjID; BEGIN SET NOCOUNT ON; SELECT DISTINCT a.ExamObjID, COUNT(CASE WHEN b.value = a.value THEN 1 END) AS Matches, COUNT(CASE WHEN b.value <> a.value THEN 1 END) AS Mismatches INTO #temp FROM vwValues a INNER JOIN @Target b ON a.Locus = b.Locus AND a.value = b.value AND a.ExamObjID <> b.ExamObjID GROUP BY a.ExamObjID INSERT INTO @Result(ExamObjID, MismatchCount) SELECT ExamObjID, Mismatches FROM #temp WHERE Matches >= 6 AND Mismatches <= 2; DROP TABLE #temp; SELECT * FROM @Result END
我不是专业开发,真的很想优化这个性能问题,希望大家能给点思路或者告诉我是否可行,非常感谢!
备注:内容来源于stack exchange,提问作者Stas Hladow
相关产品推荐
相关产品推荐

