Jak zrobić SQL jak% w Linq?

385

Mam procedurę w języku SQL, którą próbuję przekształcić w Linq:

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

Linia, którą najbardziej mnie interesuje to:

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

Mam kolumnę, która przechowuje hierarchię, na przykład / 1/3/12 /, więc po prostu używam% / 12 /% do jej wyszukiwania.

Moje pytanie brzmi: jaki jest Linq lub .NET odpowiednik używania znaku procentu?

Matt Dell
źródło
1
Twoje pytanie ma co najmniej 5głosy na tag operatora podobnego . Czy mogę prosić o zasugerowanie sql-like jako synonimu ?
Kermit

Odpowiedzi:

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

Możesz także użyć .StartsWith()lub .EndsWith().

Andleer
źródło
4
Czy użycie StartsWith () lub EndsWith () spowoduje uruchomienie zapytania? Mam na myśli, czy kod zostanie przetłumaczony na zapytanie, czy wyniki zostaną odfiltrowane w obiekcie po pobraniu z bazy danych?
Nowicjusz
5
Nie. StartsWith () i EndsWith () są częścią predykatu / filtra. Wykonanie jest nadal odraczane.
andleer
2
próbował, który dostał NullReferenceException: Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu. Więc to mi się nie podoba, gdy w moim przypadku a.Address1.StartsWith (Address1) i a.Address1 ma wartość zerową
MikeT
11
StartsWith("abc")zostaje przekonwertowany na LIKE 'abc%'i EndsWith("abc")zostaje przeniesiony doLIKE '%abc'
Simon_Weaver,
20
Nie mogłem zrozumieć, dlaczego to nie działa w przypadku użycia z literami, a potem zdałem sobie sprawę z mojej głupoty ... nie zapomnij .ToLower().Contains()itp., Jeśli chcesz zignorować wielkość liter. To, czy chcesz, będzie oczywiście zależeć od tego, czy próbujesz naśladować LIKE z bazy danych z sortowaniem bez rozróżniania wielkości liter, czy nie.
Adam Knights
251

Użyj tego:

from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;
LP
źródło
22
jest to naprawdę pomocne, jeśli chcesz użyć bardziej skomplikowanego dopasowywania wzorców zapewnianego przez polecenie like. Na przykład, jeśli chcesz sprawdzić dowolne dwie liczby (zamiast 12), możesz użyć tego wyrażenia: SqlMethods.Like (c.Hierarchy, "% / [0-9] [0-9] /%") Również , zobacz to msdn.microsoft.com/en-us/library/aa933232(SQL.80).aspx
viggity
jest to również bardzo przydatne, jeśli chcesz pozwolić zaawansowanym użytkownikom samodzielnie sfinalizować kosztowne początkowe%, gdzie użycie StartsWith lub Contains nie daje zaawansowanemu użytkownikowi takiej elastyczności
Simon_Weaver
8
Jak korzystać SqlMethodsz „notacji kropkowej”?
dan-gph
12
Pamiętaj, że musisz dołączyć System.Data.Linq.SqlClientprzestrzeń nazw.
johna
1
Nie mogłem znaleźć System.Data.Linq.SqlClient, chociaż mogę dodać System.Data.Linq. Czy to jest przestarzałe?
Burak Karakuş,
41

Zakładam, że używasz Linq-to-SQL * (patrz uwaga poniżej). Jeśli tak, użyj string.Contains, string.StartsWith i string.EndsWith, aby wygenerować SQL korzystający z operatora 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 }

lub

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

Uwaga: * = jeśli korzystasz z ADO.Net Entity Framework (EF / L2E) w .net 3.5, pamiętaj, że nie wykona tego samego tłumaczenia co Linq-na-SQL. Chociaż L2S wykonuje prawidłowe tłumaczenie, L2E v1 (3.5) przekształci się w wyrażenie t-sql, które wymusi pełne skanowanie tabeli w zapytanej tabeli, chyba że istnieje inny lepszy dyskryminator w klauzuli where lub filtrach łączenia.
Aktualizacja: Zostało to naprawione w EF / L2E v4 (.net 4.0), więc wygeneruje SQL LIKE, podobnie jak L2S.

KristoferA
źródło
Nie musisz uciekać przed sznurkami ze @znakiem, ale zdaję sobie sprawę, że może to być dobra konwencja do naśladowania.
andleer
27

Jeśli używasz VB.NET, odpowiedzią byłoby „*”. Oto, jak wyglądałaby Twoja klauzula where ...

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

Uwaga: „*” Dopasowuje zero lub więcej znaków. Oto artykuł msdn dla operatora Like .

