[Oracle DB] Thủ tục tự động tạo partition, index local, index global, di chuyển index sang tablespace khác

 - Tạo bảng partition theo ngày, partition từ 01/01/2024 - 31/12/2024

- Thêm dữ liệu 1 triệu row hoặc 10 triệu row (link https://www.tranvanbinh.vn/2022/03/sinh-du-lieu-ngau-nhien-e-thuc-hanh.html)

- Gather statistic để có thông tin row, size, block của bảng, partition

- Tạo index local và index global trên 1 cột, phân biệt 2 loại index này? (dùng TOAD)

- Truncate 1 partition và xem index global có bị unusable không? Cách khắc phục như thế nào để không bị unusable?

- Tạo index 2 cột dạng local.

====

-- 1.Tạo bảng với các trường như sau:

CREATE TABLE oaz21.table1
(
   id                VARCHAR2 (15) NOT NULL,
   start_datetime    DATE NOT NULL,
   col3              VARCHAR2 (20),
   col4              NUMBER (10, 2)
)
TABLESPACE DATA2024
PARTITION BY RANGE (start_datetime)
   (
      PARTITION
         DATA20240101
         VALUES LESS THAN
            (TO_DATE ('2024-01-02 00:00:00',
                      'SYYYY-MM-DD HH24:MI:SS',
                      'NLS_CALENDAR=GREGORIAN'))
         TABLESPACE DATA2024
    )
;

-- Câu lệnh drop bảng nếu không vừa ý:

DROP TABLE OAZ21.TABLE1 CASCADE CONSTRAINTS PURGE;

--Xem các Partition đã được tạo, trong lúc này thì bảng mới chỉ có 1 partition khởi tạo ban đầu:

SELECT partition_name, high_value, partition_position
FROM user_tab_partitions
WHERE table_name = 'TABLE1';

--2. Thủ tục tạo thêm partition hàng loạt:

--Thủ tục tạo thêm partition
DECLARE
   v_nam          NUMBER (4) := 2023; -- Năm cần tạo partition
   v_tablename    VARCHAR2 (50) := 'table1'; -- Tên bảng
   v_date_from    DATE := TO_DATE ('2023-01-02', 'yyyy-mm-dd'); -- Ngày bắt đầu
   v_date_to      DATE := TO_DATE ('2023-12-31', 'yyyy-mm-dd'); -- Ngày kết thúc
   v_numday       NUMBER (5);
   v_tablespace   VARCHAR2 (50) := 'DATA2024'; -- Tablespace
BEGIN
   v_numday := v_date_to - v_date_from;

   FOR i IN 0 .. v_numday
   LOOP
      EXECUTE IMMEDIATE
         'ALTER TABLE ' || v_tablename || ' ADD PARTITION DATA' || TO_CHAR (v_date_from + i, 'YYYYMMDD')
         || ' VALUES LESS THAN (TO_DATE('''
         || TO_CHAR (v_date_from + i + 1, 'YYYY-MM-DD')
         || ' 00:00:00'', ''YYYY-MM-DD HH24:MI:SS'')) TABLESPACE ' || v_tablespace;
   END LOOP;
END;
/

--3. Hoặc đưa vào khối job để khi chạy lệnh không phụ thuộc vào session của công cụ bạn đang dùng (SQL*, Toad, SQL Developer):

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'CREATE_PARTITIONS_JOB01',
    job_type        => 'PLSQL_BLOCK',
    job_action      => q'[
        DECLARE
           v_nam          NUMBER (4) := 2024; -- Năm 2024
           v_tablename    VARCHAR2 (50) := 'table1'; -- Tên bảng
           v_date_from    DATE := TO_DATE ('02/01/2024', 'dd/mm/yyyy'); -- Ngày bắt đầu
           v_date_to      DATE := TO_DATE ('31/12/2024', 'dd/mm/yyyy'); -- Ngày kết thúc
           v_numday       NUMBER (5);
           v_tablespace   VARCHAR2 (50) := 'DATA2024'; -- Tablespace
        BEGIN
           v_numday := v_date_to - v_date_from;
           FOR i IN 0 .. v_numday
           LOOP
              EXECUTE IMMEDIATE
                 'ALTER TABLE ' || v_tablename || ' ADD PARTITION DATA' || TO_CHAR (v_date_from + i, 'YYYYMMDD')
                 || ' VALUES LESS THAN (TO_DATE('''
                 || TO_CHAR (v_date_from + i + 1, 'YYYY-MM-DD')
                 || ' 00:00:00'', ''YYYY-MM-DD HH24:MI:SS'')) TABLESPACE ' || v_tablespace;
           END LOOP;
        END;
    ]',
    start_date      => SYSTIMESTAMP,
    repeat_interval => NULL,
    end_date        => NULL,
    enabled         => TRUE,
    auto_drop       => TRUE,
    comments        => 'Job to create daily partitions for table1 for the year 2024'
  );
