"Proč vůbec používat db.Exec()":
Je pravda, že můžete použít db.Exec
a db.Query
zaměnitelně provádět stejné příkazy SQL, ale tyto dvě metody vracejí různé typy výsledků. Pokud je implementován ovladačem, vrátí výsledek db.Exec
vám může říct, kolik řádků bylo ovlivněno dotazem, zatímco db.Query
místo toho vrátí objekt rows.
Řekněme například, že chcete provést DELETE
a chcete vědět, kolik řádků bylo odstraněno. Můžete to udělat buď správným způsobem:
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)
nebo podrobnější a objektivně nákladnější způsob:
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)
Existuje třetí způsob, jak to udělat pomocí kombinace postgresových CTE, SELECT COUNT
, db.QueryRow
a row.Scan
ale nemyslím si, že příklad je nutný, aby ukázal, jak nerozumný přístup by byl ve srovnání s db.Exec
.
Další důvod, proč používat db.Exec
přes db.Query
je, když se nestaráte o vrácený výsledek, kdy vše, co potřebujete, je provést dotaz a zkontrolovat, zda došlo k chybě nebo ne. V takovém případě můžete provést toto:
if _, err := db.Exec(`<my_sql_query>`); err != nil {
panic(err)
}
Na druhou stranu nemůžete (můžete, ale neměli byste) udělat toto:
if _, err := db.Query(`<my_sql_query>`); err != nil {
panic(err)
}
Když to uděláte, po krátké chvíli váš program zpanikaří s chybou, která říká něco podobného too many connections open
. Je to proto, že zahazujete vrácené db.Rows
hodnotu bez předchozího zadání povinného Close
zavolejte na něj a tak skončíte s tím, že počet otevřených spojení naroste a nakonec narazí na limit serveru.
"nebo připravené příkazy v Golang?":
Nemyslím si, že kniha, kterou citujete, je správná. Alespoň pro mě to vypadá jako zda db.Query
call vytvoří nový připravený příkaz pokaždé, když to závisí na ovladači, který používáte.
Viz například tyto dvě části queryDC
(neexportovaná metoda volaná db.Query
):bez připraveného výpisu a s připraveným výpisem.
Bez ohledu na to, zda je kniha správná nebo ne, db.Stmt
vytvořil db.Query
bude, pokud neprobíhá nějaké interní ukládání do mezipaměti, po zavření vrácených Rows
zahozen objekt. Pokud místo toho ručně zavoláte db.Prepare
a poté uložte a znovu použijte vrácený db.Stmt
můžete potenciálně zlepšit výkon dotazů, které je třeba provádět často.
Chcete-li pochopit, jak lze připravené prohlášení použít k optimalizaci výkonu, můžete se podívat na oficiální dokumentaci:https://www.postgresql.org/docs/current/static/sql-prepare.html