Недавно я встречал такой запрос повсюду на сайте клиента:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
DECLARE v_var NUMBER(10); BEGIN SELECT COUNT (*) INTO v_var FROM table1 JOIN table2 ON table1.t1_id = table2.t1_id JOIN table3 ON table2.t2_id = table3.t2_id ... WHERE some_predicate; IF (v_var = 1) THEN do_something ELSE do_something_else END IF; END ; |
К сожалению, COUNT(*)
часто является первым решением, которое приходит на ум, когда мы хотим проверить наши отношения на наличие предиката. Но COUNT()
стоит дорого, особенно если все, что мы делаем, это проверяем наши отношения на существование . Слово звонит в колокол? Да, мы должны использовать предикат EXISTS
, потому что, если нам не важно точное количество записей, которые возвращают true для данного предиката, нам не следует просматривать полный набор данных, чтобы фактически подсчитать точное число . Приведенный выше блок PL / SQL можно переписать тривиально в этот:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
DECLARE v_var NUMBER(10); BEGIN SELECT CASE WHEN EXISTS ( SELECT 1 FROM table1 JOIN table2 ON table1.t1_id = table2.t1_id JOIN table3 ON table2.t2_id = table3.t2_id ... WHERE some_predicate ) THEN 1 ELSE 0 END INTO v_var FROM dual; IF (v_var = 1) THEN do_something ELSE do_something_else END IF; END ; |
Давайте измерим!
Запрос 1 дает этот план выполнения:
1
2
3
4
5
6
7
8
9
|
----------------------------------------------- | Id | Operation | E-Rows | A-Rows | ----------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | SORT AGGREGATE | 1 | 1 | |* 2 | HASH JOIN | 4 | 4 | |* 3 | TABLE ACCESS FULL| 2 | 2 | |* 4 | TABLE ACCESS FULL| 6 | 6 | ----------------------------------------------- |
Запрос 2 дает этот план выполнения:
1
2
3
4
5
6
7
8
9
|
---------------------------------------------- | Id | Operation | E-Rows | A-Rows | ---------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 | NESTED LOOPS | 4 | 1 | |* 2 | TABLE ACCESS FULL| 2 | 1 | |* 3 | TABLE ACCESS FULL| 2 | 1 | | 4 | FAST DUAL | 1 | 1 | ---------------------------------------------- |
Вы можете игнорировать операции TABLE ACCESS FULL
, фактический запрос был выполнен в тривиальной базе данных без индексов.
Однако важны значительно улучшенные значения E-Rows
(E = Estimated) и, что еще важнее, оптимальные значения A-Rows
(A = Actual). Как вы можете видеть, предикат EXISTS
может быть прерван рано, как только будет обнаружена первая запись, соответствующая предикату — в этом случае немедленно.
Смотрите этот пост о более подробной информации о том, как собирать планы выполнения Oracle.
Вывод
Всякий раз, когда вы сталкиваетесь с операцией COUNT(*)
, вы должны спросить себя, действительно ли она нужна. Вы действительно должны знать точное количество записей, которые соответствуют предикату? Или вы уже счастливы, зная, что любая запись соответствует предикату?
Ответ: это, вероятно, последний.
Ссылка: | Совет дня по SQL: Остерегайтесь SELECT COUNT (*) от нашего партнера по JCG Лукаса Эдера в блоге JAVA, SQL и AND JOOQ . |