¿Cómo hacer SQL Like% en Linq?

385

Tengo un procedimiento en SQL que estoy tratando de convertir en Linq:

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

La línea que más me preocupa es:

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

Tengo una columna que almacena la jerarquía como / 1/3/12 / por ejemplo, así que solo uso% / 12 /% para buscarla.

Mi pregunta es, ¿cuál es el equivalente de Linq o .NET para usar el signo de porcentaje?

Matt Dell
fuente
1
Su pregunta tiene al menos 5votos para la etiqueta de operador similar . ¿Podría solicitar amablemente que sugiera sql-like como sinónimo ?
Kermit

Respuestas:

550
.Where(oh => oh.Hierarchy.Contains("/12/"))

También puedes usar .StartsWith()o .EndsWith().

andleer
fuente
44
¿Usar StartsWith () o EndsWith () disparará una consulta? Quiero decir, ¿el código se traducirá en una consulta o los resultados se filtrarán en el objeto después de la recuperación de la base de datos?
Novicio
55
No. StartsWith () y EndsWith () son parte del predicado / filtro. La ejecución continúa siendo diferida.
andleer
2
intentado que obtuvo NullReferenceException: referencia de objeto no establecida en una instancia de un objeto. Entonces no me gusta cuando en mi caso a.Address1.StartsWith (Address1) y a.Address1 es nulo
MikeT
11
StartsWith("abc")se convierte LIKE 'abc%'y EndsWith("abc")se convierte enLIKE '%abc'
Simon_Weaver
20
No se pudo entender por qué esto no funcionaba para un caso de uso con letras, luego me di cuenta de mi estupidez ... no olvides, .ToLower().Contains()etc., si quieres ignorar el caso. Si lo desea, por supuesto, dependerá de si está tratando de imitar LIKE desde una base de datos con intercalación entre mayúsculas y minúsculas o no.
Adam Knights
251

Utilizar este:

from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;
LP
fuente
22
Esto es realmente útil si desea utilizar la coincidencia de patrones más complicada proporcionada por el comando like. Por ejemplo, si desea verificar dos números (en lugar de 12), puede usar esta expresión: SqlMethods.Like (c.Hierarchy, "% / [0-9] [0-9] /%") También , vea esto msdn.microsoft.com/en-us/library/aa933232(SQL.80).aspx
viggity
esto también es muy útil si desea permitir a los usuarios avanzados que ahorren el costoso% inicial ellos mismos, donde usar StartsWith o Contains no le da al usuario
avanzado
8
¿Cómo se usa el SqlMethodsuso de "notación de puntos"?
dan-gph
12
Tenga en cuenta que debe incluir el System.Data.Linq.SqlClientespacio de nombres.
johna
1
No pude encontrar System.Data.Linq.SqlClient aunque puedo agregar System.Data.Linq. ¿Está en desuso?
Burak Karakuş
41

Supongo que está utilizando Linq-to-SQL * (vea la nota a continuación). Si es así, use string.Contains, string.StartsWith y string.EndsWith para generar SQL que use el operador SQL LIKE.

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 }

o

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

Nota: * = si está utilizando ADO.Net Entity Framework (EF / L2E) en .net 3.5, tenga en cuenta que no realizará la misma traducción que Linq-to-SQL. Aunque L2S hace una traducción adecuada, L2E v1 (3.5) se traducirá en una expresión t-sql que forzará un escaneo completo de la tabla que está consultando a menos que haya otro discriminador mejor en su cláusula where o filtros de unión.
Actualización: Esto se corrige en EF / L2E v4 (.net 4.0), por lo que generará un LIKE de SQL como lo hace L2S.

KristoferA
fuente
No es necesario escapar de sus hilos con el @signo, pero me doy cuenta de que esto puede ser una buena convención a seguir.
andleer
27

Si está utilizando VB.NET, la respuesta sería "*". Así es como se vería su cláusula where ...

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

Nota: "*" Coincide con cero o más caracteres. Aquí está el artículo de msdn para el operador Like .

