id!= id的意外表扫描

时间:2009-05-16 11:03:48

标签: sql sql-server sql-server-2005 indexing

一个应用程序会导致Sql Server 2005数据库负载过重。 我们不控制运行此查询的应用程序每分钟一次:

select id,col1,col2,col3 from table where id != id

请注意 id!= id ,表示行不等于自身。不出所料,结果始终没有找到行。但是,每次运行此查询时,Sql Server都会执行聚簇索引扫描扫描!

id列定义为:

varchar(15) not null primary key

查询计划显示“估计行数”的大量数字。有谁知道为什么Sql Server需要表扫描来找出明显的?

13 个答案:

答案 0 :(得分:6)

我会把这个问题伪造成......用视图抽象出来,然后欺骗查询。

将现有表'table'重命名为'table_org'或其他内容,并创建如下视图:

CREATE VIEW table
AS
SELECT * FROM table_org
WHERE id='BOGUSKEY'

现在,你应该通过主键上的表进行1次扫描,它应该(就像原始查询一样)什么也找不到。该应用程序不知道更聪明......

答案 1 :(得分:4)

你最大的问题不是表扫描。你最大的两个问题是:

  • 对于您的数据库,您有一个绝对无用的查询,每分钟运行100次。我的猜测是,正如Marc Gravell建议的那样,查询实际上是试图从表中获取列名。

更重要的是:

  • 您无法控制访问数据库的人员或内容。

第二个问题尤其会导致你无休止的头痛。假设您是组织中数据团队的一员(因为您是尝试解决此问题的人),您真的应该考虑进行组织变更以完成工作。

祝你好运!

答案 2 :(得分:3)

每个值与其余的n-1值进行比较。这就是为什么它为“估计的行数”返回一个巨大的数字。对于上述问题,最好不要使用。

这里的文章是你问题的一个很好的指针。我希望它对你有所帮助。 http://www.sqlservercentral.com/articles/Performance+Tuning/2924/

答案 3 :(得分:3)

我见过这种类型的查询。

开发人员很可能根据用户输入,当前设置或其他一些因素构建“WHERE”子句。在许多情况下,也许是默认实例,它们需要一个仅占位符的WHERE子句。那时他们使用'id!= id','1<>等标准。 1'等等。

“每分钟数百次”也让我相信这是一个被误导的默认占位符。

有时他们会使用相反的标准,如果默认情况需要所有行,则总是评估为真。

这是一个很长的镜头,但我的建议是看看你是否可以修改应用程序设置并查看此查询是否消失。最终可能会得到一个小的结果集,但是SQL Server会更频繁地运行并且更好地处理这些结果。

答案 4 :(得分:3)

您可能希望让SQL Server支持团队了解此查询(将列定义为主键时的id<> id)以及它导致的全表扫描,并查看他们是否可能需要在查询引擎中添加优化以确保这不会导致全表扫描。

要么是这样,要么与您无法控制的应用支持团队交谈。

编辑:尝试http://social.msdn.microsoft.com/forums/en-US/sqldatabaseengine/threads/上的TechNet论坛报告行为。

答案 5 :(得分:2)

你在id列上有非聚集索引吗?如果没有,最有效的课程将始终是CIX扫描。尝试在ID列上添加NCIX - 它仍然可以执行扫描,但至少它将扫描一个非常小的索引。如果您使用的是SQL Server 2008,则可以创建筛选索引(WHERE id<> id),SQL Server将使用(空)筛选索引来满足查询。

答案 6 :(得分:2)

在阅读完这里的答案和编辑后,让我总结一下你的选择:

  1. 更改MS SQL Server以处理此案例(基本上,与Microsoft支持人员交谈)
  2. 更改应用程序以避免这种情况,或者以不同方式进行更改(基本上,请与提供应用程序的公司的支持联系)
  3. 更改为SQL Server以外的其他内容(如果应用程序允许),处理此案例
  4. 更改为其他应用程序
  5. 这些都不是好的解决方案,但不幸的是,它们是你唯一的解决方案。你必须选择一个,然后继续使用它。

    我首先尝试解决方案2,它是/应该花费最短的时间来执行。

    另一方面,如果该公司不愿意更改应用程序,那么我会选择解决方案4.这是一个主要的性能错误,如果公司不愿意或无法解决该问题,则必须问问自己,下一个角落还潜伏着什么?

答案 7 :(得分:2)

我对提交这个答案几乎感到羞耻,但本着“如果没有任何理智可行,请尝试疯狂”的精神......

    Create a constraint on the table where id = id?

where子句只能返回违反约束的行,根据定义,这些行没有。这个额外的(尽管是多余的)信息可能会帮助您的优化器。它同样可以达到什么都没有的平方根,但在你的情况下,我会试一试,以防万一...

答案 8 :(得分:1)

我不知道为什么id!= id在这种情况下需要这么长时间(尽管“显而易见”是主观的 - 它不一定是我想到的优化......它似乎是一个不寻常的查询;笛卡儿尽管如此,加入是痛苦的。但一般情况下,尝试“where 1 = 0” - 或者如果您只是想要架构,可能风险稍高SET FMTONLY ON

编辑:刚刚看到“我们无法控制运行此查询的应用程序每分钟一次”...嗯......这让它变得棘手。

答案 9 :(得分:1)

对SQL Server不太熟悉,我认为下面的解决方案也适用于SQL Server。

在Oracle中我认为你可以通过使用物化视图和查询重写来超越天真的SQL。物化视图不包含任何行,查询重写识别SQL并将查询重新路由到空视图。物化视图永远不需要更新,因为它始终是空的。

答案 10 :(得分:1)

在我看来,你有一个政治问题伪装成技术问题。你可以在技术问题上花费大量的时间和精力,但只要你不断重复“我们不控制应用程序”,你就不会在追求政治选择上。

您无法控制应用,但您可能会安排应用某些影响力。让所有利益相关者了解此应用程序的行为如何影响使用该数据库的所有其他人(使用图表,因为您希望快速将此消息传递给管理人员)。并且要明确这是应用程序的作者或Microsoft修复的问题。这可能会对应用程序的作者造成压力,或者它可能会产生“O.K.,很好。让我们为该应用程序购买另一个数据库”来自管理层。

(对于Microsoft是否已在SQL Server 2008中“修复”此问题,您需要提供答案。)

答案 11 :(得分:1)

我怀疑SqlServer没有“知道”那个!=(应该是“<>”?)是反反身的(即A!= A总是假的)......它只是看到它不是'常量(取决于结果行的值),因此将其放入结果过滤器中。所以“where id<> id”可能与“where 1<> 1”非常不同。

现在,他们当然可以检测到这种情况;但是我想它只是没有进入他们的优先级列表,因为它介于“奇怪”和“愚蠢”之间。

是的,应用程序这样做很糟糕,我想你已经知道了;)

答案 12 :(得分:0)

没有办法让它不扫描,你要求除1行以外的所有东西,即扫描。您可以期待的最好的方法是让应用程序停止发送这些查询。