2009-09-08 68 views
3

情况:c#,sql 2000为什么参数在where子句中比字面值慢?

我有一张桌子,我们称之为'mytable',有三千万行。 主键是由域A和B的了:

A char(16) 
B smallint(2) 

当我做这样的搜索,它运行非常缓慢(如它做了全表)

string a="a"; 
int b=1; 
string sql = "select * from table(nolock) where [email protected] and [email protected]"; 
using (SqlCommand cmd = new SqlCommand(sql, conn)) 
{ 
    cmd.Parameters.AddWithValue("@a", a); 
    cmd.Parameters.AddWithValue("@b", b); 
    using (SqlDataReader rdr = cmd.ExecuteReader()) {...} 
} 

将其更改为然而,它运行得非常快(例如它触及索引):

string where = 
    String.Format("a='{0}' and b={1}", a, b); 

string sql = "select * from table(nolock) where " + where; 
using (SqlCommand cmd = new SqlCommand(sql, conn)) 
{ 
    using (SqlDataReader rdr = cmd.ExecuteReader()) {...} 
} 

究竟是怎么回事?对我来说似乎很陌生。

+0

尝试重新启动数据库服务器并颠倒测试顺序。所有这些的目标是排除SQL Server部分的缓存。 – 2009-09-08 06:38:16

+0

不幸的是不是生产服务器的选项。 – Chris 2009-09-08 06:39:09

+0

这可能会有所帮助 - 但可能不会:http://stackoverflow.com/questions/1211287/getting-a-query-to-index-seek-rather-than-scan – PaulB 2009-09-08 06:41:02

回答

8

参数和列匹配的数据类型?它们看起来并不如此datatype precedence适用

该列是smallint,但您发送int。该列将被转换为int,因为它具有更高的优先级。所以它不会使用索引。

+0

使用不匹配的类型是我目睹并堕入自己的常见错误。是的,如果它是隐式转换的字段(而不是参数),那么你可以很好地并且真实地得到#### ed。理解类型优先级以及索引如何工作(以及不工作)将有助于理解这里。 – MatBailie 2009-09-08 12:38:08

3

如果您声明b变量为short而不是int,它会产生什么影响吗? 如果您明确指定参数的类型,它是否会有所作为? 如果您使用“where a = @ a和b = @ b”而不是逗号形式,它有什么区别吗?

我同意这听起来很奇怪,我真的不希望这些变化有任何帮助,但它可能值得一试。

+0

对不起,逗号只是我抄写我的代码很糟糕。将解决。 至于短,值得一试。 – Chris 2009-09-08 06:38:06

+0

确实,设置参数类型值得一试。 – 2009-09-08 06:44:12

+0

这实际上会产生巨大的差异... – gbn 2009-09-08 06:47:56

0

您可以告诉SQL Server查询使用哪个索引。使用WITH (INDEX = INDEX_ID)选项,其中INDEX_ID是索引的ID。

获取索引ID与:

​​

所以尽量然后:

SELECT * FROM table(NOLOCK) WITH (INDEX = 1) WHERE [email protected] and [email protected] 
+0

sql服务器真的很厚我需要提示索引? – Chris 2009-09-08 06:47:25

+0

@Chris:不:这是因为你有数据类型不匹配。索引提示将无济于事。 – gbn 2009-09-08 06:49:02

+0

我从来没有,但选择存在 – Scoregraphic 2009-09-08 06:51:03

0

正如@gbn说,设置数据类型应该很容易让你的。

string where = 
    String.Format("a='{0}' and b={1}", a, b); 

在上面的示例中,您告诉SQL将参数a视为char。
而在其他示例中,它将被视为varchar。

使用SQL事件探查器查看在这两种情况下执行的SQL是什么。这应该为你清除它。

0

在第一种情况下,您将向该命令添加SqlParameter类。执行该命令时,很可能会生成错误数据类型的DECLARE语句。 (您可以使用SQL跟踪验证此情况。)如果是这种情况,优化程序将无法选择正确的索引并回退到表扫描。

如果您使用存储的proc代替,则会强制参数进入您声明的数据类型。但是,如果您在参数上指定SqlDbType,仍然可以从代码执行此操作。