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?


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


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

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.


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 .


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;

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

4

Rdzeń .NET ma teraz EF.Functions.Like


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

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ą.


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;


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

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.


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;
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.