Oracle
 sql >> база данни >  >> RDS >> Oracle

Oracle:Как ефективно да избирате редове с помощта на списък с ключове

Не е добра практика да подавате стойности за IN състояние като конкатенация на низове. Първите неща, разбира се, сигурността и коректността, но следващата точка е производителността.
Всеки път, когато извикате оператора, машината на базата данни го анализира, изгражда план за заявка и след това изпълнява действия, посочени в SQL израза.
Ако изграждате текст на заявката от нулата всеки път, след което и трите етапа се изпълняват всеки път.
Но ако използвате променливи за свързване през цялото време, заявката изглежда еднаква, така че базата данни използва кеширан план на заявка, което ускорява изпълнението на заявката. Дори вие можете да извикате oci_parse() само веднъж и използвайте повторно $stmt променлива с различен набор от предоставени параметри.
Така че за най-добра производителност трябва да използвате свързваща променлива и да я попълните с масив, като използвате oci_bind_array_by_name .

Допълнително нещо е, че извличането на резултати чрез oci_fetch_all може да работи по-бързо от четенето на набор от резултати ред по ред, но зависи от логиката на обработка на резултатите.

Актуализация

Изглежда, че предаването на параметри на масив работи само ако ще изпълните PL/SQL блок и не можете да го използвате с SQL изрази. Но друга възможност е да използвате колекции за предаване на списък със стойности на параметри. Възможно е да се задоволят условията на въпроса дори с масиви, но този начин е по-малко елегантен.
Освен различни начини за запитване към база данни има нещо като системни настройки. В случай на PHP има някои параметри в php.ini файл, който контролира взаимодействието с Oracle. Един от тях ( oci8.statement_cache_size ), свързани с кеширане на заявки и производителност.

Примери

Всички примери използват една и съща настройка на данни в Oracle.
За предаване на данни избирам предварително дефиниран SYS.ODCIVarchar2List тип, но също така е възможно да се дефинира персонализиран тип със същите характеристики (демонстрирано в примера за настройка на данни). По-долу е даден код за демонстрация на настройка на схема за данни и принцип на използване на колекции в DML.

SQLFiddle

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);
?>


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да проверя дали даден масив съдържа определен низ?

  2. Как да направя ръчно деинсталиране на Oracle?

  3. GATHER_PLAN_STATISTICS не генерира основна статистика на плана

  4. Номер на ред в sql сървър като LINE в USER_SOURCE oracle

  5. PL/SQL списък, разделен със запетая; премахване на дублирания и поставяне в масив