"Varför ens använda db.Exec()":
Det är sant att du kan använda db.Exec
och db.Query
omväxlande för att köra samma SQL-satser, men de två metoderna returnerar olika typer av resultat. Om det implementeras av drivrutinen returneras resultatet från db.Exec
kan berätta hur många rader som påverkades av frågan, medan db.Query
returnerar radobjektet istället.
Låt oss till exempel säga att du vill köra en DELETE
uttalande och du vill veta hur många rader som raderades av den. Du kan göra det antingen på rätt sätt:
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)
eller det mer omfattande och objektivt dyrare sättet:
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)
Det finns ett tredje sätt att göra detta med en kombination av postgres CTE, SELECT COUNT
, db.QueryRow
och row.Scan
men jag tror inte att ett exempel är nödvändigt för att visa hur orimligt ett tillvägagångssätt det skulle vara jämfört med db.Exec
.
Ytterligare ett skäl att använda db.Exec
över db.Query
är när du inte bryr dig om det returnerade resultatet, när allt du behöver är att köra frågan och kontrollera om det var ett fel eller inte. I ett sådant fall kan du göra detta:
if _, err := db.Exec(`<my_sql_query>`); err != nil {
panic(err)
}
Å andra sidan kan du inte (du kan men du borde inte) göra detta:
if _, err := db.Query(`<my_sql_query>`); err != nil {
panic(err)
}
Om du gör detta kommer ditt program efter en kort stund att få panik med ett felmeddelande som säger något som liknar too many connections open
. Detta beror på att du kasserar de returnerade db.Rows
utan att först göra den obligatoriska Close
anropa det, och så slutar du med att antalet öppna anslutningar ökar och så småningom når serverns gräns.
"eller förberedda uttalanden i Golang?":
Jag tror inte att boken du citerade är korrekt. Åtminstone för mig ser det ut som om det är en db.Query
eller inte call skapar en ny förberedd sats varje gång beror på vilken drivrutin du använder.
Se till exempel dessa två avsnitt av queryDC
(en oexporterad metod som anropas av db.Query
):utan förberett utlåtande och med förberett utlåtande.
Oavsett om boken är korrekt eller inte en db.Stmt
skapad av db.Query
skulle, om det inte pågår någon intern cachning, kastas efter att du stänger de returnerade Rows
objekt. Om du istället manuellt ringer db.Prepare
och cache och återanvänd den returnerade db.Stmt
du kan eventuellt förbättra prestandan för de frågor som måste köras ofta.
För att förstå hur ett förberett uttalande kan användas för att optimera prestanda kan du ta en titt på den officiella dokumentationen:https://www.postgresql.org/docs/current/static/sql-prepare.html