web-dev-qa-db-ja.com

Pythonでの準備されたステートメントとパラメータ化されたクエリの混同

私が理解している限り、準備されたステートメントは(主に)データベースの機能であり、このようなパラメーターを使用するコードからパラメーターを分離することができます。例:

PREPARE fooplan (int, text, bool, numeric) AS
    INSERT INTO foo VALUES($1, $2, $3, $4);
EXECUTE fooplan(1, 'Hunter Valley', 't', 200.00);

パラメータ化されたクエリは手動の文字列補間を代用するので、

cursor.execute("SELECT FROM tablename WHERE fieldname = %s" % value)

我々はできる

cursor.execute("SELECT FROM tablename WHERE fieldname = %s", [value])

現在、準備されたステートメントは、ほとんどの場合、データベース言語で使用されており、パラメーター化されたクエリは主にデータベースに接続するプログラミング言語で使用されているようですが、この規則には例外があります。

問題は、準備されたステートメントとパラメーター化されたクエリの違いについて尋ねると、多くの混乱が生じることです。彼らの目的は確かに同じですが、その方法論は異なっているようです。しかし、 sources があり、両方が同じであることを示しています。 MySQLdbとPsycopg2はパラメーター化されたクエリをサポートしているようですが、準備されたステートメントをサポートしていません(例:MySQLdbの here および postgresドライバーのTODOリスト または この答え sqlalchemyグループ)。実際には、準備済みステートメントをサポートするpsycopg2カーソルを実装する Gist と、それに関する最小限の 説明 があります。準備されたステートメントを手動で提供するために、psycopg2にカーソルオブジェクトをサブクラス化する suggestion もあります。

次の質問に対する信頼できる回答を希望します。

  • 準備されたステートメントとパラメーター化されたクエリの間に意味のある違いはありますか?これは実際には重要ですか?パラメーター化されたクエリを使用する場合、準備されたステートメントについて心配する必要がありますか?

  • 違いがある場合、Pythonエコシステムの準備済みステートメントの現在のステータスはどれですか?どのデータベースアダプターが準備済みステートメントをサポートしていますか?

22
Robert Smith
  • 準備済みステートメント:データベースで事前に解釈されたクエリルーチンへの参照。パラメーターを受け入れる準備ができています。

  • パラメータ化されたクエリ:alongsideプレースホルダー値を持つSQL(通常は?または%sまたはそのフレーバー)で値を渡すようにコードによって作成されたクエリ。

ここでの混乱は、準備されたステートメントオブジェクトを直接取得する機能と、値を「パラメーター化されたクエリ」メソッドに渡す機能との間の区別が(見かけ上)欠けていることに起因すると思われます。 、または少なくともあなたのために作ります。

たとえば、SQLite3ライブラリのCインターフェースには 準備されたステートメントオブジェクト を操作するための多くのツールがありますが、 Python api はそれらについてほとんど言及していません。ステートメントを準備して、いつでも何度でも使用することはできません。代わりに、SQLコードを取得するsqlite3.executemany(sql, params)を使用して、準備済みステートメント内部的にを作成し、ループ内でそのステートメントを使用して、指定したイテラブル内の各パラメータータプルを処理できます。

Pythonの他の多くのSQLライブラリは同じように動作します。準備されたステートメントオブジェクトを操作することは大変な作業であり、あいまいさを招く可能性があります。Pythonのような言語では、明快さを重視し、生の実行速度を容易にするので、実際には最大のオプションではありません。 。基本的に、毎回再解釈される複雑なSQLクエリに対して数十万または数百万の呼び出しを行わなければならない場合、おそらく別の方法で行う必要があります。とにかく、データベースサーバーの周りで同じ準備済みステートメントを保持する場合、同じSQLコードを何度も解釈し続ける必要がないため、これらのオブジェクトに直接アクセスできることを望む人もいます。ほとんどの場合、これは間違った方向から問題に取り組み、他の場所で、またはコードを再構築することで、大幅に節約できます。*

おそらくより一般的に重要なのは、準備されたステートメントとパラメーター化されたクエリがデータを健全に保ち、SQLコードから分離する方法です。 これは文字列のフォーマットよりも非常に好ましいです!パラメータ化されたクエリと準備されたステートメントは、何らかの形でアプリケーションからデータベースに変数データを渡す唯一の方法。それ以外の方法でSQLステートメントを作成しようとすると、実行速度が大幅に低下するだけでなく、 その他の問題 に対して脆弱になります。

*たとえば、DBにフィードするデータをジェネレーター関数で作成し、executemany()を使用して、execute()を呼び出すのではなく、ジェネレーターから一度にすべて挿入します。ループするたびに。

tl; dr

パラメーター化されたクエリは、準備されたステートメントを内部で生成し、パラメーターを渡して実行する単一の操作です