robertz
fuente
¿El operador VB Like se traduce en llamadas L2S? (No tengo idea.)
andleer
8
Sí, el operador VB Like se traduce a la versión SQL de like cuando se usa en una expresión de consulta LINQ. Además, el operador VB Like no está restringido a expresiones de consulta.
robertz
1
Vi que existía fuera de las operaciones de LINQ. Buen material. +1
andleer
9

Bueno indexOf también funciona para mí

var result = from c in SampleList
where c.LongName.IndexOf(SearchQuery) >= 0
select c;
Rumplin
fuente
1
Esta debería ser la respuesta aceptada. IndexOf se traduce a CHARINDEX en sql. Esto posiblemente puede ser más rápido que LIKE. Pero aparte de eso, ofrece la posibilidad de construir consultas de búsqueda como '% some% thing%'. Donde 'algo' tiene que estar ubicado antes de 'cosa', lo que no se puede hacer con Contiene.
Ruard van Elburg
Me encanta cuando las respuestas que necesito tienen 8 años y escondieron varios niveles debajo de la respuesta aceptada. En pocas palabras, esto funcionó mientras que .Contains (@ "/ 12 /") y otras respuestas similares no. ¡Muy apreciado!
IdusOrtus
4

Usa dicho código

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;
}
Ernesto
fuente
4

.NET core ahora tiene EF.Functions.Like

kofifus
fuente
¿Puedes explicar cómo usar esto para resolver el problema del OP?
Robert Columbia
ver, es decir, la respuesta de LP, es solo la versión principal de SqlMethods.Me gusta
kofifus
Esta respuesta debe contener un ejemplo práctico de cómo usar esta función.
FoxDeploy
3

En caso de que no coincida con cadenas numéricas, siempre es bueno tener un caso común:

.Where(oh => oh.Hierarchy.ToUpper().Contains(mySearchString.ToUpper()))
Adelante
fuente
2

Siempre hago esto:

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

Sé que no uso la declaración similar, pero funciona bien en segundo plano, esto se traduce en una consulta con una declaración similar.

H. Pauwelyn
fuente
¿En qué se diferencia su respuesta de la respuesta aceptada (respondida hace 7 años) u otras respuestas? ¿Qué valor agrega?
David Ferenczy Rogožan
1
@DawidFerenczy Esta respuesta funciona con la sintaxis de consulta "from foo in bar", y la aceptada no.
nasch
1

Prueba esto, esto funciona bien para mí

from record in context.Organization where record.Hierarchy.Contains(12) select record;
isuruAb
fuente
1
System.Data.Linq.SqlClient.SqlMethods.Like("mystring", "%string")
Tan Silliksaar
fuente
0

Contiene se usa en Linq, al igual que Like se usa en SQL.

string _search="/12/";

. . .

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

Puede escribir su script SQL en Linq de la siguiente manera:

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

Para aquellos que caen aquí, como yo, buscando un método "SQL Like" en LINQ, tengo algo que funciona muy bien.

Estoy en un caso en el que no puedo alterar la base de datos de ninguna manera para cambiar la clasificación de columnas. Así que tengo que encontrar una manera en mi LINQ para hacerlo.

Estoy usando el método auxiliar SqlFunctions.PatIndex que actúa de manera similar al operador real SQL LIKE.

Primero necesito enumerar todos los diacríticos posibles (una palabra que acabo de aprender) en el valor de búsqueda para obtener algo como:

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

y luego en LINQ por ejemplo:

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();

Entonces, para mis necesidades, he escrito un método de Ayuda / Extensión

   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;
        }
    }

Si alguno de ustedes tiene sugerencias para mejorar este método, estaré encantado de escucharlo.

Hugo
fuente
Su ejemplo es básicamente una colación insensible de acento casera. Una vez tuve que lidiar con un proyecto donde todas y cada una de las consultas pasaban por un filtro para lograr lo que una recopilación adecuada habría hecho automáticamente. Consulte stackoverflow.com/a/2461550/1736944 para conocer cuál es el mejor enfoque. Asigne la clasificación adecuada a la base de datos, tabla y / o campo, según se considere apropiado. (Trabajar sin una recopilación adecuada en el lugar es pura tortura)
9Rune5
0

Muy tarde, pero reuní esto para poder hacer comparaciones de cadenas usando comodines de estilo 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="/programming/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;
    }
}
Steven Ackerman
fuente