2009-08-22 16 views
5

Próbuję pobrać rekordy w MySQL za pomocą prostego użytego pola przesłanego. Dokładniej, użytkownik wprowadza nazwę (imię lub nazwisko lub imię i nazwisko), a serwer powinien zwracać dopasowane wiersze.Używanie "LUB" między klauzul HAVING i WHERE w MySQL?

Co robie tak daleko jest coś takiego:

SELECT * FROM people 
WHERE 
    firstname LIKE '%user_submitted_data%' OR 
    lastname LIKE '%user_submitted_data%' 

To działa dobrze teraz, ale (oczywiście) nie będzie działać, gdy użytkownik przekazuje FULLNAME. Czy istnieje sposób na dodanie OR między całymi "warunkami typu WHERE" i "warunkami typu HAVING"? W ten sposób można zrobić coś takiego:

SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
FROM people 
WHERE 
    firstname LIKE '%user_submitted_data%' OR 
    lastname LIKE '%user_submitted_data%' OR 
    HAVING fullname LIKE '%user_submitted_data%' 

wiem, może po prostu podzielić oryginalny łańcuch, ale to ma jakiś negatywny wpływ, ponieważ masz do czynienia z nazwami zawierającymi przestrzenie, takie jak „De Gaule” i takie tam.

Odpowiedz

4

Czy podkwerenda:

SELECT [some fields] 
FROM 
    SELECT firstname, lastname, CONCAT(firstname, ' ', lastname) as fullname 
    FROM people) AS tmp 
WHERE firstname LIKE '%user_submitted_data%' 
OR lastname LIKE '%user_submitted_data%' 
OR fullname LIKE '%user_submitted_data%' 
+0

Dzięki, to naprawia. – Jimmy

1

Rozważmy kilka możliwych wejść:

John 
Smith 
John Smith 

Początkowej zapytania próbki wynosi:

SELECT * FROM people 
WHERE 
    firstname LIKE '%user_submitted_data%' OR 
    lastname LIKE '%user_submitted_data%' 

Teraz, gdy użytkownik wchodzi do pierwszego wejścia , to zapytanie wybierze wszystkie osoby, których imię zawiera "John"; wybierze także wszystkie osoby, których nazwisko zawiera "John" (na przykład wszystkie Johnsons w bazie danych). Podobnie drugie wejście wybierze wszystkich ludzi, których imię zawiera "Smith"; wybierze także wszystkich ludzi, których nazwisko zawiera "Smith" (na przykład Smithsons and Smithers). Jak na razie dobrze; nie jest idealny ze względu na rozróżnianie wielkości liter (od tego momentu zignoruję wielkość liter, ale prawdopodobnie nie należy tego ignorować), ale wszystko będzie w porządku.

Trzecie wejście wybierze tylko osoby, których imię zawiera "John Smith"; wybierze także tych ludzi, których nazwisko zawiera "John Smith". Jednak jest raczej prawdopodobne, że jest bardzo mało ludzi, którzy spełniają te kryteria - ci ludzie zwani John Smith będą mieli po prostu Johna w imieniu i tylko Smith w imię. To raczej nie jest to, o czym myślałeś.

Nie jest jasne, czy w tabeli znajduje się kolumna o nazwie "fullname". Jeśli to zrobisz, możesz po prostu dopasować do tej kolumny zamiast dopasowywania pojedynczo do imienia i nazwiska. Jeśli tego nie zrobisz, być może uda ci się wytworzyć taką kolumnę, a następnie odpytuj ją.

SELECT * 
    FROM (SELECT firstname || ' ' || lastname AS fullname, ... FROM people) AS t 
WHERE t.fullname LIKE '%user_submitted_data%' 

Działa to całkiem dobrze.

Jeśli jednak martwisz się nazwami takimi jak "Charles De Gaulle" (lub "Charles de Gaulle") lub "Michael van den Berg"), dopasowanie nie powiedzie się, jeśli ktoś wejdzie do "Charlesa Gaulle'a" lub "Charlesa Gaulle'a". Michael Berg ", a co dopiero Michael Vandenberg. Prawdopodobnie będziesz musiał zastąpić dowolne spacje w danych wejściowych użytkownika symbolem "%". Nawet wtedy stajesz wobec problemu, że słowa muszą pojawiać się dokładnie w kolejności podanej przez użytkownika - co może nie mieć znaczenia, ale powinieneś świadomie zadecydować, że to nie ma znaczenia. Na przykład, jeśli dane wejściowe to "Adam John Smith", zapytanie nie będzie miało znaczenia "John Adam Smith"; jeśli wprowadzeniem jest "Smith, John", to nie będzie nikogo odbierać (najprawdopodobniej).

