2009-10-22 33 views
10

Tôi tự hỏi hiệu năng của truy vấn sẽ giống như sử dụng từ khóa LIKE và ký tự đại diện như giá trị so với không có mệnh đề where.Hiệu suất tương tự SQL chỉ với ký tự đại diện (%) là một giá trị

Xem xét mệnh đề where như "WHERE a LIKE '%'". Điều này sẽ khớp với tất cả các giá trị có thể có của cột 'a'. Điều này so sánh với việc không có mệnh đề where.

Lý do tôi hỏi đây là tôi có một ứng dụng có một số trường mà người dùng có thể chỉ định giá trị để tìm kiếm. Trong một số trường hợp, người dùng muốn tất cả các kết quả có thể có. Tôi hiện đang sử dụng một truy vấn như thế này:

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ? 

Các giá trị của '%' và '%' có thể được cung cấp để phù hợp với tất cả các giá trị có thể cho a và b hoặc. Điều này thuận tiện vì tôi có thể sử dụng một truy vấn được đặt tên duy nhất trong ứng dụng của tôi cho việc này. Tôi tự hỏi những gì cân nhắc hiệu suất là cho việc này. Trình tối ưu hóa truy vấn có giảm LIKE '%' để chỉ khớp với tất cả không? Tôi nhận ra rằng bởi vì tôi đang sử dụng một truy vấn được đặt tên (câu lệnh chuẩn bị), điều đó cũng có thể ảnh hưởng đến câu trả lời. Tôi nhận ra câu trả lời có khả năng là cơ sở dữ liệu cụ thể. Vì vậy, cụ thể như thế nào sẽ làm việc này trong Oracle, MS SQL Server và Derby.

Cách tiếp cận thay thế cho điều này sẽ là sử dụng 3 truy vấn riêng biệt dựa trên người dùng nhập ký tự đại diện.

A là ký tự đại diện truy vấn:

SELECT * FROM TableName WHERE b LIKE ? 

B là ký tự đại diện truy vấn:

SELECT * FROM TableName WHERE a LIKE ? 

A và B là ký tự đại diện:

SELECT * FROM TableName 

Không ký tự đại diện:

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ? 

Rõ ràng có một truy vấn đơn giản nhất và dễ bảo trì nhất. Tôi chỉ muốn sử dụng một truy vấn nếu hiệu suất vẫn tốt.

+0

Tôi chỉ có thể đề xuất kiểm tra. Nhưng tôi sẽ cố gắng để dính vào các truy vấn đơn giản. Đừng quá phức tạp, tạo ra hai cái riêng biệt. IMHO. – Nate

+5

Tôi khuyên bạn nên đọc bài viết của Erland về điều kiện tìm kiếm động. Nó khá dài nhưng tôi nghĩ nó sẽ cung cấp cho bạn một cái nhìn rất nghiêm túc và đáng giá về chủ đề này. http://www.sommarskog.se/dyn-search-2005.html –

+1

"Tôi tự hỏi hiệu suất của truy vấn sẽ là gì". Câu trả lời dễ dàng là: Chạy một số thử nghiệm. –

Trả lời

3

Tôi đã hy vọng sẽ có một câu trả lời sách giáo khoa này nhưng nó âm thanh giống như nó sẽ thay đổi phần lớn với các loại cơ sở dữ liệu. Hầu hết các câu trả lời chỉ ra rằng tôi nên chạy một bài kiểm tra để chính xác những gì tôi đã làm.

Ứng dụng của tôi chủ yếu nhắm mục tiêu cơ sở dữ liệu Derby, MS SQL và Oracle. Vì derby có thể được nhúng và dễ cài đặt nên tôi đã thử nghiệm hiệu suất trên đó trước tiên. Kết quả thật đáng ngạc nhiên. Tôi đã thử nghiệm kịch bản trường hợp xấu nhất đối với một bảng khá lớn. Tôi đã chạy thử nghiệm 1000 lần và tính trung bình kết quả.

Query 1:

SELECT * FROM TableName 

Query 2 (Với giá trị của a = "%" và b = "%"):

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ? 

Query 1 thời gian trung bình: 178ms

Truy vấn 2 thời gian trung bình: 181ms

Vì vậy, hiệu suất trên derby gần như giống nhau giữa hai truy vấn.

2

Bất kỳ DBMS nào có giá trị muối của nó sẽ loại bỏ các mệnh đề LIKE '%' trước khi cố gắng chạy truy vấn. Tôi khá chắc chắn tôi đã thấy DB2/z làm điều này trong các kế hoạch thực hiện của nó.

Câu lệnh đã chuẩn bị không nên tạo sự khác biệt vì nó phải được chuyển thành thực SQL trước khi nó được đưa đến công cụ thực thi.

Nhưng, giống như với tất cả các câu hỏi tối ưu hóa, số đo , đừng đoán! Các DBA tồn tại vì chúng liên tục điều chỉnh DBMS dựa trên dữ liệu thực tế (thay đổi theo thời gian). Ở mức tối thiểu, bạn nên thời gian (và nhận được kế hoạch thực hiện) cho tất cả các biến thể với dữ liệu tĩnh phù hợp để xem có sự khác biệt nào không.

