„Защо дори да използвате db.Exec()“:
Вярно е, че можете да използвате db.Exec
и db.Query
взаимозаменяемо за изпълнение на едни и същи sql оператори, но двата метода връщат различни типове резултати. Ако се реализира от драйвера, резултатът се връща от db.Exec
може да ви каже колко реда са били засегнати от заявката, докато db.Query
вместо това ще върне обекта rows.
Например, да кажем, че искате да изпълните DELETE
изявление и искате да знаете колко реда са били изтрити от него. Можете да го направите по правилния начин:
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)
или по-подробният и обективно по-скъп начин:
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)
Има трети начин, по който можете да направите това с комбинация от postgres CTEs, SELECT COUNT
, db.QueryRow
и row.Scan
но не мисля, че е необходим пример, за да покаже колко неразумен би бил подходът в сравнение с db.Exec
.
Друга причина да използвате db.Exec
през db.Query
е когато не ви пука за върнатия резултат, когато всичко, от което се нуждаете, е да изпълните заявката и да проверите дали е имало грешка или не. В такъв случай можете да направите това:
if _, err := db.Exec(`<my_sql_query>`); err != nil {
panic(err)
}
От друга страна, не можете (можете, но не трябва) да правите това:
if _, err := db.Query(`<my_sql_query>`); err != nil {
panic(err)
}
Правейки това, след кратко време програмата ви ще изпадне в паника с грешка, която казва нещо подобно на too many connections open
. Това е така, защото изхвърляте върнатите db.Rows
стойност, без първо да правите задължителното Close
извикайте го и така в крайна сметка броят на отворените връзки нараства и в крайна сметка достига лимита на сървъра.
"или подготвени изявления на Голанг?":
Не мисля, че цитираната от теб книга е вярна. Поне за мен изглежда като дали db.Query
или не call създава нов подготвен оператор всеки път, зависи от драйвера, който използвате.
Вижте например тези две секции на queryDC
(неекспортиран метод, извикан от db.Query
):без подготвено изявление и с подготвено изявление.
Независимо дали книгата е правилна или не db.Stmt
създадено от db.Query
ще бъде, освен ако не се извършва някакво вътрешно кеширане, изхвърлено, след като затворите върнатите Rows
обект. Ако вместо това извикате ръчно db.Prepare
и след това кеширайте и използвайте повторно върнатия db.Stmt
можете потенциално да подобрите производителността на заявките, които трябва да се изпълняват често.
За да разберете как подготвеното изявление може да се използва за оптимизиране на производителността, можете да разгледате официалната документация:https://www.postgresql.org/docs/current/static/sql-prepare.html