web-dev-qa-db-ja.com

なぜGolangで* DB.exec()または準備されたステートメントを使用するのですか?

GolangをPostgresqlで使用しています。

here 行を返さない操作(挿入、削除、更新)にはexec()を使用する必要がある

関数名にクエリが含まれている場合、データベースに質問するように設計されており、空であっても行のセットを返します。行を返さないステートメントでは、クエリ関数を使用しないでください。 Exec()を使用する必要があります。

次に、 ここ

Goは、カバーの下で準備さ​​れたステートメントを作成します。たとえば、単純なdb.Query(sql、param1、param2)は、sqlを準備し、それをパラメーターで実行し、最後にステートメントを閉じることで機能します。

query()がカバーされたステートメントの下で使用する場合なぜ準備されたステートメントを使用する必要がありますか?

8
CommonSenseCode

「db.Exec()を使用する理由」:

db.Execdb.Queryを同じように使用して同じsqlステートメントを実行できるのは事実ですが、2つのメソッドは異なるタイプの結果を返します。ドライバーによって実装される場合、db.Execから返された結果は、クエリによって影響を受けた行数を示すことができますが、db.Queryは代わりに行オブジェクトを返します。

たとえば、DELETEステートメントを実行し、それによって削除された行数を知りたいとします。適切な方法でそれを行うことができます:

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)

または、より冗長で客観的にコストの高い方法:

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)

Postgres CTE、SELECT COUNTdb.QueryRow、およびrow.Scanの組み合わせでこれを行うことができる3番目の方法がありますが、どのように不合理なアプローチを示すために例が必要であるとは思わないdb.Execと比較した場合。

db.Exec over db.Queryを使用するもう1つの理由は、返された結果を気にせず、必要なのはクエリを実行してエラーの有無を確認することだけである場合です。そのような場合、これを行うことができます:

if _, err := db.Exec(`<my_sql_query>`); err != nil {
    panic(err)
}

一方、これはできません(できますが、できません)。

if _, err := db.Query(`<my_sql_query>`); err != nil {
    panic(err)
}

これを行うと、しばらくすると、プログラムはtoo many connections openに類似したエラーを示すパニックに陥ります。これは、最初に必須のClose呼び出しを行わずに、返されたdb.Rows値を破棄するため、開いている接続の数が増え、最終的にサーバーの制限に達するためです。


「またはGolangでステートメントを準備しましたか?」:

あなたが引用した本は正しいとは思わない。少なくとも私にとっては、db.Query呼び出しが使用するドライバーに依存するたびに新しい準備されたステートメントを作成するかどうかのように見えます。

たとえば、queryDCdb.Queryによって呼び出される非エクスポートメソッド)の次の2つのセクションを参照してください: 準備済みステートメントなし および 準備済みステートメント付き

ブックが正しいかどうかに関係なく、db.Stmtによって作成されたdb.Queryは、内部キャッシュが行われていない限り、返されたRowsオブジェクトを閉じた後に破棄されます。代わりに手動でdb.Prepareを呼び出してから、返されたdb.Stmtをキャッシュして再利用すると、頻繁に実行する必要があるクエリのパフォーマンスを潜在的に改善できます。

準備済みステートメントを使用してパフォーマンスを最適化する方法を理解するには、公式ドキュメントをご覧ください: https://www.postgresql.org/docs/current/static/sql-prepare.html

19
mkopriva