0

key主キー (と呼びましょう) とvalueフィールドを含む Oracle テーブルがあります。私の PHP アプリケーションにはキーのリストがあり、対応するすべての値をデータベースから抽出したいと考えています。次の PHP コードのようなものを使用してそれを行うことができます。

$keyList = array('apple', 'orange', 'banana');

$conn = oci_pconnect(USERNAME, PASSWORD, URI);
$stmt = oci_parse($conn, 'SELECT * FROM myTable WHERE value IN ('
                         .explode(',', $keylist)
                         .')');
oci_execute($stmt);
while($row = oci_fetch_array($stmt, OCI_ASSOC)) {
    echo "{$row['KEY']}, {$row['VALUE']}\n"; // Print the values
}

これは機能するはずですが、$keyList最大 200 個のアイテム (場合によってはそれ以上) になる可能性があります。疑問が生じます:

  • これはこれを行うための最も効率的/堅牢な方法ですか?
  • それとも、データベースから単一の値を選択し、リスト内のキーごとに 1 回実行する、ある種の準備済みステートメントを使用して実行する方がよいでしょうか?
4

2 に答える 2

4

IN条件の値を文字列連結として渡すことはお勧めできません。まず第一に、もちろんセキュリティと正確性ですが、次のポイントはパフォーマンスです。
ステートメント データベース エンジンを呼び出すたびに、それが解析され、クエリ プランが作成され、その後、SQL ステートメントで指定されたアクションが実行されます。
毎回最初からクエリ テキストを作成すると、3 つのステージすべてが毎回実行されます。
ただし、バインド変数を常に使用すると、クエリは同じように見えるため、データベースはキャッシュされたクエリ プランを使用してクエリの実行を高速化します。一度だけ呼び出して、提供されたパラメーターの異なるセットで変数をoci_parse()再利用することもできます。 したがって、最高のパフォーマンスを得るには、バインド変数を使用し、 を使用して配列で埋める必要があります。$stmt
oci_bind_array_by_name

さらに、 を使用して結果を取得すると、oci_fetch_all結果セットを行ごとに読み取るよりも高速に実行される可能性がありますが、結果を処理するロジックによって異なります。

アップデート

配列パラメーターの受け渡しは、PL/SQL ブロックを実行する場合にのみ機能し、SQL ステートメントでは使用できないようです。しかし、別の可能性は、コレクションを使用してパラメーター値のリストを渡すことです。配列でも質問の条件を満たすことは可能ですが、この方法はエレガントではありません。
データベースを照会するさまざまな方法に加えて、システム設定などがあります。PHP の場合、php.iniファイルには Oracle とのやり取りを制御するパラメータがいくつかあります。そのうちの 1 つ ( oci8.statement_cache_size) は、クエリのキャッシュとパフォーマンスに関連しています。

すべての例では、Oracle で同じデータ設定を使用しています。
データを渡すために事前定義されたSYS.ODCIVarchar2List型を選択しますが、同じ特性を持つカスタム型を定義することも可能です (データ セットアップの例で示されています)。以下は、データ スキームの設定と DML でコレクションを使用する原則を示すコードです。

SQLフィドル

create table myTable(value varchar2(100), key varchar2(100))
/

insert into myTable(value, key)
select * from (
  select 'apple', 'apple_one' from dual union all
  select 'apple', 'apple_two' from dual union all
  select 'banana', 'banana_one' from dual union all
  select 'orange', 'orange_one' from dual union all
  select 'orange', 'orange_two' from dual union all
  select 'potato', 'potato_one' from dual
)
/

create or replace type TCustomList as table of varchar2(4000)
/

create or replace package TestPackage as

  type TKeyList is table of varchar2(1000) index by binary_integer;

  function test_select(pKeyList in out TKeyList) return sys_refcursor;

end;
/

create or replace package body TestPackage is

  function test_select(pKeyList in out TKeyList) return sys_refcursor
  is               
    vParam sys.ODCIVarchar2List := sys.ODCIVarchar2List();
    vCur sys_refcursor;  
    vIdx binary_integer;
  begin                

    vIdx := pKeyList.first;
    while(vIdx is not null) loop
      vParam.Extend;
      vParam(vParam.last) := pKeyList(vIdx);
      vIdx := pKeyList.next(vIdx);
    end loop;

    open vCur for 
      select * from myTable where value in (select column_value from table(vParam))    
    ;

    return vCur;
  end;

end;
/

コレクションを示すためのクエリ:

--select by value list
select * from myTable 
where value in (
        select column_value 
        from table(Sys.ODCIVarchar2List('banana','potato'))
      )
/

--same with custom type
select * from myTable 
where value in (
        select column_value 
        from table(TCustomList('banana','potato'))
      )
/

--same with demonstration of casting 
select * from myTable 
where value in (
        select column_value 
        from table(cast(TCustomList('banana','potato') as Sys.ODCIVarchar2List))
      )
/

例 1 - コレクションを使用した PHP からの呼び出し