Tôi biết rằng các truy vấn như:

select c from t where ((1 = 1) or (c = ?)) 

tối ưu hóa để loại bỏ toàn bộ mệnh đề where trước khi thực hiện (trên DB2 anyway và, trước khi bạn hỏi, các cấu trúc rất hữu ích khi bạn cần phải loại bỏ các hiệu ứng của mệnh đề where nhưng vẫn duy trì trình giữ chỗ tham số (sử dụng BIRT với Javascript để sửa đổi các truy vấn cho các ký tự đại diện)).

+1

Tôi không nghĩ rằng điều này là đúng, đặc biệt nếu cột được so sánh là NULLable. LIKE '%' không nên trả về những hàng mà cột là NULL, vì vậy việc loại bỏ các tiêu chí không nên đột ngột giới thiệu lại chúng với kết quả. –

+0

Các DBMS nên (và nó * là * "nên") có thể cho biết nếu cột không phải là nullable và do đó không tối ưu hóa. Trong mọi trường hợp, điều đó sẽ làm cho câu hỏi OPs bắt đầu, vì họ cũng không thể làm điều đó. – paxdiablo

1

Tùy thuộc vào cách biến vị ngữ LIKE được cấu trúc và trên trường bạn đang thử nghiệm, bạn có thể cần quét toàn bộ bảng. Về mặt ngữ nghĩa, '%' có thể ngụ ý quét toàn bộ bảng nhưng Sql Server thực hiện tất cả các loại tối ưu hóa nội bộ trên các truy vấn. Vì vậy, câu hỏi trở thành: Liệu Sql Server tối ưu hóa trên một vị từ LIKE hình thành với '%' và ném nó ra khỏi mệnh đề WHERE?

12

SQL Server thường sẽ thấy

WHERE City LIKE 'A%' 

và đối xử với nó như

WHERE City >= 'A' AND City < 'B' 

... và vui vẻ sử dụng một chỉ mục tìm kiếm nếu thích hợp. Tôi nói 'nói chung', bởi vì tôi đã thấy nó không làm đơn giản hóa điều này trong một số trường hợp.

Nếu ai đó đang cố gắng để làm:

WHERE City LIKE '%ville' 

... sau đó một chỉ mục tìm kiếm sẽ được về cơ bản là không thể.

Nhưng một cái gì đó đơn giản như:

WHERE City LIKE '%' 

sẽ được xem xét tương đương với:

WHERE City IS NOT NULL 
+2

DB2 (ít nhất) có khái niệm về các chỉ mục đảo ngược, nơi '% ville' dễ dàng được tối ưu hóa (bằng cách lưu trữ các giá trị đảo ngược trong chỉ mục và thay đổi truy vấn thành 'elliv%'). Bạn có thể mô phỏng tương tự trên các DBMS khác với một cột phụ và các trình kích hoạt chèn/cập nhật. – paxdiablo

+0

Chắc chắn rồi, nhưng% ville% trở nên phức tạp hơn. Nếu bạn đang tìm kiếm toàn bộ từ, thì tìm kiếm FullText sẽ trở thành một lựa chọn đẹp hơn. –

+2

+1 để chỉ ra rằng 'LIKE '%'' sẽ chỉ trả về các hàng có giá trị không null. –

4

Bạn có thể sử dụng bất cứ truy vấn phân tích các cung cấp DBMS (ví dụ EXPLAIN cho MySQL, SET SHOWPLAN_ALL ON cho MS SQL (hoặc sử dụng một trong số other methods), EXPLAIN PLAN FOR cho Oracle) để xem truy vấn sẽ được thực hiện như thế nào.

0

Điều gì sẽ xảy ra nếu một cột có giá trị trống không trống? Truy vấn của bạn có thể sẽ phù hợp với nó.

Nếu đây là truy vấn cho ứng dụng thế giới thực, hãy thử sử dụng tính năng lập chỉ mục văn bản miễn phí của hầu hết các cơ sở dữ liệu sql hiện đại. Các vấn đề hiệu suất sẽ trở nên không đáng kể.

Một đơn giản nếu tuyên bố của if (AB) tìm kiếm ab khác (A) tìm kiếm một khác B tìm kiếm b khác người dùng nói họ không nói rõ bất cứ điều gì

là tầm thường để duy trì và trở nên dễ hiểu hơn nhiều so với việc đưa ra các giả định về toán tử LIKE. Bạn có thể sẽ làm điều đó trong giao diện người dùng dù sao khi bạn hiển thị kết quả "Tìm kiếm của bạn cho A tìm thấy x" hoặc "Tìm kiếm của bạn cho AB tìm thấy ..."

0

Tôi không chắc chắn về giá trị của việc sử dụng tuyên bố chuẩn bị với loại tham số bạn mô tả. Lý do là bạn có thể đánh lừa trình tối ưu hóa truy vấn để chuẩn bị kế hoạch thực hiện sẽ hoàn toàn sai tùy thuộc vào tham số nào là '%'. Ví dụ, nếu tuyên bố đã được chuẩn bị với một kế hoạch thực hiện bằng cách sử dụng chỉ mục trên cột A, nhưng tham số cho cột A hóa ra là '%', bạn có thể gặp hiệu suất kém.

