Serwer SQL ignoruje wielkość liter w wyrażeniu gdzie

88

Jak skonstruować zapytanie SQL (MS SQL Server), w którym w klauzuli „where” nie jest rozróżniana wielkość liter?

SELECT * FROM myTable WHERE myField = 'sOmeVal'

Chcę, aby wyniki wróciły, ignorując sprawę

Raul Agrait
źródło

Odpowiedzi:

136

W domyślnej konfiguracji bazy danych SQL Server w porównaniach ciągów nie jest rozróżniana wielkość liter. Jeśli Twoja baza danych zastępuje to ustawienie (poprzez użycie alternatywnego sortowania), musisz określić, jakiego rodzaju sortowania użyć w zapytaniu.

SELECT * FROM myTable WHERE myField = 'sOmeVal' COLLATE SQL_Latin1_General_CP1_CI_AS

Zwróć uwagę, że zestawienie, które podałem, to tylko przykład (chociaż najprawdopodobniej będzie działać dobrze dla Ciebie). Bardziej szczegółowy opis sortowania SQL Server można znaleźć tutaj .

Adam Robinson
źródło
Aby potwierdzić, wystarczy dodać to tylko raz, na końcu WHEREoświadczenia i wpłynie to na wszystkie WHEREklauzule, prawda?
ashleedawg
Chcesz wiedzieć, czy Twoja odpowiedź ma jakiś problem z wydajnością, konwertując wartość kolumny na wielkość liter UPPERlub wielkość LOWERliter, a następnie używając LIKEdo wyszukiwania?
shaijut
1
@ashleedawg - dobre pytanie ... wygląda na to, że jest to ustawienie dla poszczególnych linii.
Leo Gurdian
29

Zazwyczaj porównania ciągów nie uwzględniają wielkości liter. Jeśli twoja baza danych jest skonfigurowana do sortowania z rozróżnianiem wielkości liter, musisz wymusić użycie tej bez rozróżniania wielkości liter:

SELECT balance FROM people WHERE email = '[email protected]'
  COLLATE SQL_Latin1_General_CP1_CI_AS 
Andrejs Cainikovs
źródło
@AskeB. and Andrejs: To nie jest technicznie problem z konfiguracją bazy danych. Zobacz moją odpowiedź, aby uzyskać wyjaśnienie dotyczące porównań ciągów.
Solomon Rutzky
21

Znalazłem inne rozwiązanie gdzie indziej; to znaczy do użycia

upper(@yourString)

ale wszyscy tutaj mówią, że w SQL Server to nie ma znaczenia, ponieważ i tak ignoruje wielkość liter? Jestem prawie pewien, że w naszej bazie danych rozróżniana jest wielkość liter.