Jeśli chcesz sobie z tym poradzić, prawdopodobnie musisz tokenizować dane wprowadzane przez użytkownika i wyszukiwać osobne słowa.Uważaj na kogoś, kto pyta o podciąg łańcucha słowa (na przykład ktoś pyta o "de" jako słowo imienne) - żadne z zapytań w tym momencie nie gwarantuje, że słowa wprowadzone przez użytkownika pasują do całych słów w wartościach (John vs Johnson), a czyniąc to za pomocą standardowego SQL LIKE operator jest prawie niemożliwy.

+0

Dzięki za głęboki awnser. Z jakiegoś powodu wygląda na to, że MySQL nie rozróżnia wielkości liter w moim systemie (OSX), więc do tej pory wszystko działało dobrze. Jeśli chodzi o szyk wyrazów, operator LIKE bardzo dobrze pasuje do kontekstu. Nie potrzebuję (ani nie chcę) Johh Smitha, aby wyjść, gdy użytkownik szuka Johna Adama Smitha. Próba wyszukiwania jest dość ograniczona. – Jimmy

0

Można odwoływać się do kolumny obliczane w klauzuli WHERE jeśli określić tę kolumnę w podkwerendzie:

SELECT p.* 
FROM (
    SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
    FROM people 
) p 
WHERE 
    p.firstname LIKE '%user_submitted_data%' OR 
    p.lastname LIKE '%user_submitted_data%' OR 
    p.fullname LIKE '%user_submitted_data%'; 

ale szczerze mówiąc, dla danego typu wyszukiwania robisz, LIKE z symboli wieloznacznych jest straszne rozwiązanie . Należy pomyśleć o użyciu indeksu FULLTEXT:

CREATE FULLTEXT INDEX people_names ON people(firstname, lastname); 

SELECT * 
FROM people 
WHERE MATCH(firstname, lastname) AGAINST(?); 

PS: FULLTEXT indeksy działa tylko z silnikiem przechowywania MyISAM. Innym rozwiązaniem, jeszcze szybszym, jest użycie Sphinx Search do indeksowania pełnotekstowego.

0

Mimo że używanie podkwerendy działa dobrze, będzie to miało wpływ, ponieważ nie trafisz na żadne indeksy.

Co powiesz na dodanie kolumny obliczeniowej (firstname || ' ' || lastname) do tabeli i jej indeksu? Na pewno byłoby znacznie szybciej.

Jeśli nie można zrobić, że myślę, że odpytywanie jak

WHERE firstname || ' ' || lastname LIKE '%user_submitted_data%' 

powinien nadal pracować szybciej niż dwóch OR s oraz jeden podzapytania.

+0

Nie sądzę, że istnieje sposób, aby zrobić indeksowaną kolumnę obliczeniową w MySQL, lub tak pokazać moje badania. Jeśli to możliwe, chciałbym o tym usłyszeć. – Jimmy

+0

Może masz rację, myślę, że nie ma możliwości użycia kolumn obliczeniowych w ogóle !? – Sklivvz

+0

Tymczasem MySQL obsługuje indeksowane kolumny obliczeniowe. Jednak warunki LIKE nie mogą używać indeksu, jeśli wzorzec zaczyna się od elementu zastępczego ('%'). –

7

Po prostu umieść wszystkie warunki w klauzuli HAVING.

SELECT [some fields], CONCAT(firstname, ' ', 'lastname') as fullname 
FROM people 
HAVING firstname LIKE '%user_submitted_data%' 
OR  lastname LIKE '%user_submitted_data%' 
OR  fullname LIKE '%user_submitted_data%

Klauzula WHERE mógł odrzucić wiersze wcześnie, ale ponieważ nie można wyrzucić je aż po został oceniony stan na kolumnie komputerowej, i że musi czekać aż HAVING, to kupuje nic do korzystania WHERE .

+0

Wypracowane dla mnie rozwiązanie wydaje się bardziej eleganckim rozwiązaniem niż używanie podzapytań. Dziękuję Ci! –