5

バックグラウンド

こんにちは、私はPHPとMySQLで実験/教育ツールを開発しています。私はSQLを初めて使用しますが、最初から正しい方法で物事をやりたいと思っています。私はすべての変数置換にPDOプリペアドステートメントを使用しており、可能な限りバックティックを使用しています(したがって、私が理解しているように、MySQL以外のデータベースには移植できません)。私の問題については、どうすればいいのか考えていますが、実装に数時間かかるので(SQLの構文も初めてです)、その間、最初に質問を作成したいと思いました。誰かが「これはそれをする方法ではありません!」と叫ぶことができる場合に備えて。何時間も節約できます。

問題

ユーザーがドロップダウンメニューから選択するインターフェイスを作成したいと思います。

  1. テーブルA
  2. そのテーブルの1つ以上のフィールド(例:A.xおよびA.y
  3. テーブルB
  4. そのテーブルの1つ以上のフィールド(例:B.zおよびB.y

送信すると、コードは内部結合を実行し、各フィールドにそれぞれ一致します。たとえばA.x = B.zA.y = B.yなど、一致したすべての行を返します。

私の計画は、INNER JOINSQLステートメントを生成し、フィールドをループしてプレースホルダー(?)を挿入し、それぞれのパラメーターをバインドして、最後にステートメントを実行することです。

これを行う簡単な方法はありますか?これを行うためのより良い方法はありますか?これはどういうわけか悪用可能ですか?

事前にどうもありがとうございました。私が終了するまでに誰も応答しない場合(疑わしい)、私は解決策を投稿します。

その他

検証すると仮定します

  1. Aユーザーがとの間で同数のフィールドを選択することB
  2. フィールドとテーブルが存在すること、

また、フィールド名は同一である必要はありません。順番に一致します。(私が気付いていないかもしれない他の詳細を指摘してください!)

最終的には、これらの選択を「設定」テーブル自体に保存することが目標です。実際、ユーザーは戻ってくるたびに見たい「ビュー」を作成します。

4

2 に答える 2

2

あなたは非常に正しいことをしているので、私はあなたが何か間違ったことをしていると指摘して罪悪感を感じます!:)

プリペアドステートメントを使用してフィールド値をパラメータ化することのみが可能であり、列名やテーブル名などのSQL識別子は使用できません。したがって、プリペアドステートメントパラメータを使用してなどを基準A.xB.z渡すことはできませ。代わりに、ひどく間違っていると感じることを実行し、それらをSQL文字列に直接連結する必要があります。JOIN

ただし、すべてが失われるわけではありません。漠然とした好みの順序で、次のことができます。

  1. オプションリストをユーザーに提示します。このリストから、後でSQLを再構築します。

    <select name="join_a">
      <option value="1">x</option>
      <option value="2">y</option>
    </select>
    <select name="join_b">
      <option value="1">z</option>
      <option value="2">y</option>
    </select>
    

    次に、フォームハンドラー:

    switch ($_POST['join_a']) {
      case 1:  $acol = 'x'; break;
      case 2:  $acol = 'y'; break;
      default: die('Invalid input');
    }
    switch ($_POST['join_b']) {
      case 1:  $bcol = 'z'; break;
      case 2:  $bcol = 'y'; break;
      default: die('Invalid input');
    }
    
    $sql .= "FROM A JOIN B ON A.$acol = B.$bcol";
    

    このアプローチには、PHPを危険にさらす以外に(この場合、SQLインジェクションよりもはるかに大きな懸念があります)、任意のSQLがRDBMSに侵入できないという利点があります。

  2. ユーザー入力が期待値の1つと一致することを確認します。

    <select name="join_a">
      <option>x</option>
      <option>y</option>
    </select>
    <select name="join_b">
      <option>z</option>
      <option>y</option>
    </select>
    

    次に、フォームハンドラー:

    if (!in_array($_POST['join_a'], ['x', 'y'])
     or !in_array($_POST['join_b'], ['z', 'y']))
       die('Invalid input');
    
    $sql .= "FROM A JOIN B ON A.$_POST[join_a] = B.$_POST[join_b]";
    

    このアプローチは、安全性のためにPHPの関数に依存していますin_array(また、基になる列名をユーザーに公開しますが、アプリケーションを考えると、それが問題になるとは思えません)。

  3. 次のような入力クレンジングを実行します。

    mb_regex_encoding($charset); // charset of database connection
    $sql .= 'FROM A JOIN B ON A.`' . mb_ereg_replace('`', '``', $_POST['join_a']) . '`'
                        . ' = B.`' . mb_ereg_replace('`', '``', $_POST['join_b']) . '`'
    

    ここではユーザー入力を引用し、その引用から逃れるためのユーザーによる試みを置き換えますが、このアプローチは、あらゆる種類の欠陥や脆弱性(PHPのmb_ereg_replace関数または引用符で囲まれた識別子内の特別に細工された文字列のMySQLの処理)でいっぱいになる可能性があります。

    上記の方法のいずれかを使用して、ユーザー定義の文字列をSQLに完全に挿入しないようにすることは、可能であればはるかに優れています

于 2012-06-07T06:09:11.520 に答える
1

ユーザー入力がテーブルとフィールドの選択のみに制限されている(つまり、追加の条件がない)と仮定すると、アプローチは問題ないはずです。興味深いですね :)

追加したいのは、特定の結合が他の結合よりも優れているということです。たとえば、主キー(または他のインデックス)を使用して2つのテーブルを結合すると、全表スキャンが必要な2つの無関係な列よりもパフォーマンスが向上します。

これはすべて、そもそもテーブルの大きさに依存します。数千レコード未満の場合は、大丈夫です。真剣な熟考を超えた何かが整っています:)

于 2012-06-07T06:06:48.760 に答える