"Pourquoi même utiliser db.Exec()":
Il est vrai que vous pouvez utiliser db.Exec
et db.Query
indifféremment pour exécuter les mêmes instructions SQL, mais les deux méthodes renvoient des types de résultats différents. Si implémenté par le pilote, le résultat renvoyé par db.Exec
peut vous dire combien de lignes ont été affectées par la requête, tandis que db.Query
renverra l'objet rows à la place.
Par exemple, disons que vous voulez exécuter un DELETE
et vous voulez savoir combien de lignes ont été supprimées par celle-ci. Vous pouvez le faire de la manière appropriée :
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)
ou la manière la plus détaillée et objectivement la plus coûteuse :
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)
Il existe une troisième façon de procéder avec une combinaison de postgres CTE, SELECT COUNT
, db.QueryRow
et row.Scan
mais je ne pense pas qu'un exemple soit nécessaire pour montrer à quel point une approche serait déraisonnable par rapport à db.Exec
.
Une autre raison d'utiliser db.Exec
sur db.Query
c'est quand vous ne vous souciez pas du résultat renvoyé, quand tout ce dont vous avez besoin est d'exécuter la requête et de vérifier s'il y a eu une erreur ou non. Dans ce cas, vous pouvez faire ceci :
if _, err := db.Exec(`<my_sql_query>`); err != nil {
panic(err)
}
D'un autre côté, vous ne pouvez pas (vous pouvez mais vous ne devriez pas) faire ceci :
if _, err := db.Query(`<my_sql_query>`); err != nil {
panic(err)
}
En faisant cela, après un court instant, votre programme paniquera avec une erreur qui dit quelque chose comme too many connections open
. C'est parce que vous supprimez le db.Rows
renvoyé valeur sans faire d'abord l'obligatoire Close
appelez-le, et vous vous retrouvez donc avec le nombre de connexions ouvertes qui augmente et finit par atteindre la limite du serveur.
"ou des déclarations préparées en Golang ?" :
Je ne pense pas que le livre que vous citez soit correct. Au moins pour moi, cela ressemble ou non à un db.Query
call crée une nouvelle instruction préparée à chaque fois dépend du pilote que vous utilisez.
Voir par exemple ces deux sections de queryDC
(une méthode non exportée appelée par db.Query
) :sans instruction préparée et avec instruction préparée.
Que le livre soit correct ou non, un db.Stmt
créé par db.Query
serait, à moins qu'il n'y ait une mise en cache interne en cours, jeté après la fermeture des Rows
renvoyés objet. Si vous appelez plutôt manuellement db.Prepare
puis mettre en cache et réutiliser le db.Stmt
renvoyé vous pouvez potentiellement améliorer les performances des requêtes qui doivent être exécutées souvent.
Pour comprendre comment une instruction préparée peut être utilisée pour optimiser les performances, vous pouvez consulter la documentation officielle :https://www.postgresql.org/docs/current/static/sql-prepare.html