Chuẩn bị các truy vấn liên kết: DatabasePrepare
Trong nhiều trường hợp, các tham số cần được nhúng vào các truy vấn SQL. Vì truy vấn SQL "ban đầu" là một chuỗi tương ứng với cú pháp đặc biệt, nó có thể được tạo thành bằng lời gọi StringFormat
đơn giản hoặc bằng cách nối chuỗi, thêm các giá trị tham số vào đúng vị trí. Chúng ta đã sử dụng kỹ thuật này trong các truy vấn để tạo bảng ("CREATE TABLE %s '%s' (%s);"), nhưng ở đây chỉ một phần của các tham số chứa dữ liệu (danh sách giá trị được thay thế cho %s trong dấu ngoặc), còn lại đại diện cho một tùy chọn và tên bảng. Trong phần này, chúng ta sẽ tập trung hoàn toàn vào việc thay thế dữ liệu vào truy vấn. Việc thực hiện điều này theo cách SQL gốc là quan trọng vì nhiều lý do.
Trước hết, truy vấn SQL chỉ được truyền đến động cơ SQLite dưới dạng chuỗi, và tại đó nó được phân tích thành các thành phần, kiểm tra tính đúng đắn và "biên dịch" theo một cách nhất định (tất nhiên, đây không phải là trình biên dịch MQL5). Truy vấn đã biên dịch sau đó được thực thi bởi cơ sở dữ liệu. Đó là lý do tại sao chúng ta đặt từ "ban đầu" trong dấu ngoặc kép.
Khi cùng một truy vấn cần được thực thi với các tham số khác nhau (ví dụ, chèn nhiều bản ghi vào một bảng; chúng ta đang dần tiếp cận nhiệm vụ này), việc biên dịch và kiểm tra truy vấn riêng cho từng bản ghi là khá không hiệu quả. Sẽ đúng hơn nếu biên dịch truy vấn một lần, sau đó thực thi hàng loạt, chỉ thay thế các giá trị khác nhau.
Thao tác biên dịch này được gọi là chuẩn bị truy vấn và được thực hiện bởi hàm DatabasePrepare
.
Các truy vấn đã chuẩn bị còn có một mục đích nữa: với sự trợ giúp của chúng, động cơ SQLite trả về kết quả thực thi truy vấn cho mã MQL5 (bạn sẽ tìm hiểu thêm về điều này trong các phần Thực thi các truy vấn đã chuẩn bị và Đọc riêng các trường bản ghi kết quả truy vấn).
Khoảnh khắc cuối cùng, nhưng không kém phần quan trọng, liên quan đến các truy vấn tham số hóa là chúng bảo vệ chương trình của bạn khỏi các cuộc tấn công tiềm tàng của hacker được gọi là SQL injection. Trước hết, điều này rất quan trọng đối với cơ sở dữ liệu của các trang web công cộng, nơi thông tin do người dùng nhập được ghi lại trong cơ sở dữ liệu bằng cách nhúng nó vào các truy vấn SQL: nếu trong trường hợp này sử dụng thay thế định dạng đơn giản '%s', người dùng sẽ có thể nhập một chuỗi dài thay vì dữ liệu mong đợi với các lệnh SQL bổ sung, và nó sẽ trở thành một phần của truy vấn SQL ban đầu, làm sai lệch ý nghĩa của nó. Nhưng nếu truy vấn SQL được biên dịch, nó không thể bị thay đổi bởi dữ liệu đầu vào: nó luôn được coi là dữ liệu.
Mặc dù chương trình MQL không phải là chương trình máy chủ, nó vẫn có thể lưu trữ thông tin nhận được từ người dùng trong cơ sở dữ liệu.
int DatabasePrepare(int database, const string sql, ...)
Hàm DatabasePrepare
tạo một handle trong cơ sở dữ liệu được chỉ định cho truy vấn trong chuỗi sql
. database
phải được mở trước đó bằng hàm DatabaseOpen
DatabaseOpen.
Vị trí các tham số truy vấn được chỉ định trong chuỗi sql
bằng các đoạn '?1', '?2', '?3', v.v. Số thứ tự biểu thị chỉ số tham số được sử dụng trong tương lai khi gán giá trị đầu vào cho nó, trong các hàm DatabaseBind
DatabaseBind. Các số trong chuỗi sql
không cần phải theo thứ tự và có thể lặp lại nếu cùng một tham số cần được chèn vào các vị trí khác nhau trong truy vấn.
Chú ý! Chỉ số trong các đoạn thay thế '?n' bắt đầu từ 1, trong khi trong các hàm
DatabaseBind
nó bắt đầu từ 0. Ví dụ, tham số '?1' trong nội dung truy vấn sẽ nhận giá trị khi gọiDatabaseBind
tại chỉ số 0, tham số '?2' tại chỉ số 1, v.v. Độ lệch cố định 1 này được duy trì ngay cả khi có khoảng trống (dù là vô tình hay cố ý) trong việc đánh số các tham số '?n'.
Nếu bạn định liên kết tất cả các tham số theo thứ tự nghiêm ngặt, bạn có thể sử dụng ký hiệu rút gọn: tại vị trí của mỗi tham số, chỉ cần chỉ định ký hiệu '?' mà không có số: trong trường hợp này, các tham số được đánh số tự động. Bất kỳ tham số '?' nào không có số sẽ nhận số lớn hơn 1 so với số tối đa của các tham số đã đọc bên trái (với số rõ ràng hoặc được tính theo cùng nguyên tắc, và tham số đầu tiên sẽ nhận số 1, tức là '?1').
Do đó, yêu cầu
SELECT * FROM table WHERE risk > ?1 AND signal = ?2
tương đương với:
SELECT * FROM table WHERE risk > ? AND signal = ?
Nếu một số tham số là hằng số hoặc truy vấn được chuẩn bị để thực thi một lần nhằm lấy kết quả, các giá trị tham số có thể được truyền vào hàm DatabasePrepare
dưới dạng danh sách phân tách bằng dấu phẩy thay vì dấu ba chấm (giống như trong Print
hoặc Comment
).
Các tham số truy vấn chỉ có thể được sử dụng để đặt giá trị trong các cột bảng (khi ghi, thay đổi hoặc điều kiện lọc). Tên của bảng, cột, tùy chọn và từ khóa SQL không thể được truyền qua các tham số '?'/'?n'.
Hàm DatabasePrepare
tự nó không thực hiện truy vấn. Handle trả về từ nó sau đó phải được truyền vào các lời gọi hàm DatabaseRead
DatabaseRead hoặc DatabaseReadBind
DatabaseReadBind. Các hàm này thực thi truy vấn và làm cho kết quả sẵn sàng để đọc (có thể là một bản ghi hoặc nhiều bản ghi). Tất nhiên, nếu có các chỗ giữ tham số ('?' hoặc '?n') trong truy vấn, và các giá trị cho chúng không được chỉ định trong DatabasePrepare
, trước khi thực thi truy vấn, bạn cần liên kết các tham số và dữ liệu bằng các hàm DatabaseBind
DatabaseBind phù hợp.
Nếu một giá trị không được gán cho tham số, NULL sẽ được thay thế cho nó trong quá trình thực thi truy vấn.
Trong trường hợp xảy ra lỗi, hàm DatabasePrepare
sẽ trả về INVALID_HANDLE.
Ví dụ về việc sử dụng DatabasePrepare
sẽ được giới thiệu trong các phần tiếp theo, sau khi khám phá các tính năng khác liên quan đến các truy vấn đã chuẩn bị.