在连接中使用OR条件+函数似乎会混淆SQL Server查询优化器

时间:2014-04-22 16:39:36

标签: sql sql-server

我正在努力调试特定查询的性能。查询是这样的:

select count(*)  
FROM dbo.user d
INNER JOIN dbo.distinct_first_name dfn ON (
        [dbo].jw(dfn.first_name, 'john') > 0.8
        AND
        (d.first_name = dfn.first_name
         OR d.nick_name = dfn.first_name
         OR d.middle_name = dfn.first_name)
        )

查询在不同的名字表(包含大约15k行)上运行Jaro Winkler过滤器,然后内部将其连接到用户表以生成结果集。根据定义,这需要大约1分钟才能在用户表中运行大约500k行。

以下是我所知道的:

1)Jaro Winkler过滤器几乎是即时的(单独0.1秒)

2)如果我将user子句更改为仅包含其中一列(即删除OR),则只需0.4s

3)如果我将此更改为三个查询,并将它们背靠背运行,则需要大约2秒

4)如果我将Jaro Winkler过滤器更改为0.99(因此只有一个结果),则查询执行时间没有实质性差异

5)如果我用相等操作替换Jaro Winkler过滤器(dfn.first_name ='john'),则总查询时间减少到4s

(所有时间都相当缓慢;现实生活中的表现会更好。)

因此,由于某种原因,函数和OR的组合使查询优化器混乱。执行计划的信息量不大;它说90%的查询花费在:

<RelOp NodeId="63" PhysicalOp="Clustered Index Seek" LogicalOp="Clustered Index Seek" EstimateRows="1.69029" EstimateIO="0.003125" EstimateCPU="0.000158859" AvgRowSize="17" EstimatedTotalSubtreeCost="71.4311" TableCardinality="15958" Parallel="0" EstimateRebinds="448881" EstimateRewinds="0.504024" EstimatedExecutionMode="Row">
                              <OutputList>
                                <ColumnReference Database="[mydb]" Schema="[dbo]" Table="[distinct_first_name]" Alias="[dfn]" Column="first_name" />
                              </OutputList>
                              <RunTimeInformation>
                                <RunTimeCountersPerThread Thread="0" ActualRows="857936" ActualEndOfScans="859454" ActualExecutions="859454" />
                              </RunTimeInformation>
                              <IndexScan Ordered="1" ScanDirection="FORWARD" ForcedIndex="0" ForceSeek="0" ForceScan="0" NoExpandHint="0" Storage="RowStore">

向上拆分查询实际上是一个选项,因为这是一个选项,我可能会稍微重新设计一下架构,但是我很难过这会让你陷入困境。有什么想法吗?

2 个答案:

答案 0 :(得分:2)

您可能想尝试的一些事项:

  • dfn表的聚集索引是什么?它只是一个名字的表,仅此而已吗?如果是,请删除自动编号列(如果有)并将名称设置为聚簇索引。

  • '约翰'是你争吵的争论吗?我认为是。您可以先在两个名称数据集中的最小值上计算Jaro Winkler过滤器,然后将其插入临时表中。然后加入临时表上的另一个表。请记住,临时表也可以从索引中受益(如果添加它们)。

  • 您可以通过创建多列索引来提高性能:名字,昵称,中间名称。由于您在where语句中引用了所有列,因此单个索引的有用性会下降。

  • 我认为运行SQL Tuning顾问工具并查看它提出了哪些建议总是很有趣。只需将监视器连接到SQL Server实例,并将查询的执行记录到工作负载文件中。然后,您可以将工作负载文件提供给顾问程序工具,如果启用该选项,它将建议索引,统计信息甚至架构更改。

  • 尽可能预先计算。如果我没记错,在Jaro Winkler过滤器中,字符串长度是一个重要因素。您可以使用名称的字符串长度向dfn表添加列。像函数和视图这样的东西很不错,但并不是最好的性能。该功能就像一个黑盒子,无法使用任何预先存在或预先计算的数据。

最重要的是:衡量您的结果。 SQL查询优化器有自己的想法。密切关注执行计划并尝试不同的方案。

基于文本列的查询总是更难以优化。您可能希望查看全文索引以提高性能,但这是一个需要调查的单独主题。

答案 1 :(得分:1)

首先,first_name_alphaonlynick_name_alphaonly实际上都是非持久计算列,因此所有基数都关闭,然后相乘。

然后,在distinct_first_name表上有857.936个单独的聚簇索引,并且仅在此之后才应用包含jw函数的过滤器。

在计算列上创建索引会有所帮助。在加入之前对distinct_first_name进行过滤(进入#temp表)也可能有所帮助。然后是关于将OR转换为UNION ALL的建议。

优化器afaik永远不会将OR重新排列到UNION本身。相信它被称为安全。