<?php
  $keyList = array('apple', 'potato');

  $conn = oci_pconnect("user_name", "user_password", "SERVER_TNS_NAME");

  $stmt = oci_parse($conn, "SELECT * FROM myTable where value in (select column_value from table(:key_list))");

  $coll = oci_new_collection($conn, 'ODCIVARCHAR2LIST','SYS');

  for ($i=0; $i < count($keyList); $i++) {
    $coll->append($keyList[$i]);
  }

  oci_bind_by_name($stmt, 'key_list', $coll, -1, OCI_B_NTY);

  oci_execute($stmt);

  while($row = oci_fetch_array($stmt, OCI_ASSOC)) {
      echo "{$row['KEY']}, {$row['VALUE']}\n"; // Print the values
  }
  echo "---\n";

  $coll->free();

  //-- Run statement another time with different parameters
  //-- without reparsing.

  $coll = oci_new_collection($conn, 'ODCIVARCHAR2LIST','SYS');
  $coll->append('banana');
  oci_bind_by_name($stmt, 'key_list', $coll, -1, OCI_B_NTY);

  oci_execute($stmt);

  while($row = oci_fetch_array($stmt, OCI_ASSOC)) {
      echo "{$row['KEY']}, {$row['VALUE']}\n"; // Print the values
  }
  echo "---\n";

  $coll->free();

  oci_free_statement($stmt);
  oci_close($conn);
?>

例 2 - 配列とパッケージを使用した PHP からの呼び出し

<?php
  $keyList = array('apple', 'potato');

  $conn = oci_pconnect("user_name", "user_password", "SERVER_TNS_NAME");

  $stmt = oci_parse($conn, "begin :cur := TestPackage.test_select(:key_list); end;");

  $curs = oci_new_cursor($conn);

  oci_bind_array_by_name($stmt, "key_list", $keyList, 2, 100, SQLT_CHR);
  oci_bind_by_name($stmt, "cur", $curs, -1, OCI_B_CURSOR);

  oci_execute($stmt);
  oci_execute($curs);

  while($row = oci_fetch_array($curs, OCI_ASSOC)) {
      echo "{$row['KEY']}, {$row['VALUE']}\n"; // Print the values
  }
  echo "---\n";


  //-- Run statement another time with different parameters
  //-- without reparsing.

  $keyList = array('banana');

  oci_bind_array_by_name($stmt, "key_list", $keyList, 2, 100, SQLT_CHR);

  oci_execute($stmt);
  oci_execute($curs);

  while($row = oci_fetch_array($curs, OCI_ASSOC)) {
      echo "{$row['KEY']}, {$row['VALUE']}\n"; // Print the values
  }
  echo "---\n";

  oci_free_statement($stmt);
  oci_close($conn);
?>

例 3 - 配列と無名ブロックを使用した PHP からの呼び出し

<?php
  $keyList = array('apple', 'potato');

  $conn = oci_pconnect("user_name", "user_password", "SERVER_TNS_NAME");

  $stmt = oci_parse($conn, "
    declare
      type TKeyList is table of varchar2(4000) index by binary_integer;

      pKeyList TKeyList := :key_list;
      vParam   sys.ODCIVarchar2List := sys.ODCIVarchar2List();
      vIdx     binary_integer;
    begin

      -- Copy PL/SQL array to a type which allowed in SQL context
      vIdx := pKeyList.first;
      while(vIdx is not null) loop
        vParam.Extend;
        vParam(vParam.last) := pKeyList(vIdx);
        vIdx := pKeyList.next(vIdx);
      end loop;

      open :cur for select * from myTable where value in (select column_value from table(vParam));
    end;
  ");

  $curs = oci_new_cursor($conn);

  oci_bind_array_by_name($stmt, "key_list", $keyList, 2, 100, SQLT_CHR);
  oci_bind_by_name($stmt, "cur", $curs, -1, OCI_B_CURSOR);

  oci_execute($stmt);
  oci_execute($curs);

  while($row = oci_fetch_array($curs, OCI_ASSOC)) {
      echo "{$row['KEY']}, {$row['VALUE']}\n"; // Print the values
  }
  echo "---\n";


  //-- Run statement another time with different parameters
  //-- without reparsing.

  $keyList = array('banana');

  oci_bind_array_by_name($stmt, "key_list", $keyList, 2, 100, SQLT_CHR);

  oci_execute($stmt);
  oci_execute($curs);

  while($row = oci_fetch_array($curs, OCI_ASSOC)) {
      echo "{$row['KEY']}, {$row['VALUE']}\n"; // Print the values
  }
  echo "---\n";

  oci_free_statement($stmt);
  oci_close($conn);
?>
于 2013-08-06T22:49:26.407 に答える
2

そもそもOracleからのものでない限り、おそらくあなたのアプローチが最も効率的な方法ですkeyList(たとえば、それが別のクエリの結果である場合は、それらのクエリを組み合わせたいと思うでしょう)。

このようなラウンドトリップは非常にコストがかかる可能性があるため、リストのキーごとに 1 回実行することは絶対にありません。200 項目のリストについては、実験する必要がありますが、問題にはならないと思います。

もう 1 つの非常に重要なポイント: 必ずデータをサニタイズしてkeyListください。そうしないと、アプリが SQL インジェクションにさらされる可能性があります。

于 2013-08-06T18:04:07.740 に答える