2010-03-12 34 views
6

Ok đây là vấn đề của tôi:thông số tìm kiếm tùy chọn trong truy vấn sql và các hàng với null giá trị

Trước khi tôi bắt đầu mô tả, hãy để tôi nói với bạn rằng tôi đã googled lên rất nhiều và tôi đăng câu hỏi này trong một giải pháp tối ưu tốt :)

tôi đang xây dựng một dịch vụ còn lại trên WCF để có được userProfiles ... người dùng có thể lọc userProfiles bằng cách đưa ra một cái gì đó giống như userProfiles vị trí? = London

bây giờ tôi có phương pháp sau

GetUserProfiles(string firstname, string lastname, string age, string location) 

chuỗi truy vấn sql tôi đã tạo là: select firstname, lastname, .... from profiles where (firstName like '%{firstname}%') AND (lastName like '%{lastName}%') .... v.v ... với tất cả các biến được thay thế bằng trình định dạng chuỗi.

Vấn đề với điều này là nó lọc bất kỳ hàng có firstname, lastname, tuổi tác hay vị trí có một giá trị null ....

làm một cái gì đó giống như (firstName like '%{firstName}%' OR firstName IS NULL) sẽ tẻ nhạt và tuyên bố sẽ trở thành unmaintanable! (trong ví dụ này chỉ có 4 đối số, nhưng theo phương pháp thực tế của tôi có 10)

Giải pháp nào tốt nhất cho điều này? .... Tình huống này thường được xử lý như thế nào?

Cơ sở dữ liệu được sử dụng: MySql

+0

Bạn đã cân nhắc sử dụng LINQ-to-SQL chưa? –

+0

không có tôi chưa được coi là linq để sql ... –

Trả lời

1

Bạn có thể sử dụng COALESCE(value,...)

coalesce(firstName, '') like '%{firstname}%' 
+1

'COALESCE()' về cơ bản là tra cứu O (n) vì không có chỉ mục nào được sử dụng. – cletus

2

Đó là một đặc tính cơ bản của NULL rằng khi bạn so sánh nó với bất cứ điều gì — bao gồm NULL — nó trả false, đó là lý do tại sao những gì bạn đang làm không hoạt động.

Vì vậy, câu hỏi đầu tiên cần trả lời là: tại sao bạn muốn một hàng có số NULLlastname được trả lại khi họ nhập số lastname của "Smith"? Có thể bạn có nghĩa là hoặc tên đầu tiên hoặc họ cần khớp để được trả về, trong trường hợp đó bạn không chạy truy vấn chính xác. Các giải pháp ngây thơ nhất là:

