Câu hỏi ban đầu là "Làm cách nào để tôi tham số hóa truy vấn ..."
Hãy để tôi nói ngay tại đây, rằng đây không phải là một câu trả lời cho câu hỏi ban đầu. Đã có một số minh chứng về điều đó trong các câu trả lời tốt khác.
Như đã nói, hãy tiếp tục và đánh dấu câu trả lời này, đánh giá thấp nó, đánh dấu nó không phải là một câu trả lời ... làm bất cứ điều gì bạn tin là đúng.
Xem câu trả lời từ Mark Brackett để biết câu trả lời ưa thích mà tôi (và 231 người khác) nêu lên. Cách tiếp cận được đưa ra trong câu trả lời của anh ấy cho phép 1) sử dụng hiệu quả các biến liên kết và 2) cho các vị từ có thể mở rộng được.
Câu trả lời được chọn
Điều tôi muốn giải quyết ở đây là cách tiếp cận được đưa ra trong câu trả lời của Joel Spolsky, câu trả lời "được chọn" là câu trả lời đúng.
Cách tiếp cận của Joel Spolsky là thông minh. Và nó hoạt động hợp lý, nó sẽ thể hiện hành vi có thể dự đoán và hiệu suất có thể dự đoán được, với các giá trị "bình thường" và với các trường hợp cạnh chuẩn, chẳng hạn như NULL và chuỗi rỗng. Và nó có thể là đủ cho một ứng dụng cụ thể.
Nhưng về mặt khái quát hóa phương pháp này, chúng ta cũng hãy xem xét các trường hợp góc tối hơn, như khi Name
cột chứa ký tự đại diện (như được nhận biết bởi vị từ THÍCH.) Ký tự đại diện tôi thấy thường được sử dụng là %
(ký hiệu phần trăm.). Vì vậy, bây giờ hãy giải quyết vấn đề đó ở đây và sau đó tiếp tục các trường hợp khác.
Một số vấn đề với% character
Xem xét một giá trị Tên của 'pe%ter'
. (Đối với các ví dụ ở đây, tôi sử dụng giá trị chuỗi bằng chữ thay cho tên cột.) Một hàng có giá trị Tên là '' pe% ter 'sẽ được trả về bởi một truy vấn có dạng:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Nhưng hàng tương tự sẽ không được trả về nếu thứ tự của các cụm từ tìm kiếm bị đảo ngược:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
Hành vi chúng tôi quan sát là loại kỳ lạ. Thay đổi thứ tự của các cụm từ tìm kiếm trong danh sách sẽ thay đổi tập kết quả.
Nó gần như đi mà không nói rằng chúng ta có thể không muốn pe%ter
kết hợp bơ đậu phộng, bất kể anh ấy thích nó như thế nào.
Trường hợp góc tối
(Có, tôi sẽ đồng ý rằng đây là trường hợp tối nghĩa. Có lẽ một trường hợp không có khả năng được kiểm tra. Chúng tôi không mong đợi ký tự đại diện trong giá trị cột. Chúng tôi có thể giả định rằng ứng dụng ngăn chặn giá trị đó được lưu trữ. Nhưng theo kinh nghiệm của tôi, tôi hiếm khi thấy một ràng buộc cơ sở dữ liệu đặc biệt không cho phép các ký tự hoặc mẫu được coi là ký tự đại diện ở phía bên phải của LIKE
toán tử so sánh.
Vá một lỗ
Một cách tiếp cận để vá lỗ hổng này là thoát khỏi %
ký tự đại diện. (Đối với bất kỳ ai không quen thuộc với mệnh đề thoát trên toán tử, đây là liên kết đến tài liệu SQL Server .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Bây giờ chúng ta có thể khớp với% theo nghĩa đen. Tất nhiên, khi chúng ta có một tên cột, chúng ta sẽ cần phải tự động thoát khỏi ký tự đại diện. Chúng ta có thể sử dụng REPLACE
hàm để tìm các lần xuất hiện của %
ký tự và chèn ký tự dấu gạch chéo ngược trước mỗi ký tự, như sau:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Vì vậy, giải quyết vấn đề với% ký tự đại diện. Hầu hết.
Thoát khỏi lối thoát
Chúng tôi nhận ra rằng giải pháp của chúng tôi đã giới thiệu một vấn đề khác. Nhân vật trốn thoát. Chúng tôi thấy rằng chúng tôi cũng sẽ cần phải thoát khỏi bất kỳ sự xuất hiện của chính nhân vật thoát. Lần này, chúng tôi sử dụng! như nhân vật thoát:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
Gạch dưới quá
Bây giờ chúng tôi đang triển khai, chúng tôi có thể thêm một thẻ REPLACE
điều khiển ký tự gạch dưới. Và để giải trí, lần này, chúng ta sẽ sử dụng $ làm ký tự thoát.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Tôi thích cách tiếp cận này hơn để thoát vì nó hoạt động trong Oracle và MySQL cũng như SQL Server. (Tôi thường sử dụng dấu gạch chéo ngược làm ký tự thoát, vì đó là ký tự chúng ta sử dụng trong các biểu thức thông thường. Nhưng tại sao lại bị ràng buộc bởi quy ước!
Những dấu ngoặc
SQL Server cũng cho phép các ký tự đại diện được coi là chữ bằng cách đặt chúng trong ngoặc []
. Vì vậy, chúng tôi chưa hoàn thành việc sửa chữa, ít nhất là cho SQL Server. Vì các cặp ngoặc có ý nghĩa đặc biệt, chúng tôi cũng sẽ cần phải thoát chúng. Nếu chúng ta quản lý để thoát đúng dấu ngoặc, thì ít nhất chúng ta sẽ không phải bận tâm với dấu gạch nối -
và carat ^
trong dấu ngoặc. Và chúng ta có thể để bất kỳ %
và các _
ký tự bên trong dấu ngoặc thoát, vì về cơ bản chúng ta đã vô hiệu hóa ý nghĩa đặc biệt của dấu ngoặc.
Tìm cặp dấu ngoặc phù hợp không nên khó. Khó hơn một chút so với việc xử lý các lần xuất hiện của singleton% và _. (Lưu ý rằng việc thoát khỏi tất cả các lần xuất hiện của dấu ngoặc là không đủ, bởi vì dấu ngoặc đơn được coi là bằng chữ và không cần phải thoát. Logic trở nên khó hiểu hơn tôi có thể xử lý mà không cần chạy nhiều trường hợp kiểm tra .)
Biểu hiện nội tuyến trở nên lộn xộn
Biểu thức nội tuyến đó trong SQL ngày càng dài hơn và xấu hơn. Có lẽ chúng ta có thể làm cho nó hoạt động, nhưng thiên đàng giúp đỡ linh hồn tội nghiệp phía sau và phải giải mã nó. Với nhiều người hâm mộ tôi dành cho các biểu thức nội tuyến, tôi có xu hướng không sử dụng nó ở đây, chủ yếu vì tôi không muốn phải để lại một bình luận giải thích lý do cho sự lộn xộn và xin lỗi về nó.
Một chức năng ở đâu?
Được rồi, vì vậy, nếu chúng ta không xử lý đó như là một biểu thức nội tuyến trong SQL, thì sự thay thế gần nhất mà chúng ta có là một hàm do người dùng định nghĩa. Và chúng ta biết rằng sẽ không tăng tốc mọi thứ (trừ khi chúng ta có thể định nghĩa một chỉ mục trên nó, giống như chúng ta có thể với Oracle.) Nếu chúng ta phải tạo một hàm, chúng ta có thể làm điều đó tốt hơn trong mã gọi SQL tuyên bố.
Và chức năng đó có thể có một số khác biệt trong hành vi, phụ thuộc vào DBMS và phiên bản. (Một tiếng hét cho tất cả các nhà phát triển Java của bạn rất quan tâm đến việc có thể sử dụng bất kỳ công cụ cơ sở dữ liệu nào có thể hoán đổi cho nhau.)
Kiến thức tên miền
Chúng tôi có thể có kiến thức chuyên môn về miền cho cột, (nghĩa là tập hợp các giá trị được phép thi hành cho cột. Chúng tôi có thể biết một tiên nghiệm rằng các giá trị được lưu trữ trong cột sẽ không bao giờ chứa dấu phần trăm, dấu gạch dưới hoặc dấu ngoặc trong trường hợp đó, chúng tôi chỉ bao gồm một nhận xét nhanh rằng những trường hợp đó được bảo hiểm.
Các giá trị được lưu trữ trong cột có thể cho phép% hoặc _ ký tự, nhưng một ràng buộc có thể yêu cầu các giá trị đó được thoát, có thể sử dụng một ký tự được xác định, sao cho các giá trị là so sánh "an toàn". Một lần nữa, một nhận xét nhanh về tập hợp các giá trị được phép và đặc biệt là nhân vật nào được sử dụng làm nhân vật thoát hiểm và đi theo cách tiếp cận của Joel Spolsky.
Nhưng, không có kiến thức chuyên môn và đảm bảo, điều quan trọng nhất là chúng tôi phải xem xét xử lý các trường hợp góc tối nghĩa đó và xem xét liệu hành vi đó có hợp lý và "theo thông số kỹ thuật" hay không.
Các vấn đề khác tóm tắt
Tôi tin rằng những người khác đã chỉ ra đầy đủ một số lĩnh vực quan tâm thường được xem xét khác:
Việc tiêm SQL (lấy thông tin có vẻ là thông tin do người dùng cung cấp và bao gồm thông tin trong văn bản SQL thay vì cung cấp chúng thông qua các biến liên kết. Sử dụng biến liên kết không bắt buộc, đó chỉ là một cách tiếp cận thuận tiện để ngăn chặn việc tiêm SQL. cách để đối phó với nó:
kế hoạch tối ưu hóa bằng cách sử dụng quét chỉ mục thay vì tìm kiếm chỉ mục, có thể cần một biểu thức hoặc hàm để thoát ký tự đại diện (chỉ mục có thể trên biểu thức hoặc hàm)
sử dụng các giá trị bằng chữ thay cho các biến liên kết ảnh hưởng đến khả năng mở rộng
Phần kết luận
Tôi thích cách tiếp cận của Joel Spolsky. Thật thông minh. Và nó hoạt động.
Nhưng ngay khi tôi nhìn thấy nó, tôi lập tức thấy một vấn đề tiềm ẩn với nó, và đó không phải là bản chất của tôi để cho nó trượt. Tôi không có ý chỉ trích những nỗ lực của người khác. Tôi biết nhiều nhà phát triển thực hiện công việc của họ rất cá nhân, vì họ đầu tư rất nhiều vào nó và họ quan tâm rất nhiều về nó. Vì vậy, hãy hiểu, đây không phải là một cuộc tấn công cá nhân. Những gì tôi xác định ở đây là loại vấn đề nảy sinh trong sản xuất hơn là thử nghiệm.
Vâng, tôi đã đi xa từ câu hỏi ban đầu. Nhưng nơi nào khác để lại ghi chú này liên quan đến những gì tôi coi là một vấn đề quan trọng với câu trả lời "được chọn" cho một câu hỏi?