Danny
źródło
7
Masz rację, że w bazie danych można rozróżniać wielkość liter, ale jest to dość nieefektywne, nawet jeśli jest potrzebne. COLLATE jest słowem kluczowym, którego należy użyć.
mjaggard
1
Dzięki za poruszenie tego tematu, @mjaggard. Mam nadzieję, że ty lub ktokolwiek, kto wydaje się być przeciwny mojej odpowiedzi, opracujecie dla dobra każdego takiego jak ja, który szuka i znajduje odpowiedzi takie jak moje.
Danny
1
Głosowałem za tym, ponieważ jest to całkowicie racjonalne wyjaśnienie. Sortuj ślady zbyt dużego narzutu, a co, jeśli twój ciąg zawiera znaki, których sortowanie nie rozumie? Latin 1 to kiepski schemat kodowania. Powodzenia w uzyskiwaniu znaczących wyników, jeśli ciąg znaków zawiera apostrof (na przykład: O'Brien).
Eggmatters
2
Głosowano również za. Przychodzi mi do głowy wiele przypadków, w których byłoby to przydatne. Ponadto często istnieje więcej niż jeden dobry sposób na zrobienie czegoś.
Inversus
1
Zmiana wielkości liter w celu porównania jest ogólnie zła. W niektórych językach konwersje nie odbywają się w obie strony. tj. LOWER (x)! = LOWER (UPPER (x)).
Ceisc
15

Dwie najważniejsze odpowiedzi (od Adama Robinsona i Andrejsa Cainikovsa ) są w pewnym sensie poprawne, ponieważ technicznie działają, ale ich wyjaśnienia są błędne i mogą być w wielu przypadkach mylące. Na przykład, chociaż SQL_Latin1_General_CP1_CI_ASw wielu przypadkach sortowanie zadziała, nie należy zakładać, że jest to właściwe sortowanie bez rozróżniania wielkości liter. W rzeczywistości, biorąc pod uwagę, że OP działa w bazie danych z rozróżnianiem wielkości liter (lub prawdopodobnie binarnych), wiemy, że OP nie używa sortowania, które jest domyślne dla tak wielu instalacji (szczególnie tych zainstalowanych w systemie operacyjnym za pomocą amerykańskiego angielskiego jako języka) SQL_Latin1_General_CP1_CI_AS. Jasne, OP może być używany SQL_Latin1_General_CP1_CS_AS, ale podczas pracy zVARCHARdata, ważne jest, aby nie zmieniać strony kodowej, ponieważ może to prowadzić do utraty danych, a to jest kontrolowane przez ustawienia regionalne / kulturę zestawiania (tj. Latin1_General vs francuski vs hebrajski itp.). Zobacz punkt # 9 poniżej.

Pozostałe cztery odpowiedzi są w różnym stopniu błędne.

Wyjaśnię tutaj wszystkie nieporozumienia, aby czytelnicy mogli, miejmy nadzieję, dokonać najbardziej odpowiednich / skutecznych wyborów.

  1. Nie używaj UPPER(). To zupełnie niepotrzebna dodatkowa praca. Użyj COLLATEklauzuli. W obu przypadkach należy wykonać porównanie ciągów, ale za pomocą należy UPPER()również sprawdzić, znak po znaku, czy występuje odwzorowanie na duże litery, a następnie je zmienić. I musisz to zrobić po obu stronach. Dodawanie COLLATEpo prostu kieruje przetwarzanie do generowania kluczy sortowania przy użyciu innego zestawu reguł niż domyślnie. Używanie COLLATEjest zdecydowanie bardziej wydajne (lub "wydajne", jeśli lubisz to słowo :) niż używanie UPPER(), jak udowodniono w tym skrypcie testowym (na PasteBin) .

    Jest też kwestia odnotowana przez @Ceisc w odpowiedzi @ Danny'ego:

    W niektórych językach konwersje nie odbywają się w obie strony. tj. LOWER (x)! = LOWER (UPPER (x)).

    Typowym przykładem jest turecka wielka litera „İ”.

  2. Nie, sortowanie nie jest ustawieniem obejmującym całą bazę danych, a przynajmniej nie w tym kontekście. Istnieje domyślne sortowanie na poziomie bazy danych i jest używane jako domyślne dla zmienionych i nowo utworzonych kolumn, które nie określają COLLATEklauzuli (co jest prawdopodobne, skąd pochodzi to powszechne nieporozumienie), ale nie wpływa bezpośrednio na zapytania, chyba że porównywanie literałów łańcuchowych i zmiennych z innymi literałami łańcuchowymi i zmiennymi lub odwołujesz się do metadanych na poziomie bazy danych.

  3. Nie, sortowanie nie dotyczy zapytania.

  4. Kolacje dotyczą predykatu (tj. Coś operandowego) lub wyrażenia, a nie zapytania. Dotyczy to całego zapytania, a nie tylko WHEREklauzuli. Obejmuje to POŁĄCZENIA, GRUPOWANIE, ZAMÓWIENIE, PARTYCJA WEDŁUG, itp.

  5. Nie, nie konwertuj na VARBINARY(np. convert(varbinary, myField) = convert(varbinary, 'sOmeVal')) Z następujących powodów:

    1. to jest porównanie binarne, w którym nie jest rozróżniana wielkość liter (o to właśnie chodzi w tym pytaniu)
    2. jeśli potrzebujesz porównania binarnego, użyj sortowania binarnego. Użyj takiego, który kończy się, _BIN2jeśli używasz SQL Server 2008 lub nowszego, w przeciwnym razie nie masz innego wyboru, jak tylko użyć takiego, który kończy się na _BIN. Jeśli dane są, NVARCHARto nie ma znaczenia, którego języka używasz, ponieważ w tym przypadku wszystkie są takie same, dlatego Latin1_General_100_BIN2zawsze działają. Jeśli dane VARCHAR, należy użyć tego samego ustawienia regionalne, że dane są obecnie (np Latin1_General, French, Japanese_XJISitp), ponieważ narodowe określa stronę kodową, która jest używana i zmianę stron kodowych może zmieniać dane (czyli utrata danych).
    3. użycie typu danych o zmiennej długości bez określania rozmiaru będzie zależało od rozmiaru domyślnego, a istnieją dwa różne ustawienia domyślne w zależności od kontekstu, w którym typ danych jest używany. Dla typów ciągów wartość wynosi 1 lub 30. W połączeniu z CONVERT()nim użyje wartości domyślnej 30. Niebezpieczeństwo polega na tym, że jeśli ciąg może mieć ponad 30 bajtów, zostanie po cichu obcięty i prawdopodobnie otrzymasz nieprawidłowe wyniki z tego predykatu.
    4. Nawet jeśli chcesz porównać wielkość liter , w sortowaniu binarnym nie jest rozróżniana wielkość liter (kolejne bardzo częste nieporozumienie).
  6. Nie, LIKEnie zawsze jest rozróżniana wielkość liter. Używa sortowania kolumny, do której się odwołuje, lub sortowania bazy danych, jeśli zmienna jest porównywana z literałem łańcuchowym, lub sortowania określonego za pomocą COLLATEklauzuli opcjonalnej .

  7. LCASEnie jest funkcją programu SQL Server. Wygląda na to, że jest to Oracle lub MySQL. A może Visual Basic?

  8. Ponieważ kontekstem pytania jest porównanie kolumny z literałem łańcuchowym, ani sortowanie instancji (często nazywanej „serwerem”), ani sortowanie bazy danych nie mają tutaj bezpośredniego wpływu. Sortowania są przechowywane w każdej kolumnie, a każda kolumna może mieć inne sortowanie, a te sortowania nie muszą być takie same, jak domyślne sortowanie bazy danych lub sortowanie instancji. Jasne, sortowanie instancji jest domyślne dla tego, co nowo utworzona baza danych będzie używać jako domyślnego sortowania, jeśli COLLATEklauzula nie została określona podczas tworzenia bazy danych. Podobnie, domyślne sortowanie bazy danych jest tym, czego użyje zmieniona lub nowo utworzona kolumna, jeśli COLLATEklauzula nie zostanie określona.

  9. Należy użyć sortowania bez rozróżniania wielkości liter, które poza tym jest takie samo jak sortowanie kolumny. Użyj następującego zapytania, aby znaleźć sortowanie kolumny (zmień nazwę tabeli i nazwę schematu):

    SELECT col.*
    FROM   sys.columns col
    WHERE  col.[object_id] = OBJECT_ID(N'dbo.TableName')
    AND    col.[collation_name] IS NOT NULL;
    

    Następnie wystarczy zmienić _CSsię _CI. Tak Latin1_General_100_CS_ASsię stanie Latin1_General_100_CI_AS.

    Jeśli kolumna używa sortowania binarnego (kończącego się na _BINlub _BIN2), znajdź podobne sortowanie, używając następującego zapytania:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'{CurrentCollationMinus"_BIN"}[_]CI[_]%';
    

    Na przykład zakładając, że kolumna używa Japanese_XJIS_100_BIN2, wykonaj następujące czynności:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'Japanese_XJIS_100[_]CI[_]%';
    

Aby uzyskać więcej informacji na temat zestawień, kodowania itp., Odwiedź: Informacje o zestawieniach

Solomon Rutzky
źródło
7

Nie, tylko używanie LIKEnie zadziała. LIKEwyszukuje wartości pasujące dokładnie do podanego wzorca. W tym przypadku LIKEzostanie znaleziony tylko tekst „sOmeVal”, a nie „someval”.

Praktycznym rozwiązaniem jest użycie LCASE()funkcji. LCASE('sOmeVal')pobiera mały ciąg z twojego tekstu: „someval”. Jeśli użyjesz tej funkcji dla obu stron porównania, zadziała:

SELECT * FROM myTable WHERE LCASE(myField) LIKE LCASE('sOmeVal')

Instrukcja porównuje dwa ciągi napisane małymi literami, dzięki czemu „sOmeVal” będzie pasować do każdej innej notacji „someval” (np. „Someval”, „sOMEVAl” itp.).

David Hermanns
źródło
7
W 99,9% instalacji SQL Server, które są sortowane _CI, LIKE nie rozróżnia wielkości liter.
RichardTheKiwi
1
Obecnie funkcja nazywa się LOWER
David Brossard
@DavidBrossard i David Hermanns, nie sądzę, żeby to było kiedykolwiek LCASE()w SQL Server (przynajmniej tego nie widzę). Myślę, że ta odpowiedź dotyczy zupełnie innego RDBMS. Zobacz moją odpowiedź, aby uzyskać wyjaśnienie dotyczące porównań ciągów.
Solomon Rutzky
4

Możesz wymusić rozróżnianie wielkości liter, rzutując na varbinary w ten sposób:

SELECT * FROM myTable 
WHERE convert(varbinary, myField) = convert(varbinary, 'sOmeVal')

źródło
3
Chociaż jest to funkcjonalne, nie jest to zalecane podejście. Kolacje służą do zarządzania sortowaniem i porównaniami ciągów.
Adam Robinson,
@AdamRobinson nie chodzi jednak o „porównania ciągów”?
Fandango68
@ Fandango68 Tak jest, a Adam mówi, że sortowanie jest lepsze podczas porównywania ciągów.
JLRishe
@ Fandango68 Ta odpowiedź jest błędna na kilku poziomach. Proszę zobaczyć moją odpowiedź, aby poznać szczegóły, zwłaszcza punkt 5.
Solomon Rutzky
@AdamRobinson Proszę zapoznać się z moją odpowiedzią, aby uzyskać wyjaśnienie dotyczące porównań ciągów.
Solomon Rutzky
2

W jakiej bazie danych się znajdujesz? W przypadku MS SQL Server jest to ustawienie dotyczące całej bazy danych lub można je zastąpić słowem kluczowym COLLATE dla każdego zapytania.

Ścigaj Seiberta
źródło
Cześć. W przypadku SQL Server, jeśli chodzi o to, o co chodzi, nie jest to ani ustawienie dotyczące całej bazy danych, ani dla zapytania. Zobacz moją odpowiedź, aby poznać szczegóły.
Solomon Rutzky