END;
/

--4. Thủ tục sinh 5 triệu bản ghi và chèn vào bảng:

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'INSERT_JOB05',
    job_type        => 'PLSQL_BLOCK',
    job_action      => q'[
        DECLARE
          v_start_date TIMESTAMP := TO_TIMESTAMP('2024-01-01 00:00:01', 'YYYY-MM-DD HH24:MI:SS');
		  v_end_date   TIMESTAMP := TO_TIMESTAMP('2024-12-31 23:59:59', 'YYYY-MM-DD HH24:MI:SS');
		  v_interval   INTERVAL DAY TO SECOND;
		BEGIN
		  -- Tính khoảng cách thời gian giữa mỗi bản ghi
		  v_interval := (v_end_date - v_start_date) / 5000000;
		  
		  FOR i IN 1..5000000 LOOP
			INSERT /*+ APPEND*/ INTO OAZ21.TABLE1 (ID, START_DATETIME, COL3, COL4)
			VALUES (
			  LPAD(i, 15, '0'),  -- Điền ID với các số 0 phía trước để đảm bảo độ dài 15 ký tự
			  (v_start_date + (i - 1) * v_interval), -- Tăng thời gian bắt đầu lên
			  'Value ' || i,  -- Giả sử giá trị cho COL3
			  i  -- Giả sử giá trị cho COL4
			);
            
                  IF MOD(i, 500000) = 0 THEN
                      COMMIT;
                      DBMS_LOCK.SLEEP(3); -- Dừng 3s sau mỗi lần commit
                    END IF;
		  END LOOP;
		  COMMIT; -- Đảm bảo rằng tất cả các thay đổi được lưu lại
		END;
    ]',
    start_date      => SYSTIMESTAMP,
    repeat_interval => NULL,
    end_date        => NULL,
    enabled         => TRUE,
    auto_drop       => TRUE,
    comments        => 'Job to create daily partitions for table1 for the year 2024'
  );
END;
/

-- Gọi chạy thủ tục trên:

BEGIN
  Generate_Records;
END;
/

--5. Hoặc tạo job tương tự như phía trên:

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'CREATE_PARTITIONS_JOB03',
    job_type        => 'PLSQL_BLOCK',
    job_action      => q'[
        DECLARE
          v_start_date TIMESTAMP := TO_TIMESTAMP('2024-01-01 00:00:01', 'YYYY-MM-DD HH24:MI:SS');
		  v_end_date   TIMESTAMP := TO_TIMESTAMP('2024-12-31 23:59:59', 'YYYY-MM-DD HH24:MI:SS');
		  v_interval   INTERVAL DAY TO SECOND;
		BEGIN
		  -- Tính khoảng cách thời gian giữa mỗi bản ghi
		  v_interval := (v_end_date - v_start_date) / 5000000;
		  
		  FOR i IN 1..5000000 LOOP
			INSERT /*+ APPEND*/ INTO OAZ21.TABLE1 (ID, START_DATETIME, COL3, COL4)
			VALUES (
			  LPAD(i, 15, '0'),  -- Điền ID với các số 0 phía trước để đảm bảo độ dài 15 ký tự
			  (v_start_date + (i - 1) * v_interval), -- Tăng thời gian bắt đầu lên
			  'Value ' || i,  -- Giả sử giá trị cho COL3
			  i  -- Giả sử giá trị cho COL4
			);
		  END LOOP;
		  COMMIT; -- Đảm bảo rằng tất cả các thay đổi được lưu lại
		END;
    ]',
    start_date      => SYSTIMESTAMP,
    repeat_interval => NULL,
    end_date        => NULL,
    enabled         => TRUE,
    auto_drop       => TRUE,
    comments        => 'Job to create daily partitions for table1 for the year 2024'
  );
END;
/

-- 6. Sau đó có thể select thử trong bảng này, xem plan để đánh giá hiệu quả của việc partition:

