Более быстрая альтернатива в Oracle для SELECT COUNT (*) из sometable - PullRequest
55 голосов
/ 03 декабря 2009

Я заметил, что в Oracle запрос

SELECT COUNT(*) FROM sometable;

очень медленно для больших таблиц. Кажется, что база данных фактически проходит каждую строку и увеличивает счетчик по одному. Я думаю, что где-нибудь в таблице будет счетчик количества строк в этой таблице.

Итак, если я хочу проверить количество строк в таблице в Oracle, какой самый быстрый способ сделать это?

Ответы [ 10 ]

53 голосов
/ 04 декабря 2009

Если вам нужна приблизительная оценка, вы можете экстраполировать из выборки:

SELECT COUNT(*) * 100 FROM sometable SAMPLE (1);

Для большей скорости (но меньшей точности) вы можете уменьшить размер выборки:

SELECT COUNT(*) * 1000 FROM sometable SAMPLE (0.1);

Для еще большей скорости (но еще хуже точности) вы можете использовать блочную выборку:

SELECT COUNT(*) * 100 FROM sometable SAMPLE BLOCK (1);

44 голосов
/ 04 февраля 2013

Это прекрасно работает для больших столов.

SELECT NUM_ROWS FROM ALL_TABLES WHERE TABLE_NAME = 'TABLE_NAME_IN_UPPERCASE';

Для таблиц малого и среднего размера все будет в порядке.

SELECT COUNT(Primary_Key) FROM table_name;

Приветствия

27 голосов
/ 03 декабря 2009

Подумайте об этом: база данных действительно должна идти в каждую строку, чтобы сделать это. В многопользовательской среде мой COUNT(*) может отличаться от вашего COUNT(*). Было бы нецелесообразно иметь разные счетчики для каждого сеанса, поэтому вам нужно буквально подсчитывать строки. В любом случае, в большинстве случаев в вашем запросе будет предложение WHERE или JOIN, поэтому ваш гипотетический счетчик будет иметь практическое значение.

Однако есть способы ускорить процесс: если у вас есть INDEX для столбца NOT NULL, Oracle будет считать строки индекса вместо таблицы. В правильной реляционной модели все таблицы имеют первичный ключ, поэтому COUNT(*) будет использовать индекс первичного ключа.

В растровом индексе есть записи для NULL-строк, поэтому COUNT (*) будет использовать растровый индекс, если таковой имеется.

13 голосов
/ 03 декабря 2009

Если таблица имеет индекс по столбцу 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>
7 голосов
/ 03 декабря 2009

Вариант 1. Наличие индекса для ненулевого столбца, который можно использовать для сканирования. Или создайте индекс на основе функции как:

create index idx on t(0);

затем его можно отсканировать, чтобы подсчитать.

Вариант 2. Если у вас включен мониторинг, проверьте представление мониторинга USER_TAB_MODIFICATIONS и добавьте / вычтите соответствующие значения из статистики таблицы.

Вариант 3: для быстрой оценки больших таблиц используйте предложение SAMPLE ... например ...

SELECT 1000*COUNT(*) FROM sometable SAMPLE(0.1); 

Вариант 4. Использование материализованного представления для ведения счета (*). Мощное лекарство, хотя.

гм ...

5 голосов
/ 04 декабря 2009

Вы можете создать материализованное представление быстрого обновления для сохранения количества.

Пример:

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; 

Это немного замедлит мутации в таблице, но подсчет станет намного быстрее.

3 голосов
/ 04 декабря 2009

Самый быстрый способ получить счет таблицы - это именно то, что вы сделали. О том, о чем Oracle еще не знает, нельзя делать никаких хитростей.

Есть кое-что, что вы не сказали нам. А именно, почему вы думаете, что это должно быть быстрее?

Например:

  1. Вы хотя бы составили план объяснения, чтобы увидеть, что делает Oracle?
  2. Сколько строк в этой таблице?
  3. Какую версию Oracle вы используете? 8,9,10,11 ... 7?
  4. Вы когда-нибудь запускали статистику базы данных для этой таблицы?
  5. Это часто обновляемая таблица или пакетная загрузка или просто статические данные?
  6. Это единственный медленный СЧЕТ (*), который у вас есть?
  7. Сколько времени занимает SELECT COUNT (*) FROM Dual?

Я признаю, что я не был бы счастлив с 41 секундой, но на самом деле, ПОЧЕМУ, как вы думаете, это должно быть быстрее? Если вы скажете нам, что таблица имеет 18 миллиардов строк и работает на ноутбуке, который вы купили в гаражной распродаже в 2001 году, 41 секунда, вероятно, не так уж и хороша, как «получится», если вы не приобретете более качественное оборудование. Однако, если вы скажете, что используете Oracle 9 и хорошо провели статистику прошлым летом, вы, вероятно, получите другие предложения.

2 голосов
/ 06 сентября 2016

Был соответствующий ответ от Спросите Тома , опубликованного в апреле 2016 года.

Если у вас достаточно мощности сервера, вы можете сделать

select /*+ parallel */ count(*) from sometable

Если вы только после приближения, вы можете сделать:

select 5 * count(*) from sometable sample block (10);

Также, если есть

  1. столбец, который не содержит нулей, но не определен как NOT NULL, и
  2. в этом столбце есть индекс

Вы можете попробовать:

select /*+ index_ffs(t) */ count(*) from sometable  t where indexed_col is not null
0 голосов
/ 12 марта 2018

Вы можете добиться лучшей производительности, используя следующий метод:

SELECT COUNT(1) FROM (SELECT /*+FIRST_ROWS*/ column_name 
FROM table_name 
WHERE column_name = 'xxxxx' AND ROWNUM = 1);
0 голосов
/ 27 января 2014

Вы можете использовать COUNT (1) вместо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...