【问题标题】:How to do SQL Like % in Linq?如何在 Linq 中执行 SQL Like %?
【发布时间】:2009-05-07 16:43:26
【问题描述】:

我有一个 SQL 过程,我正试图将其转换为 Linq:

SELECT O.Id, O.Name as Organization
FROM Organizations O
JOIN OrganizationsHierarchy OH ON O.Id=OH.OrganizationsId
where OH.Hierarchy like '%/12/%'

我最关心的一行是:

where OH.Hierarchy like '%/12/%'

例如,我有一列存储像 /1/3/12/ 这样的层次结构,所以我只使用 %/12/% 来搜索它。

我的问题是,什么是 Linq 或 .NET 相当于使用百分号?

【问题讨论】:

  • 您的问题至少有5like-operator 标签的投票。能否请您将sql-like 推荐为synonym
  • 不清楚是否寻找 LINQ-to-Objects 等效项。如果是这样,linq-to-entities 标签不相关,甚至令人困惑。

标签: .net linq tsql linq-to-entities sql-like


【解决方案1】:
.Where(oh => oh.Hierarchy.Contains("/12/"))

您也可以使用.StartsWith().EndsWith()

【讨论】:

  • 使用 StartsWith() 或 EndsWith() 会触发查询吗?我的意思是,代码会被翻译成查询,还是在从数据库检索后在对象中过滤结果?
  • 没有。 StartsWith() 和 EndsWith() 是谓词/过滤器的一部分。执行继续被推迟。
  • 试过得到 NullReferenceException: Object reference not set to an instance of an object。所以在我的情况下它不喜欢它 a.Address1.StartsWith(Address1) 和 a.Address1 为空
  • StartsWith("abc") 被转换为LIKE 'abc%'EndsWith("abc") 被转换为LIKE '%abc'
  • 无法弄清楚为什么这不适用于带有字母的用例,然后意识到我的愚蠢......如果你想忽略大小写,不要忘记.ToLower().Contains()等。当然,您是否想要这取决于您是否尝试使用不区分大小写的排序规则从数据库中模仿 LIKE。
【解决方案2】:

使用这个:

from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;

【讨论】:

  • 如果你想使用 like 命令提供的更复杂的模式匹配,这真的很有帮助。例如,如果你想检查任意两个数字(而不是 12),你可以使用这个表达式: SqlMethods.Like(c.Hierarchy, "%/[0-9][0-9]/%") 也,看到这个msdn.microsoft.com/en-us/library/aa933232(SQL.80).aspx
  • 如果您想让高级用户自己预先挂起昂贵的初始 %,这也非常有用,而使用 StartsWith 或 Contains 不会给高级用户这种灵活性
  • 你如何使用“点符号”来使用SqlMethods
  • 请注意,您需要包含System.Data.Linq.SqlClient 命名空间。
  • 我找不到 System.Data.Linq.SqlClient,虽然我可以添加 System.Data.Linq。是否已弃用?
【解决方案3】:

我假设您使用的是 Linq-to-SQL*(请参阅下面的注释)。如果是这样,请使用 string.Contains、string.StartsWith 和 string.EndsWith 生成使用 SQL LIKE 运算符的 SQL。

from o in dc.Organization
join oh in dc.OrganizationsHierarchy on o.Id equals oh.OrganizationsId
where oh.Hierarchy.Contains(@"/12/")
select new { o.Id, o.Name }

from o in dc.Organization
where o.OrganizationsHierarchy.Hierarchy.Contains(@"/12/")
select new { o.Id, o.Name }

注意: * = 如果您在 .net 3.5 中使用 ADO.Net Entity Framework (EF / L2E),请注意它不会进行与 Linq-to-SQL 相同的转换.尽管 L2S 进行了正确的转换,但 L2E v1 (3.5) 将转换为 t-sql 表达式,该表达式将强制对您正在查询的表进行全表扫描,除非您的 where 子句或连接过滤器中有另一个更好的鉴别器。
更新:这在 EF/L2E v4 (.net 4.0) 中已修复,因此它会像 L2S 一样生成 SQL LIKE。