--7. Chạy job gather để lấy thông tin thống kê của bảng vừa tạo:

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'GATHER_JOB01',
    job_type        => 'PLSQL_BLOCK',
    job_action      => q'[
        BEGIN
          DBMS_STATS.GATHER_TABLE_STATS(
            ownname          => 'OAZ21',         -- Tên ch? s? h?u c?a b?ng
            tabname          => 'TABLE1',        -- Tên b?ng
            estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, -- Ph?n tram u?c lu?ng, AUTO_SAMPLE_SIZE cho phép Oracle t? d?nh lu?ng
            method_opt       => 'FOR ALL COLUMNS SIZE AUTO', -- Tùy ch?n phuong pháp cho t?t c? các c?t
            cascade          => TRUE,            -- Thu th?p th?ng kê cho các indexes liên quan
            degree           => DBMS_STATS.DEFAULT_DEGREE     -- Ð? d?ng th?i
          );
        END;
    ]',
    start_date      => SYSTIMESTAMP,
    repeat_interval => NULL,
    end_date        => NULL,
    enabled         => TRUE,
    auto_drop       => TRUE,
    comments        => 'No comment'
  );
END;
/

--8. Tạo 2 index local và global cho bảng này:

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name           => 'INDEX_01',
    job_type           => 'PLSQL_BLOCK',
    job_action         => q'[
        BEGIN
            EXECUTE IMMEDIATE '
                create index oaz21.table1_I1 on oaz21.table1(id) local parallel 8 nologging online tablespace INDX2024
            ';
            EXECUTE IMMEDIATE '
                create index oaz21.table1_I2 on oaz21.table1(start_datetime) parallel 8 nologging online tablespace INDX2024
            ';
        END;
    ]',
    start_date         => SYSTIMESTAMP,
    repeat_interval    => NULL,
    end_date           => NULL,
    enabled            => TRUE,
    auto_drop          => TRUE,
    comments           => 'no comment'
  );
END;
/
-- Kiểm tra các index local và index global vừa tạo:

## Phân biệt Global Indexes và Local Indexes:

### Local Indexes

- **Phân vùng**: Tông tại trên bảng đã được partition, Mỗi phân vùng của một local index chỉ liên kết với một phân vùng duy nhất của bảng được phân vùng, mỗi phân vùng bảng sẽ có một phần của index riêng biệt.

- **Quản lý**: Việc thêm hoặc xóa phân vùng trong bảng sẽ tự động thêm hoặc xóa các phân vùng tương ứng trong local index. Điều này giúp quản lý index trở nên đơn giản hơn khi làm việc với dữ liệu được phân vùng.

### Global Indexes

- **Phân vùng**: Global index có thể lập chỉ mục dữ liệu trên tất cả các phân vùng của bảng.

- **Quản lý**: Việc thêm hoặc xóa phân vùng trong bảng có thể ảnh hưởng đến cấu trúc của global index có thể dẫn đến unusable index, phải xử lí thủ công (re-build index).

- **Hiệu suất**: Phù hợp với các truy vấn cần truy cập dữ liệu trên toàn bảng (nhiều partition).

### Sự lựa chọn giữa Local và Global Indexes

- **Local indexes** thích hợp cho các ứng dụng mà dữ liệu được truy cập và quản lý chủ yếu theo phân vùng. Chúng đơn giản hóa quản lý và có thể cung cấp hiệu suất tốt khi làm việc với dữ liệu phân vùng.

- **Global indexes** có thể hữu ích khi cần một cái nhìn tổng quan về toàn bộ dữ liệu hoặc khi thực hiện các truy vấn không thể được tối ưu hóa cho một phân vùng cụ thể.

## Truncate 1 partition và xem index global có bị unusable không? Cách khắc phục như thế nào để không bị unusable?

-- Câu lệnh kiểm tra các Indexes:

--Kiểm tra các index global và local
select INDEX_NAME,INDEX_TYPE,TABLE_NAME,UNIQUENESS,TABLESPACE_NAME,PCT_FREE,BLEVEL,LEAF_BLOCKS,DISTINCT_KEYS,STATUS,NUM_ROWS,SAMPLE_SIZE,LAST_ANALYZED
from user_indexes
--where table_name = 'TABLE1'
;

select INDEX_NAME,COMPOSITE,PARTITION_NAME,PARTITION_POSITION,STATUS,TABLESPACE_NAME,PCT_FREE,DISTINCT_KEYS,NUM_ROWS,SAMPLE_SIZE,LAST_ANALYZED
from user_ind_partitions
--where index_name='TABLE1_I1'
;

--Kiểm tra các partition
SELECT *
FROM user_tab_partitions
--WHERE table_name = 'TABLE1'
;

-- Thử truncate một phân vùng sau đó kiểm tra lại index thì trạng thái của Global index chuyển sang "unusable":

alter table oaz21.table1 truncate partition data20240101;

-- Các câu lệnh kiểm tra khác, khi này nếu select theo partition "data20240101" sẽ ra kết quả rỗng:

select rowid, a.* from oaz21.table1 a;
select rowid, a.* from oaz21.table1 partition(data20240101) a;

-- Để xử lí Global index bị unusable ta sẽ tiến hành re-build index. Câu lệnh này rất mất thời gian nên tôi sẽ đưa vào khối job để không phụ thuộc vào sesion:

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name           => 'INDEX_REBUILD_01',
    job_type           => 'PLSQL_BLOCK',
    job_action         => q'[
        BEGIN
            EXECUTE IMMEDIATE '
                ALTER INDEX oaz21.TABLE1_I2 REBUILD
            ';
        END;
    ]',
    start_date         => SYSTIMESTAMP,
    repeat_interval    => NULL,
    end_date           => NULL,
    enabled            => TRUE,
    auto_drop          => TRUE,
    comments           => 'no comment'
  );
END;
/

-- Kiểm tra kết quả sau khi rebuild index, trạng thái của global index đã chuyển sang Valid:

-- Để truncate một phân vùng mà không ảnh hưởng đến Global index, sử dụng option như sau:

ALTER TABLE oaz21.table1 TRUNCATE PARTITION data20240101 UPDATE INDEXES;

-- Trong trường hợp Index bị đặt sai tablespace, cần di chuyển index tới một tablespace khác để phù hợp với qui hoạch lưu trữ. Ta cần có tủ tục để rebuild index tới tablespace mới INDX_NEW. Đây là quá trình tiêu tốn nhiều thời gian và tài nguyên nên việc đưa thủ tục vào job để không phụ thuộc vào sesion là bắt buộc để đảm bảo an toàn hệ thống:

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name           => 'INDEX_MOVE_01',
    job_type           => 'PLSQL_BLOCK',
    job_action         => q'[
        --Chỉ lặp 1 lần
        DECLARE
           v_tablespace VARCHAR2(50) := 'INDX_NEW';

           CURSOR c1 IS
                SELECT a.index_name, a.partition_name
                FROM USER_IND_PARTITIONS a
                WHERE a.index_name = 'TABLE1_I1'
                AND a.status = 'USABLE';

        BEGIN
           FOR i1 IN c1
           LOOP
              EXECUTE IMMEDIATE 'ALTER INDEX oaz21.'
                 || i1.index_name
                 || ' REBUILD PARTITION '
                 || i1.partition_name
                 || ' TABLESPACE '
                 || v_tablespace
                 || ' nologging parallel 8 online';
           END LOOP;
        END;
    ]',
    start_date         => SYSTIMESTAMP,
    repeat_interval    => NULL,
    end_date           => NULL,
    enabled            => TRUE,
    auto_drop          => TRUE,
    comments           => 'no comment'
  );
END;
/

-- Kiểm tra tablespace INDX_NEW ta thấy hệ thống đang tạo các index mới:

-- Sau khi tạo xong index mới, tiếp tục chạy thủ tục set nologging, noparallel cho các index:

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name           => 'INDEX_NOPARALLEL_02',
    job_type           => 'PLSQL_BLOCK',
    job_action         => q'[
        DECLARE
           CURSOR c1 IS
                SELECT DISTINCT index_name
                FROM USER_IND_PARTITIONS
                WHERE index_name = 'TABLE1_I1'
                AND status = 'USABLE';

        BEGIN
           FOR i1 IN c1 LOOP
              EXECUTE IMMEDIATE 'ALTER INDEX oaz21.' || i1.index_name || ' NOLOGGING NOPARALLEL';
           END LOOP;
        END;
    ]',
    start_date         => SYSTIMESTAMP,
    repeat_interval    => NULL,
    end_date           => NULL,
    enabled            => TRUE,
    auto_drop          => TRUE,
    comments           => 'no comment'
  );
END;
/

-- Thủ tục tự động tạo partition kết hợp re-build cho part mới:

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'CREATE_PART_INDX_JOB09',
    job_type        => 'PLSQL_BLOCK',
    job_action      => q'[
        DECLARE
           v_nam          NUMBER (4) := 2025; -- Năm 2025
           v_tablename    VARCHAR2 (50) := 'table1'; -- Tên bảng
           v_date_from    DATE := TO_DATE ('31/12/2025', 'dd/mm/yyyy'); -- Ngày bắt đầu
           v_date_to      DATE := TO_DATE ('01/07/2025', 'dd/mm/yyyy'); -- Ngày kết thúc
           v_numday       NUMBER (5);
           v_tablespace   VARCHAR2 (50) := 'DATA2024'; -- Tablespace
        BEGIN
           v_numday := v_date_to - v_date_from;
           
           -- Thêm các phân vùng mới
           FOR i IN 0 .. v_numday LOOP
              EXECUTE IMMEDIATE
                 'ALTER TABLE ' || v_tablename || ' ADD PARTITION DATA' || TO_CHAR (v_date_from + i, 'YYYYMMDD')
                 || ' VALUES LESS THAN (TO_DATE('''
                 || TO_CHAR (v_date_from + i + 1, 'YYYY-MM-DD')
                 || ' 00:00:00'', ''YYYY-MM-DD HH24:MI:SS'')) TABLESPACE ' || v_tablespace;
           END LOOP;
           
           -- Rebuild lại chỉ mục cho các phân vùng mới
           FOR i IN 0 .. v_numday LOOP
              EXECUTE IMMEDIATE
                 'ALTER INDEX ' || v_tablename || '_I1 REBUILD PARTITION DATA' || TO_CHAR (v_date_from + i, 'YYYYMMDD') || ' TABLESPACE ' || v_tablespace || ' ONLINE';
              EXECUTE IMMEDIATE
                 'ALTER INDEX ' || v_tablename || '_I2 REBUILD PARTITION DATA' || TO_CHAR (v_date_from + i, 'YYYYMMDD') || ' TABLESPACE ' || v_tablespace || ' ONLINE';
           END LOOP;
        END;
    ]',
    start_date      => SYSTIMESTAMP,
    repeat_interval => NULL,
    end_date        => NULL,
    enabled         => TRUE,
    auto_drop       => TRUE,
    comments        => 'Job to create daily partitions for table1 for the year 2024'
  );
END;
/

====

-- Tất cả mọi thứ mình đều tham khảo ở đây. Bản blog này chỉ đơn giản là mình muốn có một chỗ lưu các script vì công cụ lưu như Git, OneNote, Keep không phải lúc nào cũng tiện dụng:

7.TABLESPACE

https://www.tranvanbinh.vn/2020/07/quan-tri-tablespace-oracle.html

https://www.tranvanbinh.vn/2021/02/xu-ly-khi-u01-u02-chua-datafile-ay-99.html

https://www.tranvanbinh.vn/2021/02/cach-xu-ly-loi-ora-19815-warning.html

https://www.tranvanbinh.vn/2021/04/job-tu-ong-xoa-log-file-trong-oracle.html

https://www.tranvanbinh.vn/2021/02/xu-ly-khi-u01-u02-chua-datafile-ay-99.html

https://www.tranvanbinh.vn/2021/01/quy-trinh-kiem-tra-nhanh-oracle.html

https://www.tranvanbinh.vn/2021/10/thu-tuc-add-ia-vao-asm-diskgroup-trong.html

https://www.tranvanbinh.vn/2020/07/quy-trinh-add-o-ia-moi-vao-diskgroup.html

https://www.tranvanbinh.vn/2018/08/thu-tuc-add-ia-vao-diskgroup-data-trong.html

https://www.tranvanbinh.vn/2020/11/script-resize-datafile.html

https://www.tranvanbinh.vn/2018/09/chuyen-du-lieu-tu-tablespace-indx-sang.html

https://www.tranvanbinh.vn/2020/11/asmchuyen-du-lieu-tu-phan-vung-data.html

https://www.tranvanbinh.vn/2018/09/chuyen-du-lieu-tu-tablespace-khac-sang.html

https://www.tranvanbinh.vn/2020/11/thu-tuc-chuyen-du-lieu-sang-phan-vung.html

https://www.tranvanbinh.vn/p/admin.html

PARTITION,INDEX:

https://www.tranvanbinh.vn/p/admin-objects.html

https://www.tranvanbinh.vn/2022/02/khi-nao-can-tao-partition-cho-csdl.html

https://www.tranvanbinh.vn/2020/12/quan-ly-bang-partitionfull.html

https://www.tranvanbinh.vn/2019/12/lam-nao-tao-partion-het-nam-2020-cho-ca.html

Nhận xét

Bài đăng phổ biến