Эта статья основана на версии Oracle 11g для демонстрации.
Возможно, у нас обычно возникают вопросы.Когда вы хотите подсчитать общее количество записей запроса, первое, о чем вы думаетеcount(*)
достичь, но вдруг беспокоиться о большом количестве данных в таблице, беспокоиться оcount(*)
производительность будет плохой, поэтому мы могли бы использоватьcount(列)
Статистика, в чем разница между ними?
1. Когда таблица не проиндексирована
1. Сначала выполняем следующий скрипт в sqlplus:
-- 如果存在,则删除该表
DROP TABLE TEST_TABLE;
-- 基于DBA_OBJECTS建立一张测试表,这张表是没有任何主键、外键、索引的
CREATE TABLE TEST_TABLE AS (SELECT * FROM DBA_OBJECTS);
мы получаемTEST_TABLE
таблица для нашего собственного тестирования, полученная изDBA_OBJECTS
таблица, обратите внимание, что толькоsys
Только пользователи могут использовать эту форму, пожалуйста, используйтеsys
чтобы залогиниться.
2. Дважды выполняем следующий скрипт, чтобы сравнить два соответствующих индикатора:
SELECT COUNT(*) FROM TEST_TABLE;
первый раз:
CPU开销:281,递归调用:28,一致性读:1097
второй раз:
CPU开销:281,递归调用:0,一致性读:1031
Здесь вы обнаружите, что накладные расходы такие же, но рекурсивный вызов фактически становится 0, а последовательное чтение намного меньше.Почему? Поскольку оракул будет кэшировать данные в кеше после выполнения запроса, соответствующего области общего пула оракула
3. Дважды выполняем следующий скрипт, чтобы сравнить два соответствующих индикатора:
SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
первый раз:
CPU开销:281,递归调用:27,一致性读:1097
второй раз:
CPU开销:281,递归调用:0,一致性读:1031
Вы обнаружите, что на самом делеCOUNT(*)
а такжеCOUNT(OBJECT_ID)
Эффективность такая же быстрая. Вы можете быть неубеждены, почему вы выбираетеOBJECT_ID
В качестве демонстрации вы можете просто переключиться на отдельный столбец, чтобы увидеть, хорошо, давайте продемонстрируем другой столбец:
SELECT COUNT(STATUS) FROM TEST_TABLE;
первый раз:
CPU开销:281,递归调用:4,一致性读:1095
второй раз:
CPU开销:281,递归调用:0,一致性读:1031
Результат по-прежнему1031个一致性读
, как быстро~ я до сих пор не верю, вы выбираете столбец с большим количеством нулейSUBOBJECT_NAME
Давайте продемонстрируем эту колонку! Хорошо, посмотрите на скрипт ниже:SELECT COUNT(SUBOBJECT_NAME) FROM TEST_TABLE;
первый раз:
CPU开销:281,递归调用:4,一致性读:1095
второй раз:
CPU开销:281,递归调用:0,一致性读:1031
правильно! Тем не менее, последовательные чтения1031
, разницы в эффективности нет. Но, вы заметили, что я сделал маленький кружок на последних двух картинках, почему общее количество тактов стало 357? Разве это не должно быть 72056 строк?
Это потому чтоCOUNT(列)
При статистике, если значение столбца равно null, то он не будет участвовать в расчете.Это объяснение.На самом деле,SELECT COUNT(*) FROM TEST_TABLE
а такжеSELECT COUNT(列) FROM TEST_TABLE
не эквивалентно, если вы беретеSELECT COUNT(列) FROM XXX
Чтобы выполнить требования количества записей в статистической таблице, что GG.
Во-вторых, когда таблица индексируется
Приведенный выше эксперимент доказывает, что без индексацииCOUNT(*)
а такжеCOUNT(列)
Нет никакой разницы. Предположим, мы даемOBJECT_ID
Что происходит при добавлении индекса в этот столбец, выполните следующий скрипт:CREATE INDEX OBJECT_ID_INDEX ON TEST_TABLE(OBJECT_ID);
Затем выполняем следующий скрипт:
SELECT COUNT(*) FROM TEST_TABLE;
Второй проход:
CPU开销:281,递归调用:0,一致性读:1031
Как видно из рисунка выше, даже если мы построим индекс,COUNT(*)
Он не возьмет индекс, но из плана выполнения видно, что берется полное сканирование таблицы.TABLE ACCESS FULL
, для соответствующего содержания плана выполнения вы можете прочитать другую статью редактора[Оптимизация производительности Oracle 1] Анализ плана выполнения и типа индекса
тогда в это времяCOUNT(OBJECT_ID)
Каков эффект? Выполните следующий скрипт:SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
CPU开销:45,递归调用:0,一致性读:168
Во второй раз я обнаружил, что накладные расходы ЦП и последовательные чтения были намного меньше, а также выполнялось сканирование индекса.INDEX FAST FULL SCAN
, Спектакль действительно впечатляет. Конечно, это для индексированных столбцовCOUNT(索引列)
, а как насчет эффекта неиндексированных столбцов? Выполните следующий скрипт:
SELECT COUNT(SUBOBJECT_NAME) FROM TEST_TABLE;
CPU开销:281,递归调用:0,一致性读:1031
ОбнаружитьCOUNT(非索引列)
Не так много изменилось. Есть небольшая проблема, нашаOBJECT_ID
Он может быть пустым, что, если он не пуст? Выполните следующий скрипт:
ALTER TABLE TEST_TABLE MODIFY OBJECT_ID NOT NULL;
повторно выполнитьCOUNT(*)
а такжеCOUNT(列)
, результат следующий:
SELECT COUNT(*) FROM TEST_TABLE;
CPU开销:45,递归调用:0,一致性读:168
мы обнаруживаемCOUNT(*)
Индекс тоже берется, и производительность намного быстрее.
SELECT COUNT(OBJECT_ID) FROM TEST_TABLE;
CPU开销:45,递归调用:0,一致性读:168
а такжеCOUNT(列)
Не так много изменилось.
3. Обзор функций
условие | действовать | результат |
---|---|---|
Не проиндексировано | COUNT(*) | полное сканирование таблицы |
Не проиндексировано | СЧЕТ(столбец) | полное сканирование таблицы |
Создайте индекс (столбец индекса может быть пустым) | COUNT(*) | полное сканирование таблицы |
Создайте индекс (столбец индекса может быть пустым) | COUNT (индексный столбец) | быстрое сканирование индекса |
Создайте индекс (столбец индекса может быть пустым) | Количество (неиндексированные столбцы) | полное сканирование таблицы |
Создайте индекс (столбец индекса не может быть пустым) | COUNT(*) | быстрое сканирование индекса |
Создайте индекс (столбец индекса не может быть пустым) | COUNT (индексный столбец) | быстрое сканирование индекса |
Сводное предложение,COUNT(*)
В случае, если есть непустой индекс и индекс возьмет индекс, обращая вниманиеCOUNT(*)
а такжеCOUNT(列)
Сам по себе он не эквивалентен, прежде чем его использовать, проанализируйте бизнес-сценарий.
Когда у таблицы есть индекс, и индекс не пуст,COUNT(*)
а такжеCOUNT(1)
а такжеSELECT MAX(ROWNUM) FROM XXX
Эффект тот же.
Давайте возьмем пример, чтобы избежать возврата к таблице во время оптимизации.Когда id является неиндексированным столбцом, а oper_time является индексированным столбцом, доступен следующий SQL:
select count(x.id) as countnum
from iodso.A x
where x.oper_time between 1 and 2;
Приведенный выше sql приведет к операции возврата таблицы.Поскольку нам нужно только подсчитать количество записей, мы можем использоватьcount(*)
Наоборот, производительность выше, т.к. количество записей можно считать прямо из индексной памяти, не нужноTABLE ACCESS BY ROWID
, не используйтеCount(非索引列)
образом, поэтому мы хотим избежать вышеуказанной ошибки.