【讨论】:

  • 无需使用@ 符号转义您的字符串,但我意识到这可能只是一个很好的约定。
【解决方案4】:

如果您使用的是 VB.NET,那么答案将是“*”。这是您的 where 子句的样子...

Where OH.Hierarchy Like '*/12/*'

注意:“*”匹配零个或多个字符。 Here is the msdn article for the Like operator.

【讨论】:

  • VB Like 运算符是否转换为 L2S 调用? (我不知道。)
  • 是的,VB Like 运算符在用于 LINQ 查询表达式时会转换为 SQL 版本的 like。此外,VB Like 运算符不限于查询表达式。
  • 我看到它存在于 LINQ 操作之外。好东西。 +1
【解决方案5】:

indexOf 也适合我

var result = from c in SampleList
where c.LongName.IndexOf(SearchQuery) >= 0
select c;

【讨论】:

  • 这应该是公认的答案。 IndexOf 在 sql 中转换为 CHARINDEX。这可能比 LIKE 更快。但除此之外,它还可以构建像“%some%thing%”这样的搜索查询。 'some' 必须位于 'thing' 之前的位置,而 Contains 无法做到这一点。
  • 我喜欢我需要的答案是 8 岁,并且比已接受的答案低几层。简而言之,这有效,而 .Contains(@"/12/") 和其他类似的答案没有。非常感谢!
【解决方案6】:

.NET 核心现在有EF.Functions.Like

  var isMatch = EF.Functions.Like(stringThatMightMatch, pattern);

【讨论】:

  • 你能解释一下如何使用它来解决 OP 的问题吗?
  • 见即LP的回答,它只是SqlMethods.Like的核心版本
  • 这个答案应该包含一个如何使用这个功能的可操作的例子。
【解决方案7】:

使用这样的代码

try
{
    using (DatosDataContext dtc = new DatosDataContext())
    {
        var query = from pe in dtc.Personal_Hgo
                    where SqlMethods.Like(pe.nombre, "%" + txtNombre.Text + "%")
                    select new
                    {
                        pe.numero
                        ,
                        pe.nombre
                    };
        dgvDatos.DataSource = query.ToList();
    }
}
catch (Exception ex)
{
    string mensaje = ex.Message;
}

