为什么参数比where子句中的文字值慢?

时间:2009-09-08 06:33:04

标签: c# sql sql-server tsql sql-server-2000

情况:c#,sql 2000

我有一张桌子,我们称之为'mytable',有3000万行。 主键由字段A和B组成:

A char(16)
B smallint(2)

当我进行这样的搜索时,它的运行速度非常慢(例如,它会进行完整的表扫描)

string a="a";
int b=1;
string sql = "select * from table(nolock) where a=@a and b=@b";
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()) {...}
}

到底是怎么回事?对我来说似乎很奇怪。

5 个答案:

答案 0 :(得分:8)

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

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

答案 1 :(得分:3)

如果您将b变量声明为short而不是int,这会有什么不同吗? 如果明确指定参数的类型,它会有什么不同吗? 如果你使用“where a = @ a和b = @ b”而不是逗号形式会有什么不同吗?

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

答案 2 :(得分:0)

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

获取索引ID:

SELECT i.indid, i.name FROM sysindexes i
INNER JOIN sysobjects o ON o.ID = i.id
WHERE o.Name = 'table'

那么试试吧:

SELECT * FROM table(NOLOCK) WITH (INDEX = 1) WHERE a=@a and b=@b

答案 3 :(得分:0)

正如@gbn所说,设置数据类型应该会让你轻松。

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

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

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

答案 4 :(得分:0)

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

如果您使用存储过程,则会将参数强制转换为您声明的数据类型。但是,如果在参数上指定SqlDbType,则仍可以从代码执行此操作。