2009-08-22 40 views
5

Tôi đang cố gắng tìm nạp bản ghi trong MySQL bằng cách sử dụng trường được gửi đơn giản đã sử dụng. Chính xác hơn, người dùng nhập vào một tên (firstname hoặc lastname hoặc fullname) và máy chủ sẽ trả về các hàng phù hợp.Sử dụng 'OR' giữa mệnh đề HAVING và WHERE trong MySQL?

Những gì tôi đang làm cho đến nay là một cái gì đó như:

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

Đó hoạt động tốt cho bây giờ, nhưng điều đó (rõ ràng) sẽ không hoạt động khi một người dùng gửi các fullname. Có cách nào để thêm một OR giữa toàn bộ 'WHERE type conditions' và 'HAVING type conditions' không? Bằng cách này, tôi có thể làm một cái gì đó như:

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%' 

Tôi biết tôi chỉ có thể chia chuỗi ban đầu nhưng điều đó có một số tác động tiêu cực kể từ khi bạn phải đối phó với những cái tên có chứa không gian như 'De Gaule' và các công cụ như thế.

Trả lời

4

Làm một subquery:

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

Cảm ơn bạn đã khắc phục. – Jimmy

1

Hãy xem xét một số yếu tố đầu vào có thể:

John 
Smith 
John Smith 

truy vấn mẫu ban đầu của bạn là:

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

Bây giờ, khi người dùng nhập vào các đầu vào đầu tiên , truy vấn này sẽ chọn tất cả những người có tên đầu tiên chứa 'John'; nó cũng sẽ chọn tất cả những người có họ chứa 'John' (ví dụ, tất cả các Johnsons trong cơ sở dữ liệu). Tương tự, đầu vào thứ hai sẽ chọn tất cả những người có tên đầu tiên chứa 'Smith'; nó cũng sẽ chọn tất cả những người có họ cuối cùng chứa 'Smith' (ví dụ, Smithsons và Smithers). Càng xa càng tốt; nó không phải là hoàn hảo vì các vấn đề nhạy cảm với trường hợp (tôi sẽ bỏ qua trường hợp nhạy cảm từ đây, nhưng bạn có lẽ không nên bỏ qua nó ở tất cả), nhưng nó sẽ là OK.

Thông tin đầu vào thứ ba sẽ chỉ chọn những người có tên đầu tiên chứa 'John Smith'; nó cũng sẽ chọn những người có tên cuối cùng chứa 'John Smith'. Tuy nhiên, có khả năng là có rất ít người đáp ứng được các tiêu chí đó - những người được gọi là John Smith sẽ chỉ có John trong tên và Smith trong họ. Đây không phải là những gì bạn có trong tâm trí.

Không rõ liệu bạn có một cột được gọi là 'tên đầy đủ' trong bảng hay không. Nếu bạn làm như vậy, bạn có thể đối sánh với cột đó thay vì khớp với tên và họ riêng biệt. Nếu bạn không, có thể bạn có thể sản xuất một cột như vậy và sau đó chạy truy vấn đối với điều đó.

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

Điều này hoạt động khá tốt.

Tuy nhiên, nếu bạn lo lắng về các tên như 'Charles De Gaulle' (hoặc 'Charles de Gaulle') hoặc 'Michael van den Berg'), thì sự trùng khớp sẽ thất bại nếu ai đó nhập 'Charles Gaulle' hoặc ' Michael Berg, hãy để một mình Michael Vandenberg. Có thể bạn sẽ cần phải thay thế bất kỳ ký tự dấu cách nào trong đầu vào của người dùng bằng ký hiệu '%'. Thậm chí sau đó, bạn phải đối mặt với vấn đề rằng các từ phải xuất hiện chính xác trình tự do người dùng đưa ra - điều này có thể không quan trọng, nhưng bạn nên quyết định một cách có ý thức rằng nó không quan trọng. Ví dụ: nếu đầu vào là 'Adam John Smith', thì truy vấn sẽ không bắt 'John Adam Smith'; nếu đầu vào là 'Smith, John', sau đó nó sẽ không nhận bất cứ ai (rất có thể).

Nếu bạn muốn quản lý điều này, bạn có thể cần phải mã hóa đầu vào của người dùng và tìm kiếm trên các từ riêng biệt.Cẩn thận với ai đó hỏi về một chuỗi con của một từ (ví dụ, ai đó hỏi về 'de' như một từ tên) - không có truy vấn nào tại thời điểm này đảm bảo rằng từ nhập của người dùng khớp với toàn bộ các từ trong các giá trị (John vs Johnson), và làm như vậy với toán tử LIKE chuẩn SQL là gần đủ không thể.

+0

Cảm ơn bạn đã biết về chiều sâu. Đối với một số lý do, có vẻ như MySQL là không phân biệt chữ hoa chữ thường trên hệ thống của tôi (OSX), vì vậy nó đã hoạt động tốt cho đến nay. Theo như nó đi với thứ tự từ, các nhà điều hành LIKE phù hợp với bối cảnh rất tốt. Tôi không cần (cũng không muốn) Johh Smith xuất hiện khi người dùng đang tìm kiếm John Adam Smith. Mẫu tìm kiếm khá hạn chế. – Jimmy

0

Bạn có thể tham khảo một cột tính trong mệnh đề WHERE nếu bạn xác định rằng cột trong một subquery:

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%'; 

Nhưng thành thật, cho các loại tìm kiếm bạn đang làm, LIKE với các kí hiệu là một giải pháp khủng khiếp . Bạn nên suy nghĩ về việc sử dụng một chỉ số FULLTEXT:

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

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

PS: FULLTEXT chỉ số chỉ làm việc với các công cụ lưu trữ MyISAM. Một giải pháp khác, thậm chí nhanh hơn, là sử dụng Sphinx Search để lập chỉ mục toàn văn.

0

Mặc dù việc sử dụng truy vấn phụ hoạt động tốt, nó sẽ có tác động vì bạn không đánh bất kỳ chỉ mục nào.

Điều gì về việc thêm cột được tính toán (firstname || ' ' || lastname) vào bảng và chỉ mục cho bảng? Chắc chắn nó sẽ nhanh hơn nhiều.

Nếu bạn không thể làm điều đó tôi nghĩ rằng truy vấn như

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

vẫn nên làm việc nhanh hơn so với hai OR s và một subquery.

+0

Tôi không nghĩ rằng có một cách để làm một cột tính toán được lập chỉ mục trong MySQL, hoặc để hiển thị các nghiên cứu của tôi. Nếu đó là doable, tôi rất muốn nghe về nó. – Jimmy

+0

Bạn có thể đúng, tôi nghĩ không có cách nào để sử dụng các cột được tính toán !? – Sklivvz

+0

Trong khi đó, MySQL hỗ trợ các cột được tính toán được lập chỉ mục. Nhưng các điều kiện LIKE không thể sử dụng một chỉ mục nếu mẫu bắt đầu bằng một trình giữ chỗ ('%'). –

7

Chỉ cần đặt tất cả các điều kiện vào mệnh đề 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%

Mệnh WHERE có thể loại bỏ hàng sớm, nhưng kể từ khi bạn không thể loại bỏ chúng cho đến khi sau bạn đã đánh giá các điều kiện trên cột tính toán, và rằng phải đợi cho đến khi HAVING, nó mua bạn không có gì để sử dụng WHERE .

+0

Làm việc cho tôi, có vẻ như là một giải pháp thanh lịch hơn nhiều so với sử dụng truy vấn phụ. Cảm ơn bạn! –

Các vấn đề liên quan