web-dev-qa-db-ja.com

golang sqlドライバーのprepareステートメント

GolangのSQLドライバーに関して、以下の2つのステートメントの違いは何ですか?

// store.DB is *sql.DB type
rows, err := store.DB.Query(SQL, args ...)
// err != nil
defer rows.Close()

そして

// store.DB is *sql.DB type
stmt, err := store.DB.Prepare(SQL)
// err != nil
defer stmt.Close()

rows, err := stmt.Query(args ...)
// err != nil
defer rows.Close()

彼らは同じだと思いますか?微妙な違いはありますか?

更新

多くのことを実行する必要はありませんstmt.Execまたはstmt.Querydb.Prepare、各execの後に実行されるのは、1つのqueryまたはprepareだけです。そして、db.Queryまたはdb.Exec、生のSQL文字列を使用する代わりに、メソッドに引数を渡します(セキュリティ上の理由から)。

1つの参照リンクを見つけました: http://go-database-sql.org/prepared.html
両方の方法が準備されたステートメントを使用しているようです、違いは何ですか?

18
jtuki

違いは微妙な場合があり、重要な場合もあれば、事実上存在しない場合もあります。

一般に、準備済みステートメント1.サーバーで準備され(SQL解析、実行プランが生成されるなど)、2。追加のパラメーターで実行され、次に3.が閉じられます。毎回異なるパラメーターを渡して同じSQLを再利用できるため、SQLインジェクションを防ぎ、パフォーマンスの向上(ドライバー/プロトコル固有、YMMV)を提供し、実行プランの生成やSQLの解析と同様に、ステップの繰り返しを防ぐことができます。上記のprepareステップ。

ソースコードを書いている人にとっては、準備されたステートメントは文字列を連結してDBサーバーに送信するよりも便利かもしれません。

DB.Query()メソッドはSQLを文字列として受け取り、0個以上の引数をとります(Exec()、またはQueryRow()と同様)。追加の引数のないSQL文字列は、あなたが書いたものを正確にクエリします。ただし、プレースホルダーと追加の引数を含むSQL文字列が提供されると、準備されたステートメントが内部で実行されます。

DB.Prepare()メソッドは、準備されたステートメントを明示的に実行し、stmt.Exec(...args)のように引数を渡します。

2つの違いと、どちらか一方を使用する理由について、検討する価値のあることがいくつかあります。

DB.Query()は引数なしで使用できます。これは、準備されたステートメントが必ず通過するprepare-> execute-> closeシーケンスをバイパスできるため、非常に効率的です。

追加の引数、およびクエリ文字列のプレースホルダーと共に使用することもでき、前述のように準備されたステートメントを内部で実行します。ここでの潜在的な問題は、多数のクエリを作成しているときに、それぞれが内部で準備されたステートメントを生成することです。追加の手順が含まれるため、クエリを実行するたびに準備、実行、終了するため、これはかなり非効率的です。

明示的に準備されたステートメントを使用すると、潜在的に異なる引数を使用して、以前に準備したSQLを再利用しようとしているため、その非効率を回避できる可能性があります。

しかし、これは必ずしも期待どおりに機能するとは限りません... db/sqlによって管理される基礎となる接続プールのため、「データベース接続」は非常に仮想的です。 DB.Prepare()メソッドは、特定の接続に対してステートメントを準備し、実行時に同じ接続を取得しようとしますが、その接続が利用できない場合は、利用可能な接続を取得して再接続します。それに対して準備して実行します。同じ準備済みステートメントを繰り返し使用している場合は、無意識のうちに、何度も繰り返し準備している可能性があります。これは明らかに、大量のトラフィックを処理しているときに明らかになります。

したがって、どの状況でどのユーザーを使用するかは、特定のユースケースに依存しますが、上記の詳細が、それぞれのケースで最良の決定を行うことができるように十分に明らかになることを願っています。

更新

OPでの更新を考えると、クエリを1回だけ実行する必要がある場合、引数付きのクエリはバックグラウンドで準備されたステートメントとして実行されるため、基本的に違いはありません。

直接的な方法を使用します。 DB.Query()とその類似物。準備されたステートメントを明示的に使用する場合と比べると、ソースコードが多少単純になるためです。

この場合、準備されたステートメントはセキュリティ上の理由で利用されているため、パフォーマンスを向上させるために、他の方法でセキュリティの問題を処理し、代わりにプレーンテキストクエリを使用することは価値があります。ただし、サーバーの負荷を軽くする必要があるほど十分なトラフィックがない場合(またはトラフィックが将来大幅に増加すると予測される場合)を除いて、利益は関係ない場合があります。繰り返しになりますが、これは実際の使用例に起因します。

準備されたステートメントと直接のプレーンテキストクエリの違いに関するいくつかのメトリックに興味がある人には、良い記事 here があります(これも上記の多くを説明する優れた仕事をします)。

28
Snowman

使用しているドライバー、およびDBソフトウェアが準備されていないクエリをサポートしているかどうかによって、多少異なります。

PrepareはDBに接続し、そのDB接続にバインドされた準備済みステートメントを作成します。実行されると、その接続がまだ使用可能かどうかをチェックし、使用できない場合は新しい接続を作成し、関数を再準備してから実行します。

編集:一度に1つのルーチンしかDBに接続しておらず、同じクエリを異なる引数で繰り返し実行している場合、Prepareは、 DB側のクエリ。ただし、他のルーチンによる接続ハイジャックはこれを無効にします。クエリを1回しか使用しない場合は、Prepareを使用してもまったくメリットがありません。

Queryは、ドライバーに応じて2つのことのいずれかを行います。 ConnタイプのドライバーがOpen()から返す場合、Query()メソッドもあり、直接クエリがサポートされている場合、sql.Query()はそれを直接呼び出します。結果を返します。 ConnnotQuery()メソッドを持っている場合、sql.Query()はステートメントを準備してから実行します。

例として、PostgreSQLのpqドライバー(github.com/lib/pq)にはQuery()メソッドがあります。このメソッドは、sqlパッケージと同じように準備して実行しますが、代わりの方法もあります。クエリに引数がない場合は、simpleQueryインターフェイスを使用して実行します。 実質的に準備および実行サイクルよりも高速です。

0
Kaedys