powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Какую статистику не собрал? Пляшет estimated rows
11 сообщений из 11, страница 1 из 1
Какую статистику не собрал? Пляшет estimated rows
    #39539341
Pavel_PV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем привет.
Столкнулся со странным поведением оптимизатора, хотя может сам где-то туплю.
Создадим:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
create table MAIN_T
(
 ID        NUMBER    NOT NULL,
 DATE_ID   DATE NOT NULL,
 DATE1_ID DATE)
PARTITION BY RANGE (DATE_ID)
INTERVAL(numtodsinterval(1,'DAY'))
(PARTITION VALUES LESS THAN (TO_DATE('2012-12-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS')))
PARALLEL 8; 

declare 
 v$date date;
begin
for i in 1..20000
loop
   v$date := trunc(SYSDATE) + round(i/1000);
  insert into MAIN_T(ID,DATE_ID, DATE1_ID)
  values(i,v$date,trunc(SYSDATE)-90+i);
end loop;  
commit;
end;

alter table MAIN_T add constraint MAIN_PK primary key(ID);


create table D_TIME
(
  day_id DATE not null,
  my_test varchar2(10));
alter table D_TIME  add constraint DTI_PK primary key(DAY_ID);

insert into D_TIME(day_id)
select DATE_ID
from MAIN_T
UNION
select DATE1_ID
from MAIN_T;
commit;

alter table MAIN_T   add constraint MAIN_FK foreign key (DATE_ID)  references D_TIME(DAY_ID);


begin
 dbms_stats.gather_table_stats(ownname => '*****',tabname => 'MAIN_T',method_opt => 'FOR ALL COLUMNS SIZE 254');
 dbms_stats.gather_index_stats(ownname => '*****',indname => 'MAIN_PK');
 dbms_stats.gather_table_stats(ownname => '*****',tabname => 'D_TIME',method_opt => 'FOR ALL COLUMNS SIZE 254');
 dbms_stats.gather_index_stats(ownname => '*****',indname => 'DTI_PK'); 
end;



Теперь сделаем так:

Код: plsql
1.
2.
3.
4.
5.
6.
7.
explain plan for
select A.*, B.day_id
from provar.MAIN_T A
inner join provar.D_TIME B on A.DATE_ID=B.day_id
where A.DATE_ID between trunc(SYSDATE) and trunc(SYSDATE)+10;
select *
from table(dbms_xplan.display); 


Получим такой план:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
Plan hash value: 3214451937
 
----------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |        | 10500 |   205K|  5090   (1)| 00:00:01 |       |       |
|*  1 |  FILTER                   |        |       |       |            |          |       |       |
|   2 |   PARTITION RANGE ITERATOR|        | 10500 |   205K|  5090   (1)| 00:00:01 |   KEY |   KEY |
|*  3 |    TABLE ACCESS FULL      | MAIN_T | 10500 |   205K|  5090   (1)| 00:00:01 |   KEY |   KEY |
----------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter(TRUNC(SYSDATE@!)+10>=TRUNC(SYSDATE@!))
   3 - filter("A"."DATE_ID"<=TRUNC(SYSDATE@!)+10 AND "A"."DATE_ID">=TRUNC(SYSDATE@!))
 
Note
-----
   - automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold



