Дългогодишна дискусия във форумите и дискусионните групи на Oracle е ефективността на използването на count(*) за връщане на брой редове от дадена таблица. Нова бръчка в тази дискусия сега въвежда count(rowid) като по-ефективна алтернатива; Аргументът гласи, че count(*) разширява целия списък с колони, подобно на „select * …“ и като такъв може да бъде поглъщане на ресурси, когато CLOB колоните присъстват в желаната таблица. Нека разгледаме този аргумент и да видим дали той държи вода. Нека започнем със създаване и попълване на таблица, съдържаща колона CLOB:
SQL>SQL> създайте таблица count_test( 2 идентификационен номер, 3 val varchar2(40), 4 clb clob);Таблица създадена.SQL>SQL> започнете 2 за z в 1..1000000 цикъл 3 вмъкнете в count_test 4 стойности (z, 'Запис '||z, 'Clob стойност '||z); 5 краен контур; 6 7 ангажиране; 8 край; 9 /PL/SQL процедурата е завършена успешно.SQL>
След това нека зададем събитие 10053 да изхвърли трасето на оптимизатора, за да можем да видим как Oracle планира да изпълни заявките count():
SQL> alter session set events ='10053 контекст на име на проследяване завинаги, ниво 2';Сесията е променена.
Сцената е готова, нека изпълним някои варианти на count(), за да видим как се държи Oracle. Първо, ще изпълним директно броене (*) и ще покажем плана:
SQL> изберете count(*) от count_test; COUNT(*)--------- 1000000SQL> alter session set events ='10053 контекстът на името на трасето е изключен';Session altered.SQL> обяснете плана за избор на брой (*) от count_test;Explained.SQL> изберете * от таблица(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT------------------------------ -------------------------------------------------- ------------------------- Хеш стойност на плана:371675025------------------- --------------------+---------------------------- ------+| Идентификатор | Операция | Име | Редове | Байтове | Цена | Време |------------------------------------------------+------- ----------------------------+| 0 | ИЗБЕРЕТЕ ИЗЯВЛЕНИЕ | | | | 3582 | || 1 | СОРТИРАНЕ НА АГРЕГАТ | | 1 | | | || 2 | ДОСТЪП ДО ТАБЛИЦАТА ПЪЛЕН | COUNT_TEST| 848K | | 3582 | 00:00:43 |------------------------------------------------+--- --------------------------------+ Информация за проекция на колона (идентифицирана по идентификатор на операция):------- -------------------------------------------------- -- 1 - (#keys=0) COUNT(*)[22] 2 - (набор редове=1019) Забележка----- - използвани динамични статистически данни:динамично вземане на проби (ниво=2) 19 избрани реда.SQL>предварително>Преглеждайки генерирания файл за проследяване, Oracle просто използва count(*) както е, за да върне резултатите:
Окончателна заявка след трансформации:******* НЕПРАВИЛНАТА ЗАЯВКА Е *******SELECT COUNT(*) "COUNT(*)" ОТ "BING"."COUNT_TEST" "COUNT_TEST" ... ----- Обяснете изхвърлянето на планове ---------- Таблица с планове -----=============Таблица с планове============---------------------------------------+-------- ---------------------------+| Идентификатор | Операция | Име | Редове | Байтове | Цена | Време |------------------------------------------------+------- ----------------------------+| 0 | ИЗБЕРЕТЕ ИЗЯВЛЕНИЕ | | | | 3582 | || 1 | СОРТИРАНЕ НА АГРЕГАТ | | 1 | | | || 2 | ДОСТЪП ДО ТАБЛИЦАТА ПЪЛЕН | COUNT_TEST| 848K | | 3582 | 00:00:43 |------------------------------------------------+--- --------------------------------+ Име на блок на заявка / Псевдоним на обект (идентифициран чрез идентификатор на операция):---- -------------------------------------------------- ------1 - SEL$12 - SEL$1 / "COUNT_TEST"@"SEL$1"---------------------------- -------------------------------- Информация за предикатите:-------------- ---------SQL>Няма изненади; забележете, че Oracle не разширява „*“ до всички колони в таблицата — „*“ в този случай показва, че всички редове трябва да бъдат преброени. Ако беше предоставено действително име на колона, Oracle щеше да преброи стойностите в посочената колона. Нека сега да разгледаме какво прави Oracle с count(rowid) заявка:
SQL> alter session set events ='10053 контекст на име на проследяване завинаги, ниво 2';Променена сесия.SQL> изберете count(rowid) от count_test;COUNT(ROWID)----------- 1000000SQL> alter session set events ='10053 контекстът на името на трасето е изключен'; сесията е променена.SQL> обяснете плана за изберете count(rowid) от count_test;Explained.SQL> изберете * от таблица(dbms_xplan.display(null,null,'projection '));PLAN_TABLE_OUTPUT----------------------------------------------------- -------------------------------------------------- ----Хеш стойност на плана:371675025---------------------------------------+ ----------------------------------+| Идентификатор | Операция | Име | Редове | Байтове | Цена | Време |------------------------------------------------+------- ----------------------------+| 0 | ИЗБЕРЕТЕ ИЗЯВЛЕНИЕ | | | | 3582 | || 1 | СОРТИРАНЕ НА АГРЕГАТ | | 1 | 12 | | || 2 | ДОСТЪП ДО ТАБЛИЦАТА ПЪЛЕН | COUNT_TEST| 848K | 9941K | 3582 | 00:00:43 |------------------------------------------------+--- --------------------------------+ Информация за проекция на колона (идентифицирана по идентификатор на операция):------- -------------------------------------------------- -- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (набор редове=256) ROWID[ROWID,10]Забележка----- - използвани динамични статистически данни:динамично вземане на проби (ниво=2)19 реда избран.SQL>Oracle генерира стойност на rowid за всеки ред в таблицата, операция, която ще консумира някои ресурси на процесора. Тъй като заявката се върна приблизително по същото време като версията count(*), ефективността „попадение“ изглежда незначителна. Добавянето на първичен ключ променя леко плановете, но не и текста на заявката:
SQL> промяна на таблицата count_test добавяне на ограничение count_pk първичен ключ(id);Таблицата е променена. SQL>SQL> alter session set events ='10053 контекст на името на трасето завинаги, ниво 2'; сесията е променена.SQL> изберете count(*) от count_test; COUNT(*)--------- 1000000SQL> alter session set events ='10053 контекстът на името на трасето е изключен';Session altered.SQL> обяснете плана за избор на брой (*) от count_test;Explained.SQL> изберете * от таблица(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT------------------------------ -------------------------------------------------- --------------------- Хеш стойност на плана:371675025------------------------ -------------------------------------------------- | Идентификатор | Операция | Име | Редове | Цена (%CPU)| Време |------------------------------------------------ -------------------------| 0 | ИЗБЕРЕТЕ ИЗЯВЛЕНИЕ | | 1 | 589 (2)| 00:00:01 || 1 | СОРТИРАНЕ НА АГРЕГАТ | | 1 | | || 2 | ИНДЕКС БЪРЗО ПЪЛНО СКАНИРАНЕ| COUNT_PK | 848K| 589 (2)| 00:00:01 |---------------------------------------------- ------------------------------ Информация за проекция на колона (идентифицирана по идентификатор на операцията):---------- -------------------------------------------------- 1 - (#keys=0) COUNT(*)[22] 2 - (набор редове=1019) Забележка----- - използвани динамични статистически данни:динамично вземане на проби (ниво=2) 19 избрани реда.SQL>SQL>SQL> alter събития за набор от сесии ='10053 контекст на името на проследяване завинаги, ниво 2';Сесията е променена.SQL> изберете count(rowid) от count_test;COUNT(ROWID)------------ 1000000SQL> променете събитията от набора от сесии ='10053 контекстът на името на трасето е изключен';Сесията е променена.SQL> обяснете плана за избор на count(rowid) от count_test;Explained.SQL> изберете * от таблица(dbms_xplan.display(null,null,'projection'));PLAN_TABLE_OUTPUT- -------------------------------------------------- ---------------------------------------Хеш стойност на плана:371675025----- -------------------------------------------------- -------------------------| Идентификатор | Операция | Име | Редове | Байтове | Цена (%CPU)| Време |------------------------------------------------ ----------------------------------| 0 | ИЗБЕРЕТЕ ИЗЯВЛЕНИЕ | | 1 | 12 | 589 (2)| 00:00:01 || 1 | СОРТИРАНЕ НА АГРЕГАТ | | 1 | 12 | | || 2 | ИНДЕКС БЪРЗО ПЪЛНО СКАНИРАНЕ| COUNT_PK | 848K| 9941K| 589 (2)| 00:00:01 |---------------------------------------------- -------------------------------------- Информация за проекция на колона (идентифицирана по идентификатор на операцията):-- -------------------------------------------------- ------- 1 - (#keys=0) COUNT(ROWID)[22] 2 - (набор редове=256) ROWID[ROWID,10]Забележка----- - използвана динамична статистика:динамично вземане на проби (ниво =2)Избрани са 19 реда.SQL>SQL> spool offcommit;Подробностите за проследяване 10053 не се промениха след добавянето на първичния ключ.
Изглежда, че от този експеримент са извлечени две части от информацията — count(rowid) не е по-добро от count(*), когато таблиците съдържат CLOB колони и този count(*) не разширява списъка с колони, както прави „select *“ (и няма причина да вярваме, че трябва).
Доказателството е в пудинга, както казва старата поговорка.
# # #
Вижте статии отДейвид Фицярел