【问题标题】:SQL produced by Entity Framework for string matching实体框架生成的用于字符串匹配的 SQL
【发布时间】:2009-01-29 12:06:56
【问题描述】:

鉴于此针对 EF 数据上下文的 linq 查询:

var customers = data.Customers.Where(c => c.EmailDomain.StartsWith(term))

您希望它生成这样的 SQL,对吧?

SELECT {cols} FROM Customers WHERE EmailDomain LIKE @term+’%’

嗯,实际上,它做了这样的事情:

SELECT {cols} FROM Customer WHERE ((CAST(CHARINDEX(@term, EmailDomain) AS int)) = 1)

你知道为什么吗?

另外,将 Where 选择器替换为:

c => c.EmailDomain.Substring(0, term.Length) == term

它的运行速度提高了 10 倍,但仍会产生一些非常糟糕的 SQL。

注意:Linq to SQL 正确地将 StartsWith 转换为 Like {term}%,并且 nHibernate 有一个专用的 LikeExpression。

【问题讨论】:

  • 你有没有看到在你漂亮的 linq 或任何好的 c# 源代码之后,令人讨厌的程序集或 MSIL 是如何得到的?问题是如果它给出了正确的结果,我们为什么需要打扰。
  • 感谢关于用 Substring 替换 StartsWith 的提示 - 这解决了 StartsWith 版本不匹配空字符串作为前缀的问题。

标签: sql entity-framework


【解决方案1】:

我不了解 MS SQL 服务器,但在 SQL 服务器上,紧凑型 LIKE 'foo%' 比 CHARINDEX 快数千倍,如果您在 seach 列上有 INDEX。现在我正坐着拉头发,如何用 LIKE 来强迫它。

http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/1b835b94-7259-4284-a2a6-3d5ebda76e4b

【讨论】:

    【解决方案2】:

    原因是 CharIndex 比 LIKE 执行 SQL 更快、更清晰。原因是,您可以有一些疯狂的“LIKE”子句。示例:

    SELECT * FROM Customer WHERE EmailDomain LIKE 'abc%de%sss%'
    

    但是,“CHARINDEX”函数(基本上是“IndexOf”)只处理查找一组字符的第一个实例...不允许使用通配符。

    所以,这就是你的答案:)

    编辑:我只是想补充一点,我鼓励人们在他们的 SQL 查询中使用 CHARINDEX 来处理他们不需要“LIKE”的事情。需要注意的是,在 SQL Server 2000 中...“文本”字段可以使用 LIKE 方法,但不能使用 CHARINDEX。

    【讨论】:

    • 虽然 Like 可能比 CharIndex 慢,但在 StartsWith 的上下文中使用时要快得多(即 Like {term}%)。在这种情况下,查询优化器能够利用现有索引。
    • "原因是 CharIndex 比 LIKE 执行 SQL 更快、更清晰。"这不是一个有效的陈述。人们最常在他们的 LIKE 中只使用一个或两个 % 符号,并且尾随通配符(即 LIKE 'xxx%')允许使用索引搜索,而 CHARINDEX 强制进行表扫描。
    • 原因是,你可以有一些疯狂的“LIKE”子句。不,EF 是一个包装器,它已经知道我正在执行哪种过滤器。为什么要用无法利用索引的劣质 SQL 过滤机制替换 StartsWith
    【解决方案3】:

    LIKE 和 CHARINDEX 之间的性能似乎差不多,所以这不应该是原因。请参阅herehere 进行一些讨论。 CAST 也很奇怪,因为 CHARINDEX 返回一个 int。

    【讨论】:

    • 尾随通配符(即 LIKE 'xxx%')可以使用索引查找。 CHARINDEX 没有,所以这是一个很好的例子,其中 LIKE 的性能可能要好得多(在您的第一个链接中指出)。但我同意 CAST 令人困惑。
    • LIKE 和 CHARINDEX 的性能似乎差不多。对于像LIKE prefix% 这样的简单情况,在存在索引的情况下,CHARINDEX 的性能大大低于 LIKE。
    【解决方案4】:

    charindex 返回第一项在第二项中的位置。

    sql 以 1 作为第一个位置开始(0 = 未找到)

    http://msdn.microsoft.com/en-us/library/ms186323.aspx

    我不知道它为什么使用这种语法,但它就是这样工作的

    【讨论】:

      【解决方案5】:

      我同意它并没有更快,我正在从我们的数据库中检索数以万计的带有字母 i 名称的行。但是我确实发现您需要使用 > 而不是 = ... 所以使用

      {cols} FROM Customer WHERE ((CAST(CHARINDEX(@term, EmailDomain) AS int)) > 0)
      

      而不是

      {cols} FROM Customer WHERE ((CAST(CHARINDEX(@term, EmailDomain) AS int)) = 1)
      

      这是我的两个测试......

      select * from members where surname like '%i%'    --12 seconds
      
      select * from sc4_persons where ((CAST(CHARINDEX('i', surname) AS int)) > 0)  --12 seconds
      
      select * from sc4_persons where ((CAST(CHARINDEX('i', surname) AS int)) = 1)  --too few results
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-30
        • 2020-06-22
        • 1970-01-01
        • 2012-03-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多