이행 검증과 점검 리포트
이행 전후 건수, 합계, 날짜와 문자 길이 비교, 표본 및 업무 쿼리와 권한 점검으로 찾은 프로시저 누락
#핵심 요약
실습 도구는 두 단계로 확인함
⑤ 검증은 테이블 데이터의 건수와 합계를, ⑥ AS-IS/TO-BE 점검은 오브젝트, 데이터 표본, PL/SQL, 업무 쿼리, 권한 다섯 관점을 봄
건수만 같아서는 부족함
숫자 반올림, 시간 손실, 문자 잘림을 잡으려고 합계, 날짜 최소, 최대, 문자, LOB 길이 합을 함께 비교함
권한은 목록만 보지 않고 has_table_privilege 같은 함수로 그 계정이 정말 그 일을 할 수 있는지 묻음
실습은 첫 점검에서 프로시저 누락과 그 EXECUTE 권한 누락 2건을 찾고, 조치 후 다섯 관점 모두 100%로 통과했음
#개념과 동작 원리
#⑤ 데이터 검증 항목
| 검사 | Oracle | PostgreSQL | 잡아내는 문제 |
|---|---|---|---|
| 건수 | count(*) | count(*) | 빠진 행, 중복 행 |
| 숫자 합계(최대 3개) | sum(컬럼) | sum(컬럼) | 소수점 손실, 반올림 |
| 날짜 최소, 최대 | min / max | min / max | 시, 분, 초 손실, 시간대 밀림 |
| 문자 길이 합 | sum(length()) | sum(char_length()) | 한글 깨짐, 잘림 |
| LOB 길이 합 | sum(dbms_lob.getlength()) | sum(char_length()) | CLOB 잘림 |
실습 결과는 검사 14개(CUST 6, ORDERS 8) 모두 일치, 검증 리포트는 CSV로 내려받음
#⑥ AS-IS/TO-BE 점검 관점
| 관점 | AS-IS(Oracle) | TO-BE(PostgreSQL) | 비교 방법 |
|---|---|---|---|
| 오브젝트 | ALL_TABLES, ALL_VIEWS, ALL_SEQUENCES, ALL_CONSTRAINTS, ALL_OBJECTS | pg_class, pg_constraint, pg_proc, pg_trigger | 이름 대조(누락, 추가) |
| 데이터 | count(*)와 PK 표본 5행 | count(*)와 같은 PK 5행 | 건수, 표본 행의 모든 컬럼 값 |
| PL/SQL | ALL_SOURCE(VALID/INVALID) | pg_get_functiondef | 대상 스키마에 있는지, 다른 스키마에 변환본이 있는지 |
| 업무 쿼리 | Oracle SQL | 같은 질문의 PostgreSQL SQL | 결과 행을 순서까지 비교 |
| 권한 | ALL_TAB_PRIVS | has_table_privilege, has_sequence_privilege, has_function_privilege | 그 계정이 실제로 그 일을 할 수 있는지 |
실습 첫 점검: 오브젝트 7/8, 데이터 100%, PL/SQL 0%, 업무 쿼리 100%, 권한 8/9
조치 목록은 프로시저 누락과 EXECUTE 권한 누락이었음
실습에서는 예제2에서 ora2pg로 변환해 둔 다른 스키마의 프로시저를 도구의 가져오기 버튼으로 대상 스키마에 만들고 EXECUTE 권한을 줬음
다시 점검하자 오브젝트 8/8, 데이터 2/2, PL/SQL 1/1, 업무 쿼리 7/7, 권한 9/9가 됐음
점검 보고서는 HTML로 내려받음
| 원래 파일 | 맡은 일 |
|---|---|
engine.py |
검증 validate |
checker.py |
점검 objects, data, plsql, copy_routine, preset_queries, query, privs |
app.py |
⑤ 검증 표, ⑥ 점검 탭과 보고서 화면 |
#필수 명령어 또는 SQL
#PostgreSQL 쪽 직접 확인
\dt <SCHEMA>.*
\d <SCHEMA>.orders
select count(*) from <SCHEMA>.cust;
select count(*) from <SCHEMA>.orders;
select count(*), sum(total_amt) from <SCHEMA>.v_cust_sales;
select * from <SCHEMA>.v_cust_sales order by cust_id limit 3;
select last_value from <SCHEMA>.orders_seq;psql -h localhost -U <USERNAME> <DB_NAME> -e -f mm_pg_check.sql결과 해석: 실습 결과는 CUST 100,000, ORDERS 1,000,000, 뷰 100,000행에 합계 250444357921.73, 시퀀스 last_value 1000001임-e는 실행한 SQL을 결과 앞에 찍지만 \dt, \d 같은 메타 명령은 찍지 않음
#Oracle 쪽 같은 값 확인
set lines 120
set numwidth 20
col cust_nm format a20
select count(*) from cust;
select count(*) from orders;
select count(*), sum(total_amt) from v_cust_sales;
select * from v_cust_sales order by cust_id fetch first 3 rows only;결과 해석: 건수, 뷰 합계, 앞 3행이 PostgreSQL과 같음set numwidth 20이 없으면 합계가 지수 형식으로 찍힘
#업무 쿼리 비교: 매출 상위 고객 5명
select * from (
select c.cust_id, c.cust_nm, sum(o.amount) amt
from cust c join orders o on o.cust_id = c.cust_id
group by c.cust_id, c.cust_nm
order by amt desc, c.cust_id)
where rownum <= 5select c.cust_id, c.cust_nm, sum(o.amount) amt
from cust c join orders o on o.cust_id = c.cust_id
group by c.cust_id, c.cust_nm
order by amt desc, c.cust_id
limit 5결과 해석: 실습에서는 두 결과가 5행 순서까지 같다고 적었음
금액이 같을 때를 대비해 c.cust_id로 순서를 고정함
#권한이 실제로 같은지
select grantee, table_name, privilege from user_tab_privs_made order by grantee, table_name, privilege;
select object_name, object_type, status from user_objects where object_type = 'PROCEDURE';\dp <SCHEMA>.*
\df <SCHEMA>.*
select has_table_privilege('<REPORT_USER>', '<SCHEMA>.cust', 'SELECT') as report_select,
has_table_privilege('<REPORT_USER>', '<SCHEMA>.cust', 'INSERT') as report_insert,
has_function_privilege('<USERNAME>', '<SCHEMA>.p_upgrade_grade(bigint)', 'EXECUTE') as user_execute;결과 해석: Oracle 쪽은 10행이 나오지만 PUBLIC ... INHERIT PRIVILEGES는 12c부터 사용자마다 자동으로 생기는 권한이라 이행 대상에서 빠져 9건이 됨
PostgreSQL 쪽은 t, f, t로, 보고용 역할은 SELECT만 받았음\dp의 글자는 r=SELECT, a=INSERT, w=UPDATE, d=DELETE, D=TRUNCATE, x=REFERENCES, t=TRIGGER, U=USAGE임
#가져온 프로시저 비교
\sf <SCHEMA>.p_upgrade_grade결과 해석: Oracle 실습 8줄과 비교하면 NUMBER 인자가 bigint로, 본문의 테이블 이름이 대상 스키마로 바뀌었고 나머지 로직은 같음
Oracle 실습에서는 USER_SOURCE에서 줄 번호 순으로 읽음(객체 상태와 소스 조회)
#사용 조건과 주의사항
문자 길이 비교에 CHAR 컬럼을 쓰면 값이 다름
Oracle length()는 CHAR의 뒷공백까지 세고 PostgreSQL char_length()는 char(n)의 뒷공백을 빼고 셈
실습에서는 문자 길이 검사를 VARCHAR2 컬럼만 대상으로 함
where rownum <= 3 order by...는 아무 행을 고른 뒤 정렬함
정렬한 안쪽 쿼리를 감싸 밖에서 자르거나 fetch first(12c 이상)를 씀
두 드라이버가 돌려주는 값의 모양(CHAR 뒷공백, 10과 10.00, 날짜 타입)이 달라 표본 행이 다르게 나올 수 있음
실습에서는 비교 전에 뒷공백 제거, 숫자 Decimal 정규화, 날짜 ISO 문자열로 맞춤
ora2pg 변환본은 본문에도 원래 스키마 이름이 박혀 있어, 그대로 가져오면 다른 스키마의 테이블을 고침
실습에서는 정의 전체의 스키마 접두어를 대상 스키마로 바꾸고 \sf로 확인함
PostgreSQL에서 대상 계정으로 테이블을 만들면 그 계정이 소유자라 Oracle보다 권한이 많게 보임(arwdDxt)
실습에서는 이를 "일치(소유자)"로 보고, 소유자를 바꾸려면 이행 후 alter table ... owner to를 쓴다고 적었음
표본 5행 비교는 표본임
실습의 "운영 전환 가능" 판정이 모든 행의 일치를 뜻하지는 않음