"Tại sao thậm chí sử dụng db.Exec ()":
Đúng là bạn có thể sử dụng db.Exec
và db.Query
có thể hoán đổi cho nhau để thực hiện các câu lệnh sql giống nhau, tuy nhiên hai phương thức trả về các kiểu kết quả khác nhau. Nếu được trình điều khiển triển khai, kết quả trả về từ db.Exec
có thể cho bạn biết có bao nhiêu hàng bị ảnh hưởng bởi truy vấn, trong khi db.Query
sẽ trả về đối tượng hàng thay thế.
Ví dụ:giả sử bạn muốn thực thi DELETE
và bạn muốn biết có bao nhiêu hàng đã bị xóa bởi nó. Bạn có thể làm điều đó theo cách thích hợp:
res, err := db.Exec(`DELETE FROM my_table WHERE expires_at = $1`, time.Now())
if err != nil {
panic(err)
}
numDeleted, err := res.RowsAffected()
if err != nil {
panic(err)
}
print(numDeleted)
hoặc cách chi tiết hơn và khách quan hơn, tốn kém hơn:
rows, err := db.Query(`DELETE FROM my_table WHERE expires_at = $1 RETURNING *`, time.Now())
if err != nil {
panic(err)
}
defer rows.Close()
var numDelete int
for rows.Next() {
numDeleted += 1
}
if err := rows.Err(); err != nil {
panic(err)
}
print(numDeleted)
Có một cách thứ 3 bạn có thể làm điều này với sự kết hợp của các CTE postgres, SELECT COUNT
, db.QueryRow
và row.Scan
nhưng tôi không nghĩ rằng một ví dụ là cần thiết để cho thấy cách tiếp cận sẽ không hợp lý như thế nào khi so sánh với db.Exec
.
Một lý do khác để sử dụng db.Exec
over db.Query
là khi bạn không quan tâm đến kết quả trả về, khi tất cả những gì bạn cần là thực hiện truy vấn và kiểm tra xem có lỗi hay không. Trong trường hợp như vậy, bạn có thể làm điều này:
if _, err := db.Exec(`<my_sql_query>`); err != nil {
panic(err)
}
Mặt khác, bạn không thể (có thể nhưng không nên) làm điều này:
if _, err := db.Query(`<my_sql_query>`); err != nil {
panic(err)
}
Làm điều này, sau một thời gian ngắn, chương trình của bạn sẽ phát ra lỗi với lỗi tương tự như too many connections open
. Điều này là do bạn đang loại bỏ db.Rows
được trả về giá trị mà không cần thực hiện Close
bắt buộc trước gọi nó, và do đó bạn kết thúc với số lượng kết nối mở tăng lên và cuối cùng đạt đến giới hạn của máy chủ.
"hoặc câu lệnh chuẩn bị sẵn trong Golang?":
Tôi không nghĩ rằng cuốn sách bạn đã trích dẫn là chính xác. Ít nhất đối với tôi, nó giống như có hay không một db.Query
lệnh gọi tạo ra một câu lệnh chuẩn bị mới mọi lúc phụ thuộc vào trình điều khiển bạn đang sử dụng.
Ví dụ xem hai phần này của queryDC
(một phương thức chưa được báo cáo được gọi bởi db.Query
):không có tuyên bố chuẩn bị và với tuyên bố đã chuẩn bị.
Bất kể cuốn sách có đúng hay không là db.Stmt
được tạo bởi db.Query
sẽ là, trừ khi có một số bộ nhớ đệm nội bộ đang diễn ra, bị loại bỏ sau khi bạn đóng Rows
được trả về vật. Nếu thay vào đó, bạn hãy gọi thủ công db.Prepare
và sau đó lưu vào bộ nhớ cache và sử dụng lại db.Stmt
đã trả về bạn có thể cải thiện hiệu suất của các truy vấn cần được thực thi thường xuyên.
Để hiểu cách sử dụng một tuyên bố đã chuẩn bị để tối ưu hóa hiệu suất, bạn có thể xem tài liệu chính thức:https://www.postgresql.org/docs/current/static/sql-prepare.html