Более быстрая альтернатива в Oracle для SELECT COUNT (*) FROM sometable - программирование
Подтвердить что ты не робот

Более быстрая альтернатива в Oracle для SELECT COUNT (*) FROM sometable

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

SELECT COUNT(*) FROM sometable;

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

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

4b9b3361

Ответ 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)