буквальная строка работает, но переменные берутся вечно - PullRequest
0 голосов
/ 10 февраля 2010

У меня есть запрос, который работает, когда у меня есть фиксированные значения. то есть:

select
    count(*)
from
    address a
where
    a.primary_name like upper('cambourne court') and
    a.secondary_name like upper('flat 9');

Однако замените upper('flat 9') на переменную second_name:=upper('flat 9'), и теперь поиск возвращает все 111 адресов в «cambourne court».

С чего бы это?

РЕДАКТИРОВАТЬ : Это полный файл address.sql (с удаленными комментариями)

declare
    address_details address%rowtype;
    current_loc varchar2(32);

    prime_name varchar2(255);
    prime_number varchar2(255);
    second_name varchar2(255);
    street_name varchar2(255);
    town_name varchar2(255);
    success boolean;

    the_count number;
begin

prime_name:=upper('&&primary_name');
prime_number:=upper('&&primary_number');
second_name:=upper('&&secondary_name');
street_name:=upper('&&street_name');
town_name:=upper('&&town_name');


success:=true;

-- error checking here (removed for brevity)


if success then
    current_loc := 'finding address';
    select
        count(*)
    into
        the_count
    from
        dependency d,
        address a,
        street s
    where
        d.dep_obj_id1 = 2 and
        d.dep_obj_id2 = 1 and   
        a.loc_id = d.dep_id1 and
        s.loc_id = d.dep_id2 and 
        a.primary_name like prime_name and
        a.secondary_name like second_name and
        s.name like street_name and
        s.town like town_name;

end if;

dbms_output.put_line('success: address found '||the_count); 


exception 
    when too_many_rows then 
        dbms_output.put_line('failure: too many rows while '||current_loc); 
    when no_data_found then 
        dbms_output.put_line('failure: no rows found while '||current_loc); 
    when others then
        dbms_output.put_line('failure: general error while '||current_loc); 

end; 
/

Обновление : Я перезапустил SQL * Plus, который, похоже, исправил разрыв.

Замена prime_name и second_name на фактические строки означает, что код выполняется менее чем за секунду. С переменными означает, что это занимает более 2 минут.

Ответы [ 4 ]

3 голосов
/ 10 февраля 2010

Ваши симптомы соответствуют наличию переменной PL / SQL с тем же именем, что и столбец в таблице.

[Изменить] Я чувствовал себя несколько виноватым из-за того, что голос был неверным, поэтому я попытался воспроизвести и не получить ваши результаты:

SQL> select * from address
  2  ;

PRIMARY_NAME               SECONDARY_NAME
------------------------------ ------------------------------
CAMBOURNE COURT            FLAT 9
CAMBOURNE COURT            FLAT 10

SQL> declare
  2  second_name varchar2(30) := upper('flat 9');
  3  x pls_integer;
  4  cursor c is
  5  select
  6      count(*)
  7  from address a
  8  where
  9      a.primary_name like upper('cambourne court') and
 10      a.secondary_name like upper('flat 9')
 11  ;
 12  begin
 13  select count(*) into x
 14   from address a
 15  where
 16      a.primary_name like upper('cambourne court') and
 17      a.secondary_name like upper('flat 9');
 18  dbms_output.put_line('literal: '||x);
 19  select count(*) into x
 20   from address a
 21  where
 22      a.primary_name like upper('cambourne court') and
 23      a.secondary_name like second_name;
 24  dbms_output.put_line('variable: '||x);
 25  end;
 26  /
literal: 1
variable: 1

PL/SQL procedure successfully completed.
2 голосов
/ 11 февраля 2010

111 записей предполагают, что second_name не содержит ожидаемого значения; как вы захватываете &&secondary_name, и можете ли вы проверить значение, которое оно на самом деле имеет до и после пропущенного раздела проверки? Судя по результатам, он содержит «%», а не «9», но я полагаю, вы уже это проверили.

Проблема скорости предполагает, что оптимизатор меняет поведение, изменяя порядок соединения и / или используемые индексы. По умолчанию это может быть соединение каждой строки street с каждой записью address, имеющей камбурнский корт, и только после этого проверка зависимостей, но она будет немного отличаться в зависимости от того, какие индексы он использует и какие-либо характеристики которые доступны. Разница в том, что с литералами, даже если вы используете like, здесь нет подстановочных знаков, поэтому он может знать, что может использовать индекс для primary_name и / или second_name; в переменной версии он не может знать, что при разборе запроса он должен принимать худший случай, который будет равен «%». Что он может на самом деле получить, если он возвращает 111 адресов.

Без explain plan трудно точно угадать, что происходит, но вы можете попробовать добавить несколько подсказок оптимизатора, чтобы хотя бы попытаться получить правильный порядок соединения, и даже использовать индекс - хотя это, возможно, не должно остаться на месте, если вы можете когда-либо иметь значения, начинающиеся с%. Это может сказать вам, что делается по-другому.

1 голос
/ 11 февраля 2010

Алекс указал возможную причину. Таблицы индексируются, и использование «подобно» с переменной является случаем деактивации индекса. Оптимизаторы обрабатывают выражения «как» с константами, которые не имеют подстановочных знаков или заполнителей, как «=», поэтому учитываются индексы, если они присутствуют.

Отбросьте индекс по этим столбцам, и вы получите такую ​​же плохую производительность с константами или переменными. На самом деле не делайте этого, просто автоматически отслеживайте и сравнивайте планы.

С уважением,

1 голос
/ 11 февраля 2010

План объяснения может быть наводящим на размышления. После запуска найдите sql_id из v $ sql для этого statemnet

select sql_text, sql_id from v$sql where lower(sql_text) like '%address%street%';

Затем подключите это к

select * from table(dbms_xplan.display_cursor('1mmy8g93um377'));

То, что вы должны увидеть внизу, выглядит примерно так, что показывает, есть ли какие-либо странности в плане (например, использование столбца в одной из таблиц, использование функции ...).

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A"."LOC_ID"="D"."DEP_ID1" AND "S"."LOC_ID"="D"."DEP_ID2")
   4 - filter(("A"."PRIMARY_NAME" LIKE :B4 AND "A"."SECONDARY_NAME" LIKE 
              :B3))
   6 - filter(("S"."NAME" LIKE :B2 AND "S"."TOWN" LIKE :B1))
   7 - filter(("D"."DEP_OBJ_ID1"=2 AND "D"."DEP_OBJ_ID2"=1))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...