Более быстрая альтернатива в Oracle для выбора COUNT (*) из sometable
Я заметил, что в Oracle, запрос
SELECT COUNT(*) FROM sometable;
очень медленно для больших таблиц. Похоже, что база данных на самом деле проходит через каждую строку и увеличивает счетчик по одному за раз. Я бы подумал, что где-то в таблице будет счетчик, сколько строк имеет эта таблица.
Итак, если я хочу проверить количество строк в таблице в Oracle, каков самый быстрый способ сделать это?
10 ответов:
подумайте об этом: база данных действительно должна идти в каждую строку, чтобы сделать это. в многопользовательской среде my
COUNT(*)может отличаться от вашегоCOUNT(*). Было бы непрактично иметь другой счетчик для каждого сеанса, поэтому вам нужно буквально подсчитывать строки. В большинстве случаев в любом случае у вас будет предложение WHERE или соединение в вашем запросе, поэтому ваш гипотетический счетчик будет иметь небольшую практическую ценность.однако есть способы ускорить процесс: если у вас есть индекс в столбце NOT NULL, Oracle будет считать строки индекса вместо таблицы. В правильной реляционной модели все таблицы имеют первичный ключ, поэтому
COUNT(*)будет использовать индекс первичного ключа.растровый индекс имеет записи для нулевых строк, поэтому COUNT (*) будет использовать растровый индекс, если он доступен.
Если вы хотите только приблизительную оценку, вы можете экстраполировать из образца:
SELECT COUNT(*) * 100 FROM sometable SAMPLE (1);для большей скорости (но более низкой точности) вы можете уменьшить размер выборки:
SELECT COUNT(*) * 1000 FROM sometable SAMPLE (0.1);для еще большей скорости (но еще худшей точности) вы можете использовать блочную выборку:
SELECT COUNT(*) * 100 FROM sometable SAMPLE BLOCK (1);
это отлично подходит для больших таблиц.
SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE';для малых и средних таблиц размера, следовать будет одобрен.
SELECT COUNT(Primary_Key) FROM table_name;спасибо,
если таблица имеет индекс в столбце NOT NULL, COUNT (*) будет использовать это. В противном случае выполняется полное сканирование таблицы. Обратите внимание, что индекс не должен быть уникальным, он просто должен быть не нулевым.
Вот таблица...
SQL> desc big23 Name Null? Type ----------------------------------------- -------- --------------------------- PK_COL NOT NULL NUMBER COL_1 VARCHAR2(30) COL_2 VARCHAR2(30) COL_3 NUMBER COL_4 DATE COL_5 NUMBER NAME VARCHAR2(10) SQL>Сначала мы сделаем подсчет без индексов ....
SQL> explain plan for 2 select count(*) from big23 3 / Explained. SQL> select * from table(dbms_xplan.display) 2 / select * from table)dbms_xplan.display) PLAN_TABLE_OUTPUT -------------------------------------------------------------------- Plan hash value: 983596667 -------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 1618 (1)| 00:00:20 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | TABLE ACCESS FULL| BIG23 | 472K| 1618 (1)| 00:00:20 | -------------------------------------------------------------------- Note PLAN_TABLE_OUTPUT -------------------------------------------------------------------- - dynamic sampling used for this statement 13 rows selected. SQL>нет мы создаем индекс для столбца, который может содержать нулевые записи ...
SQL> create index i23 on big23(col_5) 2 / Index created. SQL> delete from plan_table 2 / 3 rows deleted. SQL> explain plan for 2 select count(*) from big23 3 / Explained. SQL> select * from table(dbms_xplan.display) 2 / PLAN_TABLE_OUTPUT -------------------------------------------------------------------- Plan hash value: 983596667 -------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 1618 (1)| 00:00:20 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | TABLE ACCESS FULL| BIG23 | 472K| 1618 (1)| 00:00:20 | -------------------------------------------------------------------- Note PLAN_TABLE_OUTPUT -------------------------------------------------------------------- - dynamic sampling used for this statement 13 rows selected. SQL>наконец, давайте построим индекс на столбце NOT NULL ....
SQL> drop index i23 2 / Index dropped. SQL> create index i23 on big23(pk_col) 2 / Index created. SQL> delete from plan_table 2 / 3 rows deleted. SQL> explain plan for 2 select count(*) from big23 3 / Explained. SQL> select * from table(dbms_xplan.display) 2 / PLAN_TABLE_OUTPUT --------------------------------------------------------------------- Plan hash value: 1352920814 ---------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ---------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 326 (1)| 00:00:04 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | INDEX FAST FULL SCAN| I23 | 472K| 326 (1)| 00:00:04 | ---------------------------------------------------------------------- Note PLAN_TABLE_OUTPUT ---------------------------------------------------------------------- - dynamic sampling used for this statement 13 rows selected. SQL>
Вариант 1: есть индекс на ненулевой столбец присутствует, который может быть использован для сканирования. Или создайте индекс на основе функции как:
create index idx on t(0);это может быть Отсканировано, чтобы дать счетчик.
Вариант 2: Если у вас включен мониторинг, проверьте представление мониторинга USER_TAB_MODIFICATIONS и добавьте/вычитайте соответствующие значения в статистику таблицы.
Вариант 3 :для быстрой оценки больших таблиц вызовите предложение SAMPLE... например ...
SELECT 1000*COUNT(*) FROM sometable SAMPLE(0.1);Вариант 4: Используйте материализованный вид для поддержания счетчика (*). Но мощная медицина.
хм ...
вы можете создать быстрое обновление материализованного представления для хранения счетчика.
пример:
create table sometable ( id number(10) not null primary key , name varchar2(100) not null); create materialized view log on sometable with rowid including new values; create materialized view sometable_count refresh on commit as select count(*) count from sometable; insert into sometable values (1,'Raymond'); insert into sometable values (2,'Hans'); commit; select count from sometable_count;это замедлит мутации на таблице sometable немного, но подсчет станет намного быстрее.
самый быстрый способ получить граф таблицы-это именно то, что вы сделали. Нет никаких трюков, которые вы можете сделать, о которых Oracle уже не знает.
есть кое-что, о чем Вы нам не сказали. А именно, почему вы думаете, что это должно быть быстрее?
например:
- вы хотя бы сделали план объяснения, чтобы увидеть, что делает Oracle?
- сколько строк в этой таблице?
- какую версию Oracle вы используете? 8,9,10,11 ... 7?
- вы когда-нибудь запустить статистику базы данных по этой таблице?
- это часто обновляемая таблица или пакетная загрузка или просто статические данные?
- это единственный медленный счет (*) у вас есть?
- сколько времени отборный отсчет (*) от двойного взятия?
Я признаю, что не был бы доволен 41 секундой, но действительно, почему вы думаете, что это должно быть быстрее? Если вы скажете нам, что таблица имеет 18 миллиардов строк и работает на ноутбуке, вы купленный с гаражной распродажи в 2001 году, 41 секунд, вероятно, не так далеко за пределами "хорошо, как это будет", если вы не получите лучшее оборудование. Однако, если вы говорите, что находитесь на Oracle 9, и вы запустили статистику прошлым летом, вы, вероятно, получите другие предложения.
был соответствующий ответ от Спросить Тома опубликовано в апреле 2016 года.
Если у вас есть достаточная мощность сервера, вы можете сделать
select /*+ parallel */ count(*) from sometableЕсли вы только после приближения, вы можете сделать:
select 5 * count(*) from sometable sample block (10);также, если есть
- столбец, который не содержит нулей, но не определен как NOT NULL, и
- в этом столбце есть индекс
вы могли попробуйте:
select /*+ index_ffs(t) */ count(*) from sometable t where indexed_col is not null
вы можете повысить производительность, используя следующий метод:
SELECT COUNT(1) FROM (SELECT /*+FIRST_ROWS*/ column_name FROM table_name WHERE column_name = 'xxxxx' AND ROWNUM = 1);
Comments