"Warum überhaupt db.Exec() verwenden":
Es stimmt, dass Sie db.Exec
verwenden können und db.Query
austauschbar, um dieselben SQL-Anweisungen auszuführen, die beiden Methoden geben jedoch unterschiedliche Arten von Ergebnissen zurück. Falls vom Treiber implementiert, das von db.Exec
zurückgegebene Ergebnis kann Ihnen sagen, wie viele Zeilen von der Abfrage betroffen waren, während db.Query
gibt stattdessen das rows-Objekt zurück.
Nehmen wir zum Beispiel an, Sie möchten ein DELETE
ausführen -Anweisung und Sie möchten wissen, wie viele Zeilen dadurch gelöscht wurden. Sie können es auf die richtige Weise tun:
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)
oder die ausführlichere und objektiv teurere Art:
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)
Es gibt eine dritte Möglichkeit, dies mit einer Kombination aus Postgres-CTEs zu tun, SELECT COUNT
, db.QueryRow
und row.Scan
aber ich denke nicht, dass ein Beispiel notwendig ist, um zu zeigen, wie unvernünftig ein Ansatz wäre, wenn man ihn mit db.Exec
vergleicht .
Ein weiterer Grund, db.Exec
zu verwenden über db.Query
ist, wenn Sie sich nicht um das zurückgegebene Ergebnis kümmern, wenn Sie nur die Abfrage ausführen und prüfen müssen, ob ein Fehler aufgetreten ist oder nicht. In einem solchen Fall können Sie Folgendes tun:
if _, err := db.Exec(`<my_sql_query>`); err != nil {
panic(err)
}
Auf der anderen Seite können Sie (Sie können, aber Sie sollten nicht) Folgendes tun:
if _, err := db.Query(`<my_sql_query>`); err != nil {
panic(err)
}
Wenn Sie dies tun, gerät Ihr Programm nach kurzer Zeit mit einem Fehler in Panik, der so etwas wie too many connections open
besagt . Dies liegt daran, dass Sie die zurückgegebenen db.Rows
verwerfen Wert, ohne vorher das obligatorische Close
auszuführen Rufen Sie ihn an, und Sie enden damit, dass die Anzahl der offenen Verbindungen steigt und schließlich das Limit des Servers erreicht.
"oder vorbereitete Aussagen in Golang?":
Ich glaube nicht, dass das Buch, das Sie zitiert haben, richtig ist. Zumindest sieht es für mich danach aus, ob eine db.Query
Aufruf erstellt jedes Mal eine neue vorbereitete Anweisung, abhängig vom verwendeten Treiber.
Siehe zum Beispiel diese beiden Abschnitte von queryDC
(eine nicht exportierte Methode, die von db.Query
aufgerufen wird ):ohne vorbereitete Anweisung und mit vorbereiteter Anweisung.
Unabhängig davon, ob das Buch korrekt ist oder nicht, eine db.Stmt
erstellt von db.Query
würde verworfen, nachdem Sie die zurückgegebenen Rows
geschlossen haben, es sei denn, es findet ein internes Caching statt Objekt. Wenn Sie stattdessen manuell db.Prepare
aufrufen und dann die zurückgegebene db.Stmt
zwischenspeichern und wiederverwenden Sie können möglicherweise die Leistung der Abfragen verbessern, die häufig ausgeführt werden müssen.
Um zu verstehen, wie eine vorbereitete Anweisung verwendet werden kann, um die Leistung zu optimieren, können Sie einen Blick auf die offizielle Dokumentation werfen:https://www.postgresql.org/docs/current/static/sql-prepare.html