Xóa và đặt lại các truy vấn đã chuẩn bị
Vì các truy vấn đã chuẩn bị có thể được thực thi nhiều lần, trong một vòng lặp cho các giá trị tham số khác nhau, cần phải đặt lại truy vấn về trạng thái ban đầu tại mỗi lần lặp. Điều này được thực hiện bởi hàm DatabaseReset
. Nhưng không có ý nghĩa khi gọi nó nếu truy vấn đã chuẩn bị chỉ được thực thi một lần.
bool DatabaseReset(int request)
Hàm này đặt lại các cấu trúc truy vấn đã biên dịch bên trong về trạng thái ban đầu, tương tự như gọi DatabasePrepare
DatabasePrepare. Tuy nhiên, DatabaseReset
không biên dịch lại truy vấn và do đó rất nhanh.
Điều quan trọng nữa là hàm này không đặt lại các liên kết dữ liệu đã thiết lập trong truy vấn nếu đã có bất kỳ liên kết nào được thực hiện. Do đó, nếu cần, bạn có thể thay đổi giá trị của chỉ một hoặc một số lượng nhỏ tham số. Sau đó, sau khi gọi DatabaseReset
, bạn chỉ cần gọi các hàm DatabaseBind
cho các tham số đã thay đổi.
Vào thời điểm viết sách, API MQL5 không cung cấp hàm để đặt lại liên kết dữ liệu, tương tự như hàm
sqlite_clear_bindings
trong phân phối chuẩn SQLite.
Trong tham số request
, hãy chỉ định handle hợp lệ của truy vấn đã thu được trước đó từ DatabasePrepare
. Nếu bạn truyền một handle của truy vấn đã bị xóa trước đó bằng DatabaseFinalize
(xem bên dưới), một lỗi sẽ được trả về.
Hàm trả về chỉ báo thành công (true
) hoặc lỗi (false
).
Nguyên tắc chung của việc làm việc với các truy vấn lặp lại được thể hiện trong đoạn mã giả sau. Các hàm DatabaseBind
và DatabaseRead
sẽ được mô tả trong các phần tiếp theo và sẽ được "đóng gói" vào các lớp ORM.
struct Data // ví dụ cấu trúc
{
long count;
double value;
string comment;
};
Data data[];
... // lấy mảng dữ liệu
int r =
DatabasePrepare(db, "INSERT... (?, ?, ?)")); // biên dịch truy vấn với tham số
for(int i = 0; i < ArraySize(data); ++i) // vòng lặp dữ liệu
{
DatabaseBind(r, 0, data[i].count); // liên kết dữ liệu với tham số
DatabaseBind(r, 1, data[i].value);
DatabaseBind(r, 2, data[i].comment);
DatabaseRead(r); // thực thi yêu cầu
... // phân tích hoặc lưu kết quả
DatabaseReset(r); // trạng thái ban đầu tại mỗi lần lặp
}
DatabaseFinalize(r);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Sau khi truy vấn đã chuẩn bị không còn cần thiết nữa, bạn nên giải phóng tài nguyên máy tính mà nó chiếm dụng bằng cách sử dụng DatabaseFinalize
.
void DatabaseFinalize(int request)
Hàm này xóa truy vấn với handle được chỉ định, được tạo trong DatabasePrepare
DatabasePrepare.
Nếu một mô tả không chính xác được truyền vào, hàm sẽ ghi ERR_DATABASE_INVALID_HANDLE vào _LastError
.
Khi đóng cơ sở dữ liệu bằng DatabaseClose
DatabaseClose, tất cả các handle truy vấn được tạo cho nó sẽ tự động bị xóa và vô hiệu hóa.
Hãy bổ sung tầng ORM của chúng ta (DBSQLite.mqh
) với một lớp mới DBQuery
để làm việc với các truy vấn đã chuẩn bị. Hiện tại, nó chỉ chứa chức năng khởi tạo và hủy khởi tạo vốn có trong khái niệm RAII, nhưng chúng ta sẽ mở rộng nó sớm thôi.
class DBQuery
{
protected:
const string sql; // truy vấn
const int db; // handle cơ sở dữ liệu (đối số hàm tạo)
const int handle; // handle yêu cầu đã chuẩn bị
public:
DBQuery(const int owner, const string s): db(owner), sql(s),
handle(PRTF(DatabasePrepare(db, sql)))
{
}
~DBQuery()
{
DatabaseFinalize(handle);
}
bool isValid() const
{
return handle != INVALID_HANDLE;
}
virtual bool reset()
{
return DatabaseReset(handle);
}
...
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Trong lớp DBSQLite
, chúng ta bắt đầu việc chuẩn bị yêu cầu trong phương thức prepare
bằng cách tạo một thể hiện của DBQuery
. Tất cả các đối tượng truy vấn sẽ được lưu trữ trong mảng nội bộ queries
dưới dạng các con trỏ tự động, điều này cho phép mã gọi không cần theo dõi việc xóa chúng một cách rõ ràng.
class DBSQLite
{
...
protected:
AutoPtr<DBQuery> queries[];
public:
DBQuery *prepare(const string sql)
{
return PUSH(queries, new DBQuery(handle, sql));
}
...
};
2
3
4
5
6
7
8
9
10
11
12