Ако използвате точни заявки от въпроса, тогава първият вариант, разбира се, е по-бавен, защото трябва да преброи всички записи в таблицата, които отговарят на критериите.
Трябва да се напише като
SELECT COUNT(*) INTO row_count FROM foo WHERE bar =123 и rownum =1;
или
изберете 1 в row_count от dual, където съществува (изберете 1 от foo, където bar =123);
защото проверката за съществуване на запис е достатъчна за вашата цел.
Разбира се, и двата варианта не гарантират, че някой друг няма да промени нещо в foo
между две изявления, но не е проблем, ако тази проверка е част от по-сложен сценарий. Просто помислете за ситуация, когато някой промени стойността на foo.a
след като изберете стойността му в var
докато изпълнява някои действия, които препращат към избрания var
стойност. Така че в сложни сценарии е по-добре да се справят такива проблеми с паралелността на ниво логика на приложението.
За извършване на атомарни операции е по-добре да използвате един SQL оператор.
Всеки от вариантите по-горе изисква 2 контекстни превключвания между SQL и PL/SQL и 2 заявки, така че работи по-бавно от който и да е вариант, описан по-долу, в случаите, когато е намерен ред в таблица.
Има и други варианти за проверка на съществуването на ред без изключение:
изберете max(a), count(1) в var, row_count от foo където bar =123 и rownum <3;
Ако row_count =1, тогава само един ред отговаря на критериите.
Понякога е достатъчно да проверите само за съществуване поради уникалното ограничение на foo
което гарантира, че няма дублирани ленти
стойности в foo
. напр. лента
е първичен ключ.
В такива случаи е възможно да се опрости заявката:
изберете max(a) в var from foo where bar =123;if(var is not null) then ...end if;
или използвайте курсора за обработка на стойности:
for cValueA in ( select a from foo where bar =123) loop ... end loop;
Следващият вариант е от връзка , предоставен от @user272735 в неговия отговор:
изберете (изберете a от foo където bar =123) в var от dual;
От моя опит всеки вариант без изключение блокира в повечето случаи по-бързо от вариант с изключения, но ако броят на изпълненията на такъв блок е малък, тогава е по-добре да се използва блок с изключения с обработка на no_data_found
и too_many_rows
изключения за подобряване на четливостта на кода.
Правилният момент, за да изберете да използвате изключение или да не го използвате, е да зададете въпрос „Тази ситуация нормална ли е за приложението?“. Ако редът не е намерен и това е очаквана ситуация, която може да бъде обработена (напр. добавяне на нов ред или вземане на данни от друго място и т.н.), е по-добре да се избягва изключение. Ако е неочаквано и няма начин да коригирате ситуация, тогава уловете изключение, за да персонализирате съобщението за грешка, запишете го в регистъра на събитията и го хвърлете отново или просто не го прихващайте изобщо.
За да сравните производителността, просто направете прост тестов случай на вашата система, като двата варианта се извикват многократно и сравняват.
Нещо повече, в 90 процента от приложенията този въпрос е повече теоретичен, отколкото практически, защото има много други източници на производителност въпроси, които първо трябва да се вземат предвид.
Актуализация
Възпроизвеждах пример от тази страница
на сайта на SQLFiddle с малки корекции (връзка
).
Резултатите доказват този вариант с избор от dual
се представя най-добре:малко претоварване, когато повечето от заявките са успешни, и най-ниско влошаване на производителността, когато броят на липсващите редове се увеличи.
Изненадващо вариантът с count() и две заявки показа най-добър резултат в случай, че всички заявки са неуспешни.
По-долу е даден код за настройка за тестова среда и тестов скрипт.
създайте таблица t_test(a, b)asselect ниво,ниво от двойно свързване по ниво<=1e5/вмъкнете в t_test(a,b) изберете null, ниво от двойно свързване по ниво <100/създайте уникален индекс x_text на t_test(a)/създайте времена на таблица(fname varchar2(10), loop_count number, exec_time number)/create table params(pstart number, pend number)/-- loop boundsinsert into params(pstart, pend) values(1, 2000)/
-- f1 - обработка на изключения
създаване или замяна на функция f1(p в число) връщане на число като res число;започнете избиране на b в res от t_test t където t.a=p и rownum =1; return res;exception when no_data_found then return null;end;/
-- f2 - цикъл на курсора
създаване или замяна на функция f2(p в число) връщане на число като res число; начало за rec в (изберете b от t_test t, където t.a=p и rownum =1) цикъл res:=rec.b; крайна линия; връща res;end;/
-- f3 - max()
създайте или заменете функция f3(p в число) връщане на число като res число;започнете избиране на max(b) в res от t_test t където t.a=p и rownum =1; връща res;end;/
-- f4 - изберете като поле в изберете от двойно
създайте или заменете функция f4(p в число) връщане на число като res число;започнете избиране (изберете b от t_test t, където t.a=p и rownum =1) в res от двоен; връща res;end;/
-- f5 - проверете count(), след което вземете стойност
създайте или заменете функция f5(p в число) връщане на число като res число; cnt номер; започва избор на count(*) в cnt от t_test t, където t.a=p и rownum =1; if(cnt =1) след това изберете b в res от t_test t, където t.a=p; край ако; връща res;end;/
Тестови скрипт:
декларирайте v цяло число; v_start цяло число; v_end цяло число; vStartTime номер; начало изберете pstart, изчакайте във v_start, v_end от параметри; vStartTime :=dbms_utility.get_cpu_time; for i in v_start .. v_end цикъл v:=f1(i); крайна линия; вмъкнете в стойности на timings(fname, loop_count, exec_time) ('f1', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100);end;/декларирайте v цяло число; v_start цяло число; v_end цяло число; vStartTime номер; начало изберете pstart, изчакайте във v_start, v_end от параметри; vStartTime :=dbms_utility.get_cpu_time; for i in v_start .. v_end цикъл v:=f2(i); крайна линия; вмъкнете в стойности на timings(fname, loop_count, exec_time) ('f2', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100);end;/декларирайте v цяло число; v_start цяло число; v_end цяло число; vStartTime номер; начало изберете pstart, изчакайте във v_start, v_end от параметри; vStartTime :=dbms_utility.get_cpu_time; for i in v_start .. v_end цикъл v:=f3(i); крайна линия; вмъкнете в стойности на timings(fname, loop_count, exec_time) ('f3', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100);end;/декларирайте v цяло число; v_start цяло число; v_end цяло число; vStartTime номер; начало изберете pstart, изчакайте във v_start, v_end от параметри; vStartTime :=dbms_utility.get_cpu_time; for i in v_start .. v_end цикъл v:=f4(i); крайна линия; вмъкнете в стойности на timings(fname, loop_count, exec_time) ('f4', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100);end;/декларирайте v цяло число; v_start цяло число; v_end цяло число; vStartTime номер; начало изберете pstart, изчакайте във v_start, v_end от параметри; --v_end :=v_start + trunc((v_end-v_start)*2/3); vStartTime :=dbms_utility.get_cpu_time; for i in v_start .. v_end цикъл v:=f5(i); крайна линия; вмъкване в стойности на timings(fname, loop_count, exec_time) ('f5', v_end-v_start+1, (dbms_utility.get_cpu_time - vStartTime)/100);end;/изберете * от реда на времената по fname/