【问题标题】:Why are parameters slower than literal values in a where clause?为什么参数比 where 子句中的文字值慢?
【发布时间】:2009-09-08 06:33:03
【问题描述】:

情况: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()) {...}
}

到底发生了什么?我觉得很奇怪。

【问题讨论】:

  • 尝试重新启动数据库服务器并颠倒您的测试顺序。所有这些的目标是排除 SQL Server 的缓存。
  • 很遗憾,这不是生产服务器的选择。
  • 这可能会有所帮助 - 但可能不会:stackoverflow.com/questions/1211287/…
  • 仍然确保在得出更快的结论之前多次执行每个选项。并且每个选项都有不同的参数值。
  • 您可以查看执行计划或配置文件吗?

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


【解决方案1】:

参数和列的数据类型是否匹配?他们没有出现,所以datatype precedence 适用

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

【讨论】:

  • 使用不匹配的类型是我亲眼目睹并陷入的一个常见错误。是的,如果它是被隐式转换的字段(而不是参数),你可以得到很好的和真正的####ed。了解类型优先级以及索引如何工作(和不工作)将有助于理解这里。
【解决方案2】:

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

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

【讨论】:

  • 对不起,逗号只是我抄写我的代码很糟糕。会修。至于短,值得一试。
  • 设置参数类型确实值得一试。
【解决方案3】:

您可以告诉 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

【讨论】:

  • sql server 真的这么厚需要提示索引吗?
  • @Chris:不:这是因为您的数据类型不匹配。索引提示无济于事。
  • 我从来不需要,但选项存在
【解决方案4】:

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

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

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

使用 SQL 分析器查看在这两种情况下执行的 SQL 是什么。那应该会为您清除它。

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-11
      • 1970-01-01
      • 2015-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多