SELECT firstname, lastname, .... 
FROM profiles 
WHERE IFNULL(firstName LIKE'%{firstname}%' 
OR lastName LIKE '%{lastName}%' 

Bây giờ điều này sẽ làm việc cho vài trăm và có thể vài ngàn hàng nhưng sẽ không mở rộng vượt ra ngoài đó vì nhiều lý do:

  1. OR s thường không may về hiệu suất. Tránh chúng nếu có thể. Nếu bạn nhìn vào các ứng dụng cơ sở dữ liệu được viết bởi các lập trình viên có kinh nghiệm, bạn có thể sẽ không tìm thấy điều kiện OR duy nhất (trừ những người đã cải thiện đức tính của OR ở mặt sau của việc viết một ứng dụng lưu bút có 3 người dùng và 2 lần truy cập mỗi tháng) ;
  2. Thực hiện LIKE với số % ở mặt trước sẽ có nghĩa là không có chỉ mục nào bị kiện.

Có một số giải pháp cho (2). Có lẽ cách dễ nhất trong MySQL là sử dụng full text searching trên các bảng MyISAM. Nó sẽ không tìm thấy các kết quả phù hợp như "Johannes" nếu bạn nhập "han" nhưng nói chung là đủ.

Thường thì bạn xử lý các điều kiện OR bằng cách sử dụng UNION hoặc (tốt hơn) UNION ALL trên nhiều truy vấn. Ví dụ:

SELECT firstname, lastname, .... 
FROM profiles 
WHERE firstName LIKE'%{firstname}%' 
AND lastName LIKE '%{lastName}%' 
UNION ALL 
SELECT firstname, lastname, .... 
FROM profiles 
WHERE firstName LIKE'%{firstname}%' 
AND lastname IS NULL 
UNION ALL 
SELECT firstname, lastname, .... 
FROM profiles 
WHERE firstName IS NULL 
AND lastName LIKE '%{lastName}%' 
UNION ALL 
SELECT firstname, lastname, .... 
FROM profiles 
WHERE firstName IS NULL 
AND lastName IS NULL 

Trông lớn và xấu xí nhưng UNION ALL quy mô rất tốt (bỏ qua các % vào lúc bắt đầu của LIKE tiêu chí). Đó là cơ bản nối (trong trường hợp này) bốn truy vấn. A UNION sẽ làm một ẩn số DISTINCT trên hàng kết quả nhưng chúng tôi biết sẽ không có sự chồng chéo ở đây vì chúng tôi đang thay đổi kiểm tra cho NULL.

Một khả năng khác là không xử lý NULL làm thứ bạn muốn tìm kiếm. Đây là một giải pháp tốt hơn và trực quan hơn (IMHO). Nếu ai đó nhập họ của "Smith", bạn có thực sự muốn các hàng có tên hiển thị là NULL không?

Hoặc bạn có muốn chúng hiển thị vì bạn có thể đã có kết quả trùng khớp với tên không? Nếu vậy, bạn muốn có một truy vấn hơi khác. Ví dụ:

SELECT firstname, lastname, .... 
FROM profiles 
WHERE id IN (
    SELECT id 
    FROM profiles 
    WHERE firstName LIKE'%{firstname}%' 
    UNION ALL 
    SELECT id 
    FROM profiles 
    WHERE lastName LIKE '%{lastName}%' 
) 
+0

hiệu suất với rất nhiều công đoàn ?? –

+0

@glenn: như tôi đã nói, bốn biến thể union sẽ hầu hết là vô nghĩa ** trong trường hợp này ** bởi vì tiêu chí sẽ không sử dụng chỉ mục, nhưng nếu bạn đang sử dụng chỉ mục thì đó là một câu chuyện khác. Vấn đề lớn hơn là liệu bạn có đang chạy đúng truy vấn và liệu mô hình dữ liệu của bạn có tối ưu hóa hay không. – cletus

0

Bạn có thể sử dụng COALESCE:

select firstname, lastname, .... 
from profiles 
where (coalesce(firstName, '') like '%{firstname}%') 
AND (coalesce(lastName, '') like '%{lastName}%') 
+1

'COALESCE()' về cơ bản là tra cứu O (n) vì không có chỉ mục nào được sử dụng. – cletus

+1

Vâng, rõ ràng, nhưng bạn không thể sử dụng các chỉ mục vì 'LIKE '% foo%'' do đó, nó có vẻ là một điểm tranh luận ở đây. Chỉ mục có thể được sử dụng với 'LIKE 'foo%' nhưng không phải nếu có ký tự đại diện lúc bắt đầu. Nếu hiệu suất là một vấn đề ở đây, một chỉ mục văn bản đầy đủ nên được sử dụng. –

0

Bạn luôn có thể không cho phép giá trị null ở nơi đầu tiên - sau khi tất cả, tất cả mọi người phải sống cho một số tích cực của năm và được một nơi nào đó về mặt địa lý.

Nếu bạn hoàn toàn phải cho phép null, thì hãy sử dụng COALESCE, như những người khác đã đề xuất hoặc có thể là IFNULL là MySQL cụ thể và có thể tối ưu hơn một chút vì phải mất chính xác 2 thông số.

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