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



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



SELECT COUNT(*) FROM sometable;


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



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

902   10  

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 уже не знает.

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

например:

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

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

был соответствующий ответ от Спросить Тома опубликовано в апреле 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

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

SELECT COUNT(1) FROM (SELECT /*+FIRST_ROWS*/ column_name 
FROM table_name 
WHERE column_name = 'xxxxx' AND ROWNUM = 1);

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

Comments

    Ничего не найдено.