Теперь сделаем так(в вывод добавили только одну колонку "B.my_test":
Код: plsql
1.
2.
3.
4.
5.
6.
7.
explain plan for
select A.*, B.day_id, B.my_test
from provar.MAIN_T A
inner join provar.D_TIME B on A.DATE_ID=B.day_id
where A.DATE_ID between trunc(SYSDATE) and trunc(SYSDATE)+10;
select *
from table(dbms_xplan.display); 


получим такой план:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
Plan hash value: 1795835293
 
----------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |        |  5000 |   136K|  2315   (1)| 00:00:01 |       |       |
|*  1 |  FILTER                               |        |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                        |        |  5000 |   136K|  2315   (1)| 00:00:01 |       |       |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| D_TIME |    10 |    80 |     3   (0)| 00:00:01 |       |       |
|*  4 |     INDEX RANGE SCAN                  | DTI_PK |    10 |       |     2   (0)| 00:00:01 |       |       |
|   5 |    PARTITION RANGE AND                |        |   500 | 10000 |   231   (1)| 00:00:01 |KEY(AP)|KEY(AP)|
|*  6 |     TABLE ACCESS FULL                 | MAIN_T |   500 | 10000 |   231   (1)| 00:00:01 |KEY(AP)|KEY(AP)|
----------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter(TRUNC(SYSDATE@!)+10>=TRUNC(SYSDATE@!))
   4 - access("B"."DAY_ID">=TRUNC(SYSDATE@!) AND "B"."DAY_ID"<=TRUNC(SYSDATE@!)+10)
   6 - filter("A"."DATE_ID"="B"."DAY_ID" AND "A"."DATE_ID"<=TRUNC(SYSDATE@!)+10 AND 
              "A"."DATE_ID">=TRUNC(SYSDATE@!))
 
Note
-----
   - automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold
   - this is an adaptive plan



Проблема. В первом случае rows-10500(корректно!), во втором 5000. Видно, что во втором случае план конечно меняется и кол-во строк в партиции оракл оценивает некорректно - 500 в одной => сумма тоже некорректна. Если уменьшать период, то кол-во строк в одной партиции оценивается всё хуже. Не могу сообразить какую статистику я не собрал?

P.S> FK создал для примера, наверное можно было и без него.
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39539369
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Pavel_PVПроблема. В первом случае rows-10500(корректно!), во втором 5000. Видно, что во втором случае план конечно меняется и кол-во строк в партиции оракл оценивает некорректно - 500 в одной => сумма тоже некорректна. Если уменьшать период, то кол-во строк в одной партиции оценивается всё хуже. Не могу сообразить какую статистику я не собрал?

Первый план не требует доступа к D_TIME, оценивается количество строк main_t за 10-дневный интервал.
Когда добавили в select-list поле из D_TIME - то исключить эту таблицу оптимизатор уже не может и строит план по соединению.
Ведущая D_TIME, оттуда ожидается 10 строк.
Далее заход NL-ем в MAIN_T на каждый отдельный ключ, найденный в D_TIME оптимизатор ожидает 500 строк из MAIN_T.
Это НЕ количество строк в разделе MAIN_T, это количество строк, которое, как полагает оптимизатор, вернет rowsource по ключу за один заход из NL.
На счет статистики... Попробуйте поиграть с гранулярностью (статистика может быть как глобальная на уровне таблицы, так и на уровне секции), но не уверен, что это что-либо изменит в указанном случае.
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39539371
Фотография Sergey_Korolev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Pavel_PV,

в первом запрос за B.day_id не нужно было идти в таблицу
поскольку он в inner join provar.D_TIME B on A.DATE_ID=B.day_id
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39539390
Pavel_PV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
[quot andrey_anonymous].
Далее заход NL-ем в MAIN_T на каждый отдельный ключ, найденный в D_TIME оптимизатор ожидает 500 строк из MAIN_T.
Это НЕ количество строк в разделе MAIN_T, это количество строк, которое, как полагает оптимизатор, вернет rowsource по ключу за один заход из NL.[quot]
Это я понимаю прекрасно, может написал как-то криво конечно:-" оракл оценивает некорректно - 500 в одной ". Ну дальше понятно 500*10=5000. Вопрос, откуда взялись эти 500? Как оракл получил эту цифру?
Ещё до темы я проверил статистику по партициям/таблице/гистограммы и там всё корректно, кол-во написано ровно то которое и должно быть. Не совсем понял причем тут гранулярность, можно подробнее?
Если так мысли в слух. Конечно понятно, что виной всему "join" и нежелание оракла глянуть в FK. В первом случае он использует FK и поэтому ошибки нет(благодаря FK получается нужный план), во втором же случае он уже опирается на другую статистику и начинает сильно ошибаться. На таких объемах, а ошибка уже в 2 раза. При этом - всё собрано, гистограммы есть. Я сначала подумал, что не всё знаю про FK и мне сейчас ткнут в доку - вот написано как надо. В общем хочется разобраться, на системе такая ошибка приводит к кривым планам.
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39539405
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Подробнее про гранулярность тут: http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_stats.htm#i1036461
По поводу как получилась оценка...
попробуйте посмотреть (explain plan) планчики для запросов:
select A.* from provar.MAIN_T A where A.DATE_ID = date'2017-10-20'; -- может использовать гистограмму для оценки селективности
select A.* from provar.MAIN_T A where A.DATE_ID = :1 ; -- затруднительно использовать гистограмму

Ведомый rowsource в NL похож на запрос с биндом.

Что касается рассуждений на тему FK и его использования... проиндексируйте хотя бы свой FK. О вреде неиндексированных FK где только не написано, включая заборы и подзаборья.
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39539407
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну и, само собой, наиболее подробный ответ на тему "как оно получилось" можно найти в трассе 10053
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39539427
Pavel_PV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
andrey_anonymousПодробнее про гранулярность тут: http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_stats.htm#i1036461
По поводу как получилась оценка...
попробуйте посмотреть (explain plan) планчики для запросов:
select A.* from provar.MAIN_T A where A.DATE_ID = date'2017-10-20'; -- может использовать гистограмму для оценки селективности
select A.* from provar.MAIN_T A where A.DATE_ID = :1 ; -- затруднительно использовать гистограмму

Ведомый rowsource в NL похож на запрос с биндом.

Что касается рассуждений на тему FK и его использования... проиндексируйте хотя бы свой FK. О вреде неиндексированных FK где только не написано, включая заборы и подзаборья.
План для первого скорее всего будет использовать статистику по 1 партиции, там всё хорошо будет. Во втором думаю что-то похожее, нужно брать несколько партиций чтобы использовалась общая статистика по таблице.
10053 снять не проблема, но там формулу вроде не пишут.

За ссылку спасибо, прочту и попробую
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39542257
Pavel_PV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Сразу не отписался.
Про гранулярность конечно красиво написано, но не взлетело. Решил сразу сделать с
Код: sql
1.
granularity => 'ALL'



Странно это конечно, особенно странно что оракл использует fk как будто не на таблицу целиком, а на поле в ней.
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39543031
adaptive plan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Pavel_PV
Код: plsql
1.
2.
3.
Note
-----
   - this is an adaptive plan
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39547324
Pavel_PV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Не в этом корень проблемы, проверял.
...
Рейтинг: 0 / 0
Какую статистику не собрал? Пляшет estimated rows
    #39547757
Rudyshin Sergey
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
проблема видимо в некорректном распределении данных по партициям
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
SQL> select PARTITION_NAME, NUM_ROWS   from user_tab_statistics where table_name = 'MAIN_T';

PARTITION_   NUM_ROWS
---------- ----------
		20000
SYS_P2346	    0
SYS_P2347	  499
SYS_P2348	 1000
...
SYS_P2366	 1000
SYS_P2367	  501

23 rows selected.



если данные сгенерировать равномерно
Код: plsql
1.
2.
insert into MAIN_T(ID,DATE_ID, DATE1_ID)
select rownum, date '2012-12-01' + mod (rownum, 20) - 1 d, sysdate from dual connect by rownum <= 2e4 order by d;




то планы начинают выглядеть более адекватно (но дату в запросах нужно другую указывать в таком случае )
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
SQL> explain plan for
select A.*, B.day_id
from MAIN_T A
inner join D_TIME B on A.DATE_ID=B.day_id
where A.DATE_ID between trunc(date '2012-12-01') and trunc(date '2012-12-01')+10;

select *
from table(dbms_xplan.display);
  2    3    4    5  
Explained.

SQL> SQL>   2  
Plan hash value: 1101736113

---------------------------------------------------------------------------------------------------
| Id  | Operation		 | Name   | Rows  | Bytes | Cost (%CPU)| Time	  | Pstart| Pstop |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	 |	  | 11000 |   214K|  3001   (1)| 00:00:01 |	  |	  |
|   1 |  PARTITION RANGE ITERATOR|	  | 11000 |   214K|  3001   (1)| 00:00:01 |	2 |    12 |
|*  2 |   TABLE ACCESS FULL	 | MAIN_T | 11000 |   214K|  3001   (1)| 00:00:01 |	2 |    12 |
---------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("A"."DATE_ID"<=TO_DATE(' 2012-12-11 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
	      "A"."DATE_ID">=TO_DATE(' 2012-12-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

15 rows selected.

SQL> explain plan for
select A.*, B.day_id, B.my_test
from MAIN_T A
inner join D_TIME B on A.DATE_ID=B.day_id
where A.DATE_ID between trunc(date '2012-12-01') and trunc(date '2012-12-01')+10;
select *
from table(dbms_xplan.display);   2    3    4    5  
Explained.

SQL>   2  
Plan hash value: 3552288420

---------------------------------------------------------------------------------------------------------------
| Id  | Operation			     | Name   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT		     |	      | 11000 |   300K|  3001	(1)| 00:00:01 |       |       |
|   1 |  NESTED LOOPS			     |	      | 11000 |   300K|  3001	(1)| 00:00:01 |       |       |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| D_TIME |    11 |    88 |     2	(0)| 00:00:01 |       |       |
|*  3 |    INDEX RANGE SCAN		     | DTI_PK |    11 |       |     1	(0)| 00:00:01 |       |       |
|   4 |   PARTITION RANGE AND		     |	      |  1000 | 20000 |   273	(1)| 00:00:01 |KEY(AP)|KEY(AP)|
|*  5 |    TABLE ACCESS FULL		     | MAIN_T |  1000 | 20000 |   273	(1)| 00:00:01 |KEY(AP)|KEY(AP)|
---------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("B"."DAY_ID">=TO_DATE(' 2012-12-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
	      "B"."DAY_ID"<=TO_DATE(' 2012-12-11 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
   5 - filter("A"."DATE_ID"="B"."DAY_ID" AND "A"."DATE_ID"<=TO_DATE(' 2012-12-11 00:00:00',
	      'syyyy-mm-dd hh24:mi:ss') AND "A"."DATE_ID">=TO_DATE(' 2012-12-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

20 rows selected.
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Какую статистику не собрал? Пляшет estimated rows
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]