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

Използването на SELECT COUNT(*) преди SELECT INTO по-бавно ли е от използването на Exceptions?

Ако използвате точни заявки от въпроса, тогава първият вариант, разбира се, е по-бавен, защото трябва да преброи всички записи в таблицата, които отговарят на критериите.

Трябва да се напише като

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() и две заявки показа най-добър резултат в случай, че всички заявки са неуспешни.

<предварителен код>| FNAME | LOOP_COUNT | ВСИЧКИ_НЕУСПЕШНИ | ВСИЧКИ_УСПЕХ | име на вариант |---------------------------------------------------------- ------------------| f1 | 2000 | 2.09 | 0,28 | изключение || f2 | 2000 | 0,31 | 0,38 | курсор || f3 | 2000 | 0,26 | 0,27 | max() || f4 | 2000 | 0,23 | 0,28 | двойно || f5 | 2000 | 0,22 | 0,58 | count() |-- FNAME - име на тестваната функция -- LOOP_COUNT - брой цикли в едно тестово изпълнение-- ALL_FAILED - време в секунди, ако всички тествани редове са пропуснати от таблицата-- ALL_SUCCEED - време в секунди, ако всички тествани редове са намерени в таблицата -- име на вариант - кратко име на тествания вариант

По-долу е даден код за настройка за тестова среда и тестов скрипт.

създайте таблица 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/ 


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

  2. Запитване за Oracle TIMESTAMP С ЧАСОВА ЗОНА

  3. Добавете пореден индикатор към дата в Oracle

  4. Изпълнение на Oracle JDBC на ResultSet

  5. ORA-30926:не може да се получи стабилен набор от редове в изходните таблици при обединяване на таблици