編集:多くの人がこの答えを目にしています!また、多くのデータベースエンジンには、プレーンテキストクエリ構文で明示的に構築でき、クライアントのセッションの存続期間にわたって再利用できる(たとえば postgres で)準備済みステートメントの概念があることも明確にしたいと思います。さらに時間を節約するために、クエリプランをキャッシュするかどうかを制御できる場合があります。一部のフレームワークはこれらを自動的に使用します(RailsのORMが積極的に実行するのを見てきました)。これは、準備されているクエリのフォームの順列がある場合に役立つ場合もあれば、不利益になる場合もあります。

また、選択を限定したい場合、パラメータ化されたクエリではalwaysは準備されたステートメントを内部で使用しません。可能な場合はそうする必要がありますが、場合によっては、単にパラメーター値をフォーマットするだけです。ここでの「準備されたステートメント」と「パラメータ化されたクエリ」の本当の違いは、実際に使用するAPIの形状だけです。

19
Mumbleskates

最初に、あなたの質問は非常に良い準備を示しています-よくできました。

私が権威ある答えを提供する人であるかどうかはわかりませんが、状況の理解について説明しようと思います。

準備済みステートメントは、PREPAREステートメントの結果としてデータベースサーバーの側で作成されたオブジェクトであり、指定されたSQLステートメントをパラメーター付きの一時的なプロシージャに変換します。準備されたステートメントには現在のデータベースセッションの存続期間があり、セッションが終了すると破棄されます。 SQLステートメントDEALOCATEを使用すると、準備されたステートメントを明示的に破棄できます。

データベースクライアントは、SQLステートメントEXECUTEを使用して、その名前とパラメーターを呼び出すことにより、準備されたステートメントを実行できます。

パラメーター化されたステートメントは、通常のように準備されたステートメントのエイリアスです。準備されたステートメントにはいくつかのパラメーターがあります。

パラメータ化されたクエリは、同じように使用される頻度が低いようです(パラメータ化されたステートメントで24ミルのGoogleヒット、パラメータ化されたクエリで14ミルのヒット)。一部の人々はこの用語を別の目的に使用する可能性があります。

準備済みステートメントの利点は次のとおりです。

  • 実際の準備されたステートメント呼び出しの実行の高速化(PREPAREの時間をカウントしない)
  • sQLインジェクション攻撃への耐性

SQLクエリを実行するプレイヤー

実際のアプリケーションには、おそらく以下の参加者がいます。

  • アプリケーションコード
  • ORMパッケージ(例:sqlalchemy)
  • データベースドライバー
  • データベースサーバー

アプリケーションの観点からは、コードが実際にデータベースサーバーで準備済みステートメントを使用するかどうかは簡単ではありません参加者のいずれかが準備済みステートメントのサポートを欠いている可能性があります

結論

アプリケーションコード内 SQLインジェクション攻撃を受けやすいため、SQLクエリの直接シェーピングを防止します。このため、ORMコードを最適化してこの種の攻撃を防ぐことができるため、データベースサーバー側で準備済みステートメントを使用しなくても、ORMがパラメーター化クエリに提供するものをすべて使用することをお勧めします。

準備されたステートメントがパフォーマンス上の理由で価値があるかどうかを決定します。数回しか実行されない単純なSQLクエリがある場合は、役に立ちません。実行が少し遅くなることもあります。

複雑なクエリが何度も実行され、実行時間が比較的短い場合が最大の効果です。このような場合、次の手順を実行できます。

  • 使用するデータベースがPREPAREステートメントをサポートしていることを確認してください。ほとんどの場合、存在します。
  • 使用するドライブが準備済みステートメントをサポートしていることを確認してください。サポートしていない場合は、それをサポートしている別のドライブを見つけてください。
  • ORMパッケージレベルでこの機能のサポートを確認してください。ドライバーによってドライバーによって異なる場合があります(例:sqlalchemyは、MySQLの管理方法により、MySQLで準備されたステートメントにいくつかの制限があることを示しています).

あなたが本当の権威ある答えを探しているなら、私はsqlalchemyの著者に向かいます。

4
Jan Vlcinsky

SQLステートメントはすぐには実行できません。DBMSは実行前にそれらを解釈する必要があります。

準備されたステートメントは、すでに解釈されたステートメントであり、DBMSはパラメーターを変更し、クエリはすぐに開始されます。これは特定のDBMSの機能であり、高速な応答を実現できます(ストアドプロシージャと同等)。

パラメータ化されたステートメントは、プログラミング言語でクエリ文字列を作成する方法にすぎません。 SQL文字列がどのように形成されるかは問題ではないため、DBMSによる応答が遅くなります。

同じクエリを3〜4回実行して時間を測定すると(異なる条件で選択)、パラメーター化されたクエリで同じ時間が表示され、準備されたステートメントの2回目の実行(DBMSがスクリプトを解釈する必要がある最初の時間)に比べて時間が短くなります。とにかく)。

0
Fil