【讨论】:

    【解决方案8】:

    如果您不匹配数字字符串,最好有常见的情况:

    .Where(oh => oh.Hierarchy.ToUpper().Contains(mySearchString.ToUpper()))
    

    【讨论】:

      【解决方案9】:

      我总是这样做:

      from h in OH
      where h.Hierarchy.Contains("/12/")
      select h
      

      我知道我不使用 like 语句,但它在后台可以正常工作,这是翻译成带有 like 语句的查询。

      【讨论】:

      • 您的答案与接受的答案(7 年前回答)或其他答案有何不同?它增加了什么价值?
      • @DawidFerenczy 此答案适用于“from foo in bar”查询语法,而接受的则不适用。
      【解决方案10】:
      System.Data.Linq.SqlClient.SqlMethods.Like("mystring", "%string")
      

      【讨论】:

        【解决方案11】:

        试试这个,这对我来说很好用

        from record in context.Organization where record.Hierarchy.Contains(12) select record;
        

        【讨论】:

          【解决方案12】:

          晚了,但我把它放在一起,以便能够使用 SQL Like 样式通配符进行字符串比较:

          public static class StringLikeExtensions
          {
              /// <summary>
              /// Tests a string to be Like another string containing SQL Like style wildcards
              /// </summary>
              /// <param name="value">string to be searched</param>
              /// <param name="searchString">the search string containing wildcards</param>
              /// <returns>value.Like(searchString)</returns>
              /// <example>value.Like("a")</example>
              /// <example>value.Like("a%")</example>
              /// <example>value.Like("%b")</example>
              /// <example>value.Like("a%b")</example>
              /// <example>value.Like("a%b%c")</example>
              /// <remarks>base author -- Ruard van Elburg from StackOverflow, modifications by dvn</remarks>
              /// <remarks>converted to a String extension by sja</remarks>
              /// <seealso cref="https://stackoverflow.com/questions/1040380/wildcard-search-for-linq"/>
              public static bool Like(this String value, string searchString)
              {
                  bool result = false;
          
                  var likeParts = searchString.Split(new char[] { '%' });
          
                  for (int i = 0; i < likeParts.Length; i++)
                  {
                      if (likeParts[i] == String.Empty)
                      {
                          continue;   // "a%"
                      }
          
                      if (i == 0)
                      {
                          if (likeParts.Length == 1) // "a"
                          {
                              result = value.Equals(likeParts[i], StringComparison.OrdinalIgnoreCase);
                          }
                          else // "a%" or "a%b"
                          {
                              result = value.StartsWith(likeParts[i], StringComparison.OrdinalIgnoreCase);
                          }
                      }
                      else if (i == likeParts.Length - 1) // "a%b" or "%b"
                      {
                          result &= value.EndsWith(likeParts[i], StringComparison.OrdinalIgnoreCase);
                      }
                      else // "a%b%c"
                      {
                          int current = value.IndexOf(likeParts[i], StringComparison.OrdinalIgnoreCase);
                          int previous = value.IndexOf(likeParts[i - 1], StringComparison.OrdinalIgnoreCase);
                          result &= previous < current;
                      }
                  }
          
                  return result;
              }
          
              /// <summary>
              /// Tests a string containing SQL Like style wildcards to be ReverseLike another string 
              /// </summary>
              /// <param name="value">search string containing wildcards</param>
              /// <param name="compareString">string to be compared</param>
              /// <returns>value.ReverseLike(compareString)</returns>
              /// <example>value.ReverseLike("a")</example>
              /// <example>value.ReverseLike("abc")</example>
              /// <example>value.ReverseLike("ab")</example>
              /// <example>value.ReverseLike("axb")</example>
              /// <example>value.ReverseLike("axbyc")</example>
              /// <remarks>reversed logic of Like String extension</remarks>
              public static bool ReverseLike(this String value, string compareString)
              {
                  bool result = false;
          
                  var likeParts = value.Split(new char[] {'%'});
          
                  for (int i = 0; i < likeParts.Length; i++)
                  {
                      if (likeParts[i] == String.Empty)
                      {
                          continue;   // "a%"
                      }
          
                      if (i == 0)
                      {
                          if (likeParts.Length == 1) // "a"
                          {
                              result = compareString.Equals(likeParts[i], StringComparison.OrdinalIgnoreCase);
                          }
                          else // "a%" or "a%b"
                          {
                              result = compareString.StartsWith(likeParts[i], StringComparison.OrdinalIgnoreCase);
                          }
                      }
                      else if (i == likeParts.Length - 1) // "a%b" or "%b"
                      {
                          result &= compareString.EndsWith(likeParts[i], StringComparison.OrdinalIgnoreCase);
                      }
                      else // "a%b%c"
                      {
                          int current = compareString.IndexOf(likeParts[i], StringComparison.OrdinalIgnoreCase);
                          int previous = compareString.IndexOf(likeParts[i - 1], StringComparison.OrdinalIgnoreCase);
                          result &= previous < current;
                      }
                  }
          
                  return result;
              }
          }
          

          【讨论】:

          • 这太完美了!我需要这个函数才能在带有 Moq 的单元测试中使用,但所有其他答案都需要服务器或根本不匹配。
          【解决方案13】:

          Contains 用在 Linq 中,就像 Like 用在 SQL 中一样。

          string _search="/12/";
          

          。 . .

          .Where(s => s.Hierarchy.Contains(_search))
          

          您可以在 Linq 中编写 SQL 脚本,如下所示:

           var result= Organizations.Join(OrganizationsHierarchy.Where(s=>s.Hierarchy.Contains("/12/")),s=>s.Id,s=>s.OrganizationsId,(org,orgH)=>new {org,orgH});
          

          【讨论】:

            【解决方案14】:

            对于那些像我一样在 LINQ 中寻找“SQL Like”方法的人来说,我有一些工作非常好。

            我无法以任何方式更改数据库来更改列排序规则。 所以我必须在我的 LINQ 中找到一种方法来做到这一点。

            我正在使用辅助方法 SqlFunctions.PatIndex,其作用类似于真正的 SQL LIKE 运算符。

            首先,我需要在搜索值中列举所有可能的变音符号(我刚刚学过的一个词),以获得类似:

            déjà     => d[éèêëeÉÈÊËE]j[aàâäAÀÂÄ]
            montreal => montr[éèêëeÉÈÊËE][aàâäAÀÂÄ]l
            montréal => montr[éèêëeÉÈÊËE][aàâäAÀÂÄ]l
            

            然后以 LINQ 为例:

            var city = "montr[éèêëeÉÈÊËE][aàâäAÀÂÄ]l";
            var data = (from loc in _context.Locations
                                 where SqlFunctions.PatIndex(city, loc.City) > 0
                                 select loc.City).ToList();
            

            所以为了我的需要,我写了一个 Helper/Extension 方法

               public static class SqlServerHelper
                {
            
                    private static readonly List<KeyValuePair<string, string>> Diacritics = new List<KeyValuePair<string, string>>()
                    {
                        new KeyValuePair<string, string>("A", "aàâäAÀÂÄ"),
                        new KeyValuePair<string, string>("E", "éèêëeÉÈÊËE"),
                        new KeyValuePair<string, string>("U", "uûüùUÛÜÙ"),
                        new KeyValuePair<string, string>("C", "cçCÇ"),
                        new KeyValuePair<string, string>("I", "iîïIÎÏ"),
                        new KeyValuePair<string, string>("O", "ôöÔÖ"),
                        new KeyValuePair<string, string>("Y", "YŸÝýyÿ")
                    };
            
                    public static string EnumarateDiacritics(this string stringToDiatritics)
                    {
                        if (string.IsNullOrEmpty(stringToDiatritics.Trim()))
                            return stringToDiatritics;
            
                        var diacriticChecked = string.Empty;
            
                        foreach (var c in stringToDiatritics.ToCharArray())
                        {
                            var diac = Diacritics.FirstOrDefault(o => o.Value.ToCharArray().Contains(c));
                            if (string.IsNullOrEmpty(diac.Key))
                                continue;
            
                            //Prevent from doing same letter/Diacritic more than one time
                            if (diacriticChecked.Contains(diac.Key))
                                continue;
            
                            diacriticChecked += diac.Key;
            
                            stringToDiatritics = stringToDiatritics.Replace(c.ToString(), "[" + diac.Value + "]");
                        }
            
                        stringToDiatritics = "%" + stringToDiatritics + "%";
                        return stringToDiatritics;
                    }
                }
            

            如果你们有任何改进此方法的建议,我会很高兴听到您的意见。

            【讨论】:

            • 您的示例基本上是自制的不区分口音的排序规则。我曾经不得不处理一个项目,其中每个查询都通过一个过滤器来实现正确的排序规则自动完成的工作。请参阅stackoverflow.com/a/2461550/1736944 了解通常更好的方法。根据需要为数据库、表和/或字段分配适当的排序规则。 (没有适当的整理工作是纯粹的折磨)
            【解决方案15】:

            如果您需要像单元测试这样的客户端操作的 LIKE 功能,CodeProject 的这种方法可以很好地模仿通配符的行为。

            有点像@Steve Ackerman 的回答,但更全面。

            /// Published on CodeProject: http://www.codeproject.com/Articles/
            ///         608266/A-Csharp-LIKE-implementation-that-mimics-SQL-LIKE
            /// </remarks>
            public static bool Like(this string s, string match, bool CaseInsensitive = true)
            {
                //Nothing matches a null mask or null input string
                if (match == null || s == null)
                    return false;
                //Null strings are treated as empty and get checked against the mask.
                //If checking is case-insensitive we convert to uppercase to facilitate this.
                if (CaseInsensitive)
                {
                    s = s.ToUpperInvariant();
                    match = match.ToUpperInvariant();
                }
                //Keeps track of our position in the primary string - s.
                int j = 0;
                //Used to keep track of multi-character wildcards.
                bool matchanymulti = false;
                //Used to keep track of multiple possibility character masks.
                string multicharmask = null;
                bool inversemulticharmask = false;
                for (int i = 0; i < match.Length; i++)
                {
                    //If this is the last character of the mask and its a % or * we are done
                    if (i == match.Length - 1 && (match[i] == '%' || match[i] == '*'))
                        return true;
                    //A direct character match allows us to proceed.
                    var charcheck = true;
                    //Backslash acts as an escape character.  If we encounter it, proceed
                    //to the next character.
                    if (match[i] == '\\')
                    {
                        i++;
                        if (i == match.Length)
                            i--;
                    }
                    else
                    {
                        //If this is a wildcard mask we flag it and proceed with the next character
                        //in the mask.
                        if (match[i] == '%' || match[i] == '*')
                        {
                            matchanymulti = true;
                            continue;
                        }
                        //If this is a single character wildcard advance one character.
                        if (match[i] == '_')
                        {
                            //If there is no character to advance we did not find a match.
                            if (j == s.Length)
                                return false;
                            j++;
                            continue;
                        }
                        if (match[i] == '[')
                        {
                            var endbracketidx = match.IndexOf(']', i);
                            //Get the characters to check for.
                            multicharmask = match.Substring(i + 1, endbracketidx - i - 1);
                            //Check for inversed masks
                            inversemulticharmask = multicharmask.StartsWith("^");
                            //Remove the inversed mask character
                            if (inversemulticharmask)
                                multicharmask = multicharmask.Remove(0, 1);
                            //Unescape \^ to ^
                            multicharmask = multicharmask.Replace("\\^", "^");
                            
                            //Prevent direct character checking of the next mask character
                            //and advance to the next mask character.
                            charcheck = false;
                            i = endbracketidx;
                            //Detect and expand character ranges
                            if (multicharmask.Length == 3 && multicharmask[1] == '-')
                            {
                                var newmask = "";
                                var first = multicharmask[0];
                                var last = multicharmask[2];
                                if (last < first)
                                {
                                    first = last;
                                    last = multicharmask[0];
                                }
                                var c = first;
                                while (c <= last)
                                {
                                    newmask += c;
                                    c++;
                                }
                                multicharmask = newmask;
                            }
                            //If the mask is invalid we cannot find a mask for it.
                            if (endbracketidx == -1)
                                return false;
                        }
                    }
                    //Keep track of match finding for this character of the mask.
                    var matched = false;
                    while (j < s.Length)
                    {
                        //This character matches, move on.
                        if (charcheck && s[j] == match[i])
                        {
                            j++;
                            matched = true;
                            break;
                        }
                        //If we need to check for multiple charaters to do.
                        if (multicharmask != null)
                        {
                            var ismatch = multicharmask.Contains(s[j]);
                            //If this was an inverted mask and we match fail the check for this string.
                            //If this was not an inverted mask check and we did not match fail for this string.
                            if (inversemulticharmask && ismatch ||
                                !inversemulticharmask && !ismatch)
                            {
                                //If we have a wildcard preceding us we ignore this failure
                                //and continue checking.
                                if (matchanymulti)
                                {
                                    j++;
                                    continue;
                                }
                                return false;
                            }
                            j++;
                            matched = true;
                            //Consumse our mask.
                            multicharmask = null;
                            break;
                        }
                        //We are in an multiple any-character mask, proceed to the next character.
                        if (matchanymulti)
                        {
                            j++;
                            continue;
                        }
                        break;
                    }
                    //We've found a match - proceed.
                    if (matched)
                    {
                        matchanymulti = false;
                        continue;
                    }
            
                    //If no match our mask fails
                    return false;
                }
                //Some characters are left - our mask check fails.
                if (j < s.Length)
                    return false;
                //We've processed everything - this is a match.
                return true;
            } 
            

            【讨论】:

              猜你喜欢
              • 2018-04-07
              • 1970-01-01
              • 1970-01-01
              • 2014-04-25
              相关资源
              最近更新 更多