Robert
źródło
Czy operator VB Like przekłada się na wywołania L2S? (Nie mam pojęcia.)
andleer
8
Tak, operator VB Like zostaje przetłumaczony na wersję SQL podobną, gdy jest używany w wyrażeniu zapytania LINQ. Ponadto operator Like VB nie jest ograniczony do wyrażeń zapytań.
robertz
1
Widziałem, że istniał poza operacjami LINQ. Dobry towar. +1
andleer
9

Cóż, indexOf też działa dla mnie

var result = from c in SampleList
where c.LongName.IndexOf(SearchQuery) >= 0
select c;
Rumplin
źródło
1
To powinna być zaakceptowana odpowiedź. IndexOf tłumaczy na CHARINDEX w sql. Może to być szybsze niż LIKE. Ale oprócz tego daje możliwość konstruowania zapytań takich jak „% some% thing%”. Gdzie „niektóre” muszą znajdować się przed „rzeczą”, czego nie można zrobić z Contains.
Ruard van Elburg
Uwielbiam to, gdy odpowiedzi, których potrzebuję, mają 8 lat i schowałem kilka poziomów poniżej przyjętej odpowiedzi. Mówiąc prościej, to zadziałało, podczas gdy .Contains (@ "/ 12 /") i inne podobne odpowiedzi nie. Bardzo mile widziane!
IdusOrtus
4

Użyj takiego kodu

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
źródło
4

Rdzeń .NET ma teraz EF.Functions.Like

Kofifus
źródło
Czy możesz wyjaśnić, jak to wykorzystać, aby rozwiązać problem PO?
Robert Columbia,
patrz np. odpowiedź z LP, to tylko podstawowa wersja SqlMethods.Like
kofifus
Ta odpowiedź powinna zawierać praktyczny przykład użycia tej funkcji.
FoxDeploy
3

Jeśli nie pasujesz ciągów liczbowych, zawsze dobrze jest mieć wspólny przypadek:

.Where(oh => oh.Hierarchy.ToUpper().Contains(mySearchString.ToUpper()))
Wejdź
źródło
2

Robię to zawsze:

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

Wiem, że nie używam podobnej instrukcji, ale działa dobrze w tle, to jest przetłumaczone na zapytanie z podobną instrukcją.

H. Pauwelyn
źródło
Czym różni się twoja odpowiedź od odpowiedzi zaakceptowanej (udzielonej 7 lat temu) lub innych odpowiedzi? Jaką wartość to dodaje?
David Ferenczy Rogožan
1
@DawidFerenczy Ta odpowiedź działa ze składnią zapytania „from foo in bar”, a zaakceptowana nie.
nasch
1

Spróbuj tego, to działa dobrze dla mnie

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

Zawiera jest używany w Linq, podobnie jak Like jest używany w SQL.

string _search="/12/";

. . .

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

Możesz napisać skrypt SQL w Linq w następujący sposób:

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

Dla tych, którzy lubią tutaj szukać sposobu na metodę „SQL Like” w LINQ, mam coś, co działa bardzo dobrze.

Jestem w przypadku, gdy nie mogę w żaden sposób zmienić bazy danych, aby zmienić układ kolumn. Więc muszę znaleźć sposób w moim LINQ, aby to zrobić.

Używam metody pomocnika SqlFunctions.PatIndex która działa podobnie do prawdziwego operatora SQL LIKE.

Najpierw muszę wymienić wszystkie możliwe znaki diakrytyczne (słowo, które właśnie się nauczyłem) w wartości wyszukiwania, aby uzyskać coś takiego:

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

a następnie w LINQ na przykład:

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

Więc na moje potrzeby napisałem metodę pomocnika / rozszerzenia

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

Jeśli ktoś z was ma sugestię, aby ulepszyć tę metodę, chętnie Cię wysłucham.

Hugo
źródło
Twój przykład to w zasadzie zestawienie niewrażliwe na akcenty domowej roboty. Kiedyś miałem do czynienia z projektem, w którym każde zapytanie przechodziło przez filtr, aby osiągnąć to, co zrobiłoby właściwe sortowanie automatycznie. Zobacz stackoverflow.com/a/2461550/1736944, aby dowiedzieć się, jakie jest zwykle lepsze podejście. Przypisz odpowiednie zestawienie do bazy danych, tabeli i / lub pola, jeśli uzna to za stosowne. (Praca bez odpowiedniego zestawienia w miejscu to czysta tortura)
9Rune5
0

Znacznie późno, ale rzuciłem to razem, aby móc porównywać ciągi znaków przy użyciu symboli wieloznacznych w stylu SQL:

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
źródło