Более быстрая альтернатива в Oracle для SELECT COUNT (*) FROM sometable
Я заметил, что в Oracle запрос
SELECT COUNT(*) FROM sometable;
для больших таблиц очень медленный. Кажется, что база данных действительно проходит через каждую строку и увеличивает счетчик по одному. Я бы подумал, что в таблице будет счетчик, сколько строк у этой таблицы.
Итак, если я хочу проверить количество строк в таблице в Oracle, что это самый быстрый способ сделать это?
Ответы
Ответ 1
Подумайте об этом: база данных действительно должна идти в каждую строку, чтобы сделать это. В многопользовательской среде мой COUNT(*)
может отличаться от вашего COUNT(*)
. Было бы нецелесообразно иметь другой счетчик для каждой сессии, чтобы вы буквально подсчитывали строки. В большинстве случаев в любом случае у вас будет предложение WHERE или JOIN в вашем запросе, поэтому ваш гипотетический счетчик будет иметь практическое значение.
Однако есть способы ускорить работу: если у вас есть INDEX в столбце NOT NULL, Oracle будет считать строки индекса вместо таблицы. В правильной реляционной модели все таблицы имеют первичный ключ, поэтому COUNT(*)
будет использовать индекс первичного ключа.
В индексе Bitmap есть записи для строк NULL, поэтому COUNT (*) будет использовать индекс растрового изображения, если есть один доступный.
Ответ 2
Если вам нужна только приблизительная оценка, вы можете экстраполировать ее из образца:
SELECT COUNT(*) * 100 FROM sometable SAMPLE (1);
Для большей скорости (но более низкой точности) вы можете уменьшить размер выборки:
SELECT COUNT(*) * 1000 FROM sometable SAMPLE (0.1);
Для еще большей скорости (но еще хуже точности) вы можете использовать выборочную выборку:
SELECT COUNT(*) * 100 FROM sometable SAMPLE BLOCK (1);
Ответ 3
Это отлично работает для больших таблиц.
SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE';
Для таблиц с малым и средним размерами следующее будет в порядке.
SELECT COUNT(Primary_Key) FROM table_name;
Приветствия,
Ответ 4
Если таблица имеет индекс в столбце NOT NULL, COUNT (*) будет использовать это. В противном случае выполняется полное сканирование таблицы. Обратите внимание, что индекс не должен быть UNIQUE, он просто должен быть NOT NULL.
Вот таблица...
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>
Нет, мы создаем индекс в столбце, который может содержать записи NULL...
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>
Ответ 5
Вариант 1: укажите индекс для не нулевого столбца, который можно использовать для проверки. Или создайте функциональный индекс как:
create index idx on t(0);
это можно затем отсканировать, чтобы дать счет.
Вариант 2: если вы включили мониторинг, проверьте контроль USER_TAB_MODIFICATIONS и добавьте/вычтите соответствующие значения для статистики таблицы.
Вариант 3: для быстрой оценки больших таблиц вызывают предложение SAMPLE... например...
SELECT 1000*COUNT(*) FROM sometable SAMPLE(0.1);
Вариант 4: используйте материализованное представление для сохранения счетчика (*). Могущественная медицина.
um...
Ответ 6
Вы можете создать быстро обновляемое представление, чтобы сохранить счет.
Пример:
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;
Это замедлит мутации на столе немного, но счет будет намного быстрее.
Ответ 7
Самый быстрый способ получить подсчет таблицы - это именно то, что вы сделали. Нет никаких трюков, которые вы можете сделать, о которых Oracle еще не знает.
Есть вещи, которые вы нам не сказали. А именно, почему вы думаете, что это должно быть быстрее?
Например:
- Вы хотя бы сделали план объяснений, чтобы понять, что делает Oracle?
- Сколько строк в этой таблице?
- Какую версию Oracle вы используете? 8,9,10,11... 7?
- Вы когда-нибудь запускали статистику базы данных в этой таблице?
- Это часто обновляемая таблица или пакетная загрузка или только статические данные?
- Это единственный медленный COUNT (*), который у вас есть?
- Сколько времени занимает SELECT COUNT (*) FROM Dual?
Я признаю, что я не буду доволен 41 секундой, но действительно ПОЧЕМУ вы думаете, что это должно быть быстрее? Если вы сообщите нам, что таблица имеет 18 миллиардов строк и работает на ноутбуке, который вы купили в продаже в продаже в продаже в 2001 году, 41 секунда, вероятно, не так уж далека от "хорошего, как он получит", если вы не получите лучшее оборудование. Однако, если вы говорите, что находитесь в Oracle 9, и вы провели статистику прошлым летом, вы, вероятно, получите другие предложения.
Ответ 8
Был найден соответствующий ответ Ask Tom, опубликованный в апреле 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
Ответ 9
Вместо этого вы можете использовать COUNT (1)