2009-10-14 47 views
9

Tôi có một bảng có 117000 bản ghi. Tôi cần thực hiện tìm kiếm để kiểm tra 3 trường riêng biệt cho một mẫu chuỗi nhất định.Làm cách nào để tối ưu hóa/tái cấu trúc mệnh đề TSQL "LIKE"?

My mệnh đề where được như sau:

field1 LIKE '%' + @DESC + '%' 
OR field2 LIKE '%' + @DESC + '%' 
OR field3 LIKE '%' + @DESC + '%' 

Điều này dường như mất khoảng 24 giây bất kể đầu vào ...

Có cách nào tốt hơn để làm điều này? Ít hơn 10 (hoặc 5!) Giây sẽ thích hợp hơn nhiều.

Cảm ơn bạn đã được trợ giúp.

Trả lời

14

Sử dụng tìm kiếm toàn văn bản và CONTAINS. Không thể tối ưu hóa LIKE khi tìm kiếm ở giữa trường, ví dụ: khi biểu thức LIKE bắt đầu bằng '%', do đó, nó sẽ luôn quét toàn bộ bảng.

+0

bạn có thể sử dụng một chỉ mục, với một công việc nhỏ, khi làm LIKE '%' + chuỗi, xem liên kết trong bình luận khác của tôi. –

+0

@KM: Thủ thuật thú vị, cột đảo ngược. Cũng tương tự tốt với cuốn sách điện thoại, làm cho các trường hợp rõ ràng rõ ràng. –

+0

mẹo nhỏ, để che giấu không có bất cứ điều gì tương tự cho vấn đề '% text%' như thế. – HLGEM

1

Bất cứ khi nào bạn bắt đầu tìm kiếm LIKE bằng ký tự đại diện, bạn đang thực hiện quét. Trừ khi bạn có thể thu hẹp tiêu chí tìm kiếm của mình để bao gồm ký tự đầu tiên (có thể không khả thi), bạn sẽ cần phải sử dụng Tìm kiếm văn bản đầy đủ.

+3

@Stuart Ainsworth đã nói _Bất cứ lúc nào bạn bắt đầu tìm kiếm LIKE với ký tự đại diện, bạn đang thực hiện scan_, điều đó không cần phải đúng, hãy xem: http://stackoverflow.com/questions/1388059/sql-server-index -columns-used-in-like/1395881 # 1395881 –

+0

Bây giờ bạn chỉ đang chơi với ngữ nghĩa; bạn đảo ngược cột và lập chỉ mục nó, và sau đó đảo ngược mệnh đề tương tự để nó không bắt đầu bằng một ký tự đại diện. Bạn không bắt đầu tìm kiếm bằng ký tự đại diện, do đó bạn không thực hiện quét. Tôi sẽ thừa nhận đó là một giải pháp thú vị và tôi sẽ phải ghi nhớ điều đó. –

0

thế nào về

field1 + field2 + field3 LIKE '%' + @DESC + '%' 

hoặc

CONTAINS(field1 + field2 + field3, @DESC) 
1

Bạn có thực sự cần phải bắt đầu với một ký tự đại diện? Tại sao? Thông thường, bạn có thể buộc người dùng nhập ít nhất ký tự đầu tiên. Tôi mang lại điều này bởi vì một số nhà phát triển chỉ sử dụng ký tự đại diện như một thói quen không phải vì có một yêu cầu. Trong hầu hết các trường hợp, người dùng sẽ có thể nhập ký tự đầu tiên trừ khi lưu trữ các chuỗi dài (như nói tên sân bay chính thức). Nếu không, bạn thực sự cần phải sử dụng chỉ mục toàn văn mặc dù thủ thuật của KM ngược lại là khá thú vị nếu bạn không cần ký tự đại diện ở cuối.

Nếu bạn có thể tránh làm những việc giết chết hiệu suất, hãy làm như vậy.

+0

Có, tôi cần một ký tự đại diện hai mặt cho truy vấn này. Tôi cần tìm thứ gì đó như 'chuối' trong 'Strawberry Banana Yogurt'. – IronicMuffin

1

Trong khi tôi đồng ý với câu trả lời được chấp nhận, việc lập chỉ mục văn bản đầy đủ sẽ là giải pháp tốt nhất và không có nghĩa là ủng hộ việc sử dụng tìm kiếm ký tự đại diện hàng đầu nếu chúng được thực hiện thì các bước tiềm năng có thể thực hiện làm cho hiệu suất của chúng ít xấu hơn.

Kalén Delaney trong cuốn sách "Microsoft SQL Server 2008 Internals" nói:

Collation có thể làm cho một sự khác biệt rất lớn khi SQL Server phải nhìn vào hầu tất cả các ký tự trong chuỗi. Đối với dụ, hãy nhìn vào những điều sau đây:

SELECT COUNT(*) FROM tbl WHERE longcol LIKE '%abc%' 

Điều này có thể thực hiện 10 lần nhanh hơn hoặc nhiều hơn với một collation nhị phân so với Windows chiếu nonbinary. Và với dữ liệu varchar, điều này thực thi nhanh hơn bảy hoặc tám lần với một đối chiếu SQL so với Windows collation.

+0

Việc đối chiếu ảnh hưởng như thế nào đến hiệu suất? – crosenblum

+0

Thông tin thêm từ sách "Nếu bạn có cột VARCHAR, bạn có thể tăng tốc độ này lên bằng cách buộc đối chiếu như sau: CHỌN COUNT (*) TỪ WHERE longcol COLLATE SQL_Latin1_General_CP_CI_AS LIKE '% abc%';" –

+0

@yoelhalb bạn có thể có nghĩa là '_CP1_' không phải' _CP_' – jazzcat

0

Tôi đã thử một giải pháp khả thi. Trước khi giải pháp này thậm chí truy vấn không trả về kết quả và gây ra lỗi kết nối thời gian chờ.

Truy vấn của tôi đã có bộ lọc ngày và các tiêu chí khác.Tất cả các tiêu chí khác giống như tìm kiếm. Một từ khóa cột đã được tìm kiếm như '% abc%' trên cột ntext và nó đang thực hiện quét toàn bộ bảng.

Giải pháp:

Chia truy vấn trong 2 phần. 1) Phần đầu tiên trong CTE (Common Table Express) 2) Áp dụng tất cả các tiêu chí tìm kiếm trên CTE.

WITH SearchData(Column1,Column2,Column3,Column4,........) 
    AS 
    (
    SELECT Column1,Column2,Column3,Column4,........... 
    FROM myTable1 WITH(NOLOCK) 
      INNER JOIN MyTable2 WITH(NOLOCK) 
       ON MyTable1.id = MyTable2.Id 
    WHERE (MyTable1.CreationTime >= '2014-04-27' AND MyTable1.CreationTime <= '2014-05-01') 
) 

    SELECT DISTINCT top 250 Column1,Column2,Column3,Column4 
    FROM SearchData 
    WHERE (ISNULL(Column1,'') LIKE @Column1 +'%' OR @Column1 IS NULL) 
      and (Column2 LIKE @Column2+ '%' OR @Column2 IS NULL) 
      ... 
      ... 
      ... 
      ... 
      AND (Column10 like '%'[email protected]+'%' or @Column10 IS NULL) 
      AND @[email protected][email protected][email protected] <> '' 
      ORDER BY [CreationTime] DESC 

Nó làm việc cho tôi.

+0

Tôi nghĩ rằng sử dụng mệnh đề 'TOP' với truy vấn có điều kiện LIKE sẽ luôn hoạt động. Vì vậy, mệnh đề TOP của nó chứ không phải là CTE đóng góp vào hiệu suất đạt được, vì với TOP quét toàn bộ được cắt ngắn ngay khi số lượng bản ghi yêu cầu được quét. – Sunil

+1

như là một lưu ý ngoài chủ đề bằng cách sử dụng WITH (NOLOCK) trong mỗi lần tham gia là rất nguy hiểm, bạn phải rất cẩn thận với nó hoặc bạn sẽ nhận được dữ liệu trùng lặp, v.v ... tôi thấy mọi người trowing gợi ý này như một cách tiêu chuẩn tối ưu hóa truy vấn, không phải.bạn nên luôn luôn suy nghĩ hai lần và xem lại cách bảng được viết (chèn/cập nhật) trong ứng dụng của bạn trước khi trowing trong một gợi ý nolock. (chỉ nói) –

0

Nếu bạn không thể sử dụng FullTextSearch, bạn có thể tăng tốc độ gấp 10 lần. Làm gì tiếp theo:

1 Thêm tính lĩnh vực:

alter table TableName 
add CalculatedColumnName as upper(Column1 + '|' + Column2...) collate Latin1_General_100_Bin2 
persisted; 

2 Thêm chỉ mục cho lĩnh vực tính toán:

create nonclustered index IDX_TableName_CalculatedColumnName 
on TableName(CalculatedColumnName); 

3 Thay đổi nội dung truy vấn của bạn

select count(*) 
from TableName 
where CalculatedColumnName like '%' + upper(@ParameterValue) + '%' collate Latin1_General_100_Bin2 

Nguồn: http://aboutsqlserver.com/2015/01/20/optimizing-substring-search-performance-in-sql-server

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