Nếu có thể, đừng làm điều này.
Đó là câu trả lời - đó là một kiểu chống đối. Nếu máy khách biết bảng mà nó muốn có dữ liệu, thì SELECT FROM ThatTable
. Nếu một cơ sở dữ liệu được thiết kế theo cách mà điều này được yêu cầu, nó có vẻ như được thiết kế dưới mức tối ưu. Nếu một lớp truy cập dữ liệu cần biết liệu một giá trị có tồn tại trong bảng hay không, thì việc soạn SQL trong mã đó rất dễ dàng và việc đẩy mã này vào cơ sở dữ liệu là không tốt.
Đối với tôi, điều này có vẻ giống như việc lắp đặt một thiết bị bên trong thang máy, nơi người ta có thể gõ số tầng mong muốn. Sau khi nút Go được nhấn, nó sẽ di chuyển một bàn tay cơ học đến đúng nút của tầng mong muốn và nhấn nó. Điều này dẫn đến nhiều vấn đề tiềm ẩn.
Xin lưu ý: không có ý định chế nhạo, ở đây. Ví dụ về thang máy ngớ ngẩn của tôi là * thiết bị tốt nhất mà tôi có thể tưởng tượng * để chỉ ra ngắn gọn các vấn đề với kỹ thuật này. Nó thêm một lớp vô dụng của hướng, di chuyển lựa chọn tên bảng từ không gian người gọi (sử dụng DSL, SQL mạnh mẽ và được hiểu rõ) thành một kết hợp sử dụng mã SQL phía máy chủ khó hiểu / kỳ lạ.
Việc phân chia trách nhiệm như vậy thông qua chuyển động của logic xây dựng truy vấn thành SQL động làm cho mã khó hiểu hơn. Nó vi phạm một quy ước tiêu chuẩn và đáng tin cậy (cách truy vấn SQL chọn những gì cần chọn) dưới tên mã tùy chỉnh đầy tiềm ẩn lỗi.
Dưới đây là điểm chi tiết về một số vấn đề tiềm ẩn với phương pháp này:
SQL động cung cấp khả năng chèn SQL mà khó có thể nhận ra trong mã giao diện người dùng hoặc mã kết thúc sau (người ta phải kiểm tra chúng cùng nhau để xem điều này).
Các thủ tục và hàm được lưu trữ có thể truy cập tài nguyên mà chủ sở hữu SP / chức năng có quyền nhưng người gọi thì không. Theo như tôi hiểu, nếu không có sự quan tâm đặc biệt, thì theo mặc định khi bạn sử dụng mã tạo ra SQL động và chạy nó, cơ sở dữ liệu thực thi SQL động theo quyền của người gọi. Điều này có nghĩa là bạn sẽ không thể sử dụng các đối tượng đặc quyền hoặc bạn phải mở chúng cho tất cả các máy khách, làm tăng diện tích bề mặt của cuộc tấn công tiềm ẩn đối với dữ liệu đặc quyền. Đặt SP / chức năng tại thời điểm tạo để luôn chạy với tư cách người dùng cụ thể (trong SQL Server, EXECUTE AS
) có thể giải quyết vấn đề đó, nhưng làm cho mọi thứ phức tạp hơn. Điều này làm trầm trọng thêm nguy cơ tiêm SQL được đề cập ở điểm trước, bằng cách biến SQL động trở thành một vectơ tấn công rất hấp dẫn.
Khi một nhà phát triển phải hiểu mã ứng dụng đang làm gì để sửa đổi nó hoặc sửa lỗi, anh ta sẽ thấy rất khó để thực thi truy vấn SQL chính xác. Trình biên dịch SQL có thể được sử dụng, nhưng điều này có các đặc quyền đặc biệt và có thể có tác động tiêu cực đến hiệu suất trên hệ thống sản xuất. Truy vấn được thực thi có thể được ghi lại bởi SP nhưng điều này làm tăng độ phức tạp vì lợi ích đáng ngờ (yêu cầu cung cấp các bảng mới, xóa dữ liệu cũ, v.v.) và khá không rõ ràng. Trên thực tế, một số ứng dụng được cấu trúc sao cho nhà phát triển không có thông tin đăng nhập cơ sở dữ liệu, do đó, hầu như anh ta không thể thực sự thấy truy vấn đang được gửi.
Khi xảy ra lỗi, chẳng hạn như khi bạn cố gắng chọn một bảng không tồn tại, bạn sẽ nhận được một thông báo dọc theo dòng "tên đối tượng không hợp lệ" từ cơ sở dữ liệu. Điều đó sẽ xảy ra hoàn toàn giống nhau cho dù bạn đang soạn SQL ở back end hay cơ sở dữ liệu, nhưng sự khác biệt là, một số nhà phát triển kém đang cố gắng khắc phục sự cố hệ thống phải kéo sâu thêm một cấp vào một hang khác bên dưới hang nơi vấn đề tồn tại, để đào sâu vào thủ tục kỳ diệu Có Tất cả để cố gắng tìm ra vấn đề là gì. Nhật ký sẽ không hiển thị "Lỗi trong GetWidget", nó sẽ hiển thị "Lỗi trong OneProcedureToRuleThemAllRunner". Sự trừu tượng này nói chung sẽ làm cho một hệ thống tồi tệ hơn .
Một ví dụ trong pseudo-C # về chuyển đổi tên bảng dựa trên một tham số:
string sql = $"SELECT * FROM {EscapeSqlIdentifier(tableName)};"
results = connection.Execute(sql);
Mặc dù điều này không loại bỏ mọi vấn đề có thể có trong tưởng tượng, nhưng những sai sót mà tôi đã nêu ra với kỹ thuật khác không có trong ví dụ này.