-2

mệnh đề where với "like '%'" là vị từ duy nhất sẽ hoạt động chính xác giống như không có mệnh đề where.

+0

Điều này là sai, chỉ các giá trị không NULL sẽ khớp. – Thorsten

+0

cảm ơn bạn đã sửa! – user130770

+0

Tôi có nghĩa là nó sẽ hoạt động như nhau từ quan điểm hiệu suất, nhưng tôi rõ ràng là không đủ rõ ràng. Ngoài ra, điều này cũng không đúng trong một số trường hợp. – user130770

2

Derby cũng cung cấp các công cụ để kiểm tra kế hoạch truy vấn thực tế đã được sử dụng, vì vậy bạn có thể chạy thử nghiệm bằng cách sử dụng Derby và xem xét kế hoạch truy vấn mà Derby đã chọn. Bạn có thể chạy Derby với -Dderby.language.logQueryPlan = true và Derby sẽ viết kế hoạch truy vấn vào derby.log hoặc bạn có thể sử dụng cơ sở RUNTIMESTATISTICS, như được mô tả tại đây: http://db.apache.org/derby/docs/10.5/tuning/ctundepth853133.html

Tôi không chắc chắn nếu Derby sẽ loại bỏ A LIKE '%' trước thời hạn, nhưng tôi cũng không nghĩ rằng sự hiện diện của mệnh đề đó sẽ giới thiệu phần lớn sự chậm lại trong tốc độ thực thi.

Tôi rất muốn xem đầu ra kế hoạch truy vấn thực tế mà bạn nhận được trong môi trường của mình, có và không có mệnh đề A LIKE '%' tại chỗ.

2

Oracle 10gR2 dường như không thực hiện tối ưu hóa đặc biệt cho trường hợp này, nhưng nó nhận ra rằng LIKE '%' không bao gồm giá trị rỗng.

create table like_test (col1) 
as select cast(dbms_random.string('U',10) as varchar2(10)) 
from dual 
connect by level <= 1000 
/
insert into like_test values (null) 
/
commit 
/

exec dbms_stats.gather_table_stats(user,'like_test') 

explain plan for 
select count(*) 
from like_test 
/
select plan_table_output from table(dbms_xplan.display) 
/
explain plan for 
select count(*) 
from like_test 
where col1 like '%' 
/
select plan_table_output from table(dbms_xplan.display) 
/
explain plan for 
select count(*) 
from like_test 
where col1 is not null 
/
select plan_table_output from table(dbms_xplan.display) 
/

... cho ...

Plan hash value: 3733279756 

------------------------------------------------------------------------ 
| Id | Operation   | Name  | Rows | Cost (%CPU)| Time  | 
------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT |   |  1 |  3 (0)| 00:00:01 | 
| 1 | SORT AGGREGATE |   |  1 |   |   | 
| 2 | TABLE ACCESS FULL| LIKE_TEST | 1001 |  3 (0)| 00:00:01 | 
------------------------------------------------------------------------ 

... và ...

Plan hash value: 3733279756 

-------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
-------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   |  1 | 10 |  3 (0)| 00:00:01 | 
| 1 | SORT AGGREGATE |   |  1 | 10 |   |   | 
|* 2 | TABLE ACCESS FULL| LIKE_TEST | 1000 | 10000 |  3 (0)| 00:00:01 | 
-------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("COL1" LIKE '%') 

... và ...

Plan hash value: 3733279756 

-------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
-------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   |  1 | 10 |  3 (0)| 00:00:01 | 
| 1 | SORT AGGREGATE |   |  1 | 10 |   |   | 
|* 2 | TABLE ACCESS FULL| LIKE_TEST | 1000 | 10000 |  3 (0)| 00:00:01 | 
-------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("COL1" IS NOT NULL) 

Note cardinality (hàng) trên dòng TABLE ACCESS FULL

1

Một khía cạnh mà tôi nghĩ là thiếu trong cuộc thảo luận là một thực tế là OP muốn sử dụng một tuyên bố chuẩn bị. Vào thời điểm tuyên bố được chuẩn bị, cơ sở dữ liệu/trình tối ưu hóa sẽ không thể tìm ra các đơn giản hóa mà người khác đã đề cập và vì vậy sẽ không thể tối ưu hóa a like '%' vì giá trị thực tế sẽ không được biết lúc chuẩn bị.

Do đó:

  • khi sử dụng chuẩn bị phát biểu, có bốn báo cáo khác nhau có sẵn (0, chỉ có một, chỉ b, cả hai) và sử dụng thích hợp một khi cần thiết
  • xem nếu bạn có được hiệu suất tốt hơn khi bạn không sử dụng một tuyên bố chuẩn bị khi gắn bó với chỉ một tuyên bố (mặc dù sau đó nó sẽ được khá dễ dàng để không bao gồm các điều kiện 'trống rỗng')
Các vấn đề liên quan