"Perché anche usare db.Exec()":
È vero che puoi usare db.Exec
e db.Query
in modo intercambiabile per eseguire le stesse istruzioni sql, tuttavia i due metodi restituiscono diversi tipi di risultati. Se implementato dal driver il risultato restituito da db.Exec
può dirti quante righe sono state interessate dalla query, mentre db.Query
restituirà invece l'oggetto righe.
Ad esempio, supponiamo che tu voglia eseguire un DELETE
istruzione e vuoi sapere quante righe sono state cancellate da essa. Puoi farlo nel modo corretto:
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)
o il modo più dettagliato e oggettivamente più costoso:
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'è un terzo modo per farlo con una combinazione di CTE postgres, SELECT COUNT
, db.QueryRow
e row.Scan
ma non credo sia necessario un esempio per mostrare quanto sarebbe irragionevole un approccio rispetto a db.Exec
.
Un altro motivo per usare db.Exec
su db.Query
è quando non ti interessa il risultato restituito, quando tutto ciò di cui hai bisogno è eseguire la query e verificare se si è verificato un errore o meno. In tal caso puoi farlo:
if _, err := db.Exec(`<my_sql_query>`); err != nil {
panic(err)
}
D'altra parte, non puoi (puoi ma non dovresti) fare questo:
if _, err := db.Query(`<my_sql_query>`); err != nil {
panic(err)
}
In questo modo, dopo un po', il tuo programma andrà nel panico con un errore che dice qualcosa di simile a too many connections open
. Questo perché stai eliminando il db.Rows
restituito valore senza prima rendere obbligatorio il Close
chiamalo e così il numero di connessioni aperte aumenta e alla fine raggiunge il limite del server.
"o dichiarazioni preparate in Golang?":
Non credo che il libro che hai citato sia corretto. Almeno a me sembra o meno un db.Query
call crea una nuova dichiarazione preparata ogni volta che dipende dal driver che stai utilizzando.
Vedi ad esempio queste due sezioni di queryDC
(un metodo non esportato chiamato da db.Query
):senza dichiarazione preparata e con dichiarazione preparata.
Indipendentemente dal fatto che il libro sia corretto o meno un db.Stmt
creato da db.Query
sarebbe, a meno che non sia in corso una memorizzazione nella cache interna, eliminata dopo aver chiuso le Rows
restituite oggetto. Se invece chiami manualmente db.Prepare
quindi memorizza nella cache e riutilizza il db.Stmt
restituito puoi potenzialmente migliorare le prestazioni delle query che devono essere eseguite spesso.
Per capire come utilizzare una dichiarazione preparata per ottimizzare le prestazioni puoi dare un'occhiata alla documentazione ufficiale:https://www.postgresql.org/docs/current/static/sql-prepare.html