Не обичам да си губя времето. Не. Мразя да си губя времето. Не понасям да правя излишни неща.
Сега ми стовариха една таблица, която има данни за 2 години и половина, а няма нито индекси, нито партишъни. Има 334 милона реда. И аз трябва да я нацепя и да разходя данните в 30 по-малки таблички, по месеци. Малки, спретнати, чак компресирани.
Направих си аз празните таблички и започнах да прехвърлям. Ето какво се случи:
SQL> insert /*+append*/ into DATA_200703
2 select *
3 from HUDGE_TABLE_WITH_DATA
4 where j.rowtime < to_timestamp('01.04.2007', 'DD.MM.YYYY')
5 and j.rowtime >= to_timestamp('01.03.2007', 'DD.MM.YYYY')
6 order by j.nice_column;
4586739 rows created.
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
. . .
---------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | Writes | OMem | 1Mem | Used-Mem | Used-Tmp|
---------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | LOAD AS SELECT | | 1 | | 1 |00:29:29.90 | 3355K| 3388K| 52040 | 525K| 525K| 525K (0)| |
| 2 | SORT ORDER BY | | 1 | 4161K| 4586K|00:28:32.90 | 3344K| 3388K| 44291 | 1557M| 9652K| 244M (1)| 1400K|
|* 3 | FILTER | | 1 | | 4586K|00:16:10.97 | 3344K| 3343K| 0 | | | | |
|* 4 | TABLE ACCESS FULL| HUDGE_TABLE_WITH_DATA | 1 | 4161K| 4586K|00:16:06.38 | 3344K| 3343K| 0 | | | | |
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Тук вземам данните за един месец и ги сортирам по една подходяща за случая колонка (за да се сортират по-добре). Както знаете, колкото повече повторения се паднат в един блок, толкова повече може да се компресира той.
Това, което се получава, е мега гадно. За да изчопля данните за един месец, аз трябва да пребъркам цялата огромна таблица. Нямам избор – индекси няма. И това упражнение, което отнема половин час, трябва да го повторя 30 пъти. Не ме кефи, ама хич. Толкова излишни сканирания, такава брутална загуба на време…
Представете си някой, който сортира един чувал с поща. Избира си улица 1. Взема всяко едно писмо и поглежда дали е за там. Ако е за там – го слага в съответния раздел. Ако не – го оставя пак на голяма купчина. После повтаря всичко отначало за улица 2, улица 3 и т.н. И на всичко отгоре, аз дори не махам редовете, които вече съм insert-нал.
Не може ли като посетя веднъж един ред, да го напъхам в неговата си табличка; следващия – в неговата и т.н.?
* * *
Всъщност може. Още от Oracle 9i имаме multitable insert. Който, в случая ми върши работа перфектно.
SQL> insert /*+append*/
2 when
3 (rowtime < to_timestamp('01.05.2007', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.04.2007', 'DD.MM.YYYY')) then into DATA_200704
4 when
5 (rowtime < to_timestamp('01.06.2007', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.05.2007', 'DD.MM.YYYY')) then into DATA_200705
6 when
7 (rowtime < to_timestamp('01.07.2007', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.06.2007', 'DD.MM.YYYY')) then into DATA_200706
8 when
9 (rowtime < to_timestamp('01.08.2007', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.07.2007', 'DD.MM.YYYY')) then into DATA_200707
10 when
11 (rowtime < to_timestamp('01.09.2007', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.08.2007', 'DD.MM.YYYY')) then into DATA_200708
12 when
13 (rowtime < to_timestamp('01.10.2007', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.09.2007', 'DD.MM.YYYY')) then into DATA_200709
14 when
15 (rowtime < to_timestamp('01.11.2007', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.10.2007', 'DD.MM.YYYY')) then into DATA_200710
16 when
17 (rowtime < to_timestamp('01.12.2007', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.11.2007', 'DD.MM.YYYY')) then into DATA_200711
18 when
19 (rowtime < to_timestamp('01.01.2008', 'DD.MM.YYYY') and rowtime >= to_timestamp('01.12.2007', 'DD.MM.YYYY')) then into DATA_200712
20 select *
21 from HUDGE_TABLE_WITH_DATA j
22 where rowtime < to_timestamp('01.01.2008', 'DD.MM.YYYY')
23 and rowtime >= to_timestamp('01.04.2007', 'DD.MM.YYYY')
24 order by j.nice_column;
41983210 rows created.
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
. . .
---------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | Writes | OMem | 1Mem | Used-Mem | Used-Tmp|
---------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | MULTI-TABLE INSERT | | 1 | | 7 |00:03:25.78 | 3428K| 3793K| 498K| | | | |
| 2 | VIEW | | 1 | 44M| 44M|00:58:49.57 | 3358K| 3793K| 450K| | | | |
| 3 | SORT ORDER BY | | 1 | 44M| 44M|00:58:49.57 | 3358K| 3793K| 450K| 15G| 29M| 244M (1)| 13M|
|* 4 | FILTER | | 1 | | 44M|00:23:32.12 | 3344K| 3342K| 0 | | | | |
|* 5 | TABLE ACCESS FULL| HUDGE_TABLE_WITH_DATA | 1 | 44M| 44M|00:22:47.99 | 3344K| 3342K| 0 | | | | |
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Тук сканирам само един път цялата голяма таблица, а insert-вам в 8 от малките. Ако не беше сортирането, щях да го пусна направо за всичките 29 оставащи таблички, но се притесних да не ми отеснее temp-a. Което се оказа излишно притеснение.
Между другото, в сряда ме питаха кое ми е направило голям впечатление в Oracle и чак съм си казал „а стига бе!„. Е, сортирането на огромно количество данни е такова нещо. Не мога да повярвам как е сортирало около 15-16 GB данни (по мои груби сметки), с 1-pass sort, използвайки 244 MB памет (явно максимума при тоя PGA target и автоматично управление). Не за пръв път ми прави впечатление – сортирането на големи обеми информация в Oracle е невероятно ефективно!
P.S. Поради спецификата на данните и с помощта на сортирането, компресията се получи 1:8. Това е с нормалната компресия, на 10.2, а не с новата хибридна от 11.2 🙂