DBMS LOG용 디렉토리 생성

CREATE OR REPLACE DIRECTORY DIR_BDUMP
AS
'/oracle/admin/SID/bdump/';

DBMS LOG 외부 테이블 생성

CREATE TABLE DBMSLOG (
LOG_TEXT VARCHAR2(4000)
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DIR_BDUMP
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY '~'
MISSING FIELD VALUES ARE NULL (
LOG_TEXT CHAR(4000)
)
)
LOCATION ('alert_SID.log')
)
REJECT LIMIT UNLIMITED;

DBMS LOG 뷰 생성

CREATE OR REPLACE VIEW DBMSLOG_VI
AS
SELECT LOG_RNUM
, LAST_VALUE(LOG_LNUM IGNORE NULLS)
OVER(ORDER BY LOG_RNUM ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) LOG_SNUM
, LAST_VALUE(LOG_DATE IGNORE NULLS)
OVER(ORDER BY LOG_RNUM ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) LOG_DATE
, TRC_FILE
, LOG_TEXT
FROM (SELECT ROWNUM LOG_RNUM
, NVL2(LOG_DATE, ROWNUM, NULL) LOG_LNUM
, LOG_DATE
, TRC_FILE
, LOG_TEXT
FROM (SELECT CASE REGEXP_INSTR(LOG_TEXT,
'[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}'
|| ' [[:digit:]]{4}')
WHEN 0 THEN NULL
ELSE TO_DATE(LOG_TEXT
, 'Dy Mon DD HH24:MI:SS YYYY'
, 'NLS_DATE_LANGUAGE=AMERICAN')
END LOG_DATE
, CASE REGEXP_INSTR(LOG_TEXT, '^Errors in file')
WHEN 1 THEN REGEXP_REPLACE(LOG_TEXT
, '^Errors in file (.*):$'
, '\1')
ELSE NULL
END TRC_FILE
, LOG_TEXT
FROM DBMSLOG));

COMMENT ON TABLE DBMSLOG_VI IS 'DBMS Alert 로그 뷰';

COMMENT ON COLUMN DBMSLOG_VI.LOG_RNUM IS 'DBMS Alert 로그 순번';
COMMENT ON COLUMN DBMSLOG_VI.LOG_SNUM IS 'DBMS Alert 로그 시작 순번 그룹';
COMMENT ON COLUMN DBMSLOG_VI.LOG_DATE IS 'DBMS Alert 로그 일시';
COMMENT ON COLUMN DBMSLOG_VI.TRC_FILE IS 'DBMS Alert Trace 파일';
COMMENT ON COLUMN DBMSLOG_VI.LOG_TEXT IS 'DBMS Alert 로그 내용';

DBMS LOG 파일 조회

SELECT A.LOG_DATE
, A.TRC_FILE
, A.LOG_TEXT
FROM (SELECT TO_CHAR(MIN(LOG_DATE), 'YYYY-MM-DD HH24:MI:SS') LOG_DATE
, SUBSTR(XMLAGG(XMLELEMENT(TEMP, CHR(10) || TRC_FILE)
ORDER BY LOG_RNUM)
.EXTRACT('//text()').GETSTRINGVAL(), 2) TRC_FILE
, SUBSTR(XMLAGG(XMLELEMENT(TEMP, CHR(10) || LOG_TEXT)
ORDER BY LOG_RNUM)
.EXTRACT('//text()').GETSTRINGVAL(), 2) LOG_TEXT
FROM DBMSLOG_VI
WHERE LOG_DATE BETWEEN TRUNC(SYSDATE - :DAY)
AND SYSDATE - :DAY
GROUP BY LOG_SNUM
ORDER BY LOG_SNUM DESC) A
WHERE LOG_TEXT LIKE '%' || :TEXT || '%';

참고

Query the Oracle Alert Log using SQL commands
Oracle Regular Expression(정규표현식)

iBATIS에서 질의를 만들다 보면 다음과 같은 오류가 난다.

ORA-00911: 문자가 부적합합니다
ORA-00911: invalid Character

실제 내용이된 SQL이 잘못된 것이 아니라면 제일 마지막에 세미콜론(“;”)을 붙이지 않았나 의심해 보자.

CLOB를 선택해야 하는 문제로 테스트 중에 SELECT 문에서 나는 위와 같은 오류를 TypeHandler를 잘못 등록했거나 JDBC 드라이버, NativeJdbcExtractor, LobHander 문제로 착각하고 몇 시간을 헤렸다. -_-;;

이름 변경 문법

alter table info rename constraints pk_info to pk_info_old;
alter index pk_info rename to pk_info_old;
alter table info rename to info_old;

LOB 생성 문법

create table info
(
id varchar2(40) not null,
pid varchar2(40) not null,
type varchar2(2) not null,
desc varchar2(200),
value clob,
creator varchar2(20) not null,
created date default sysdate not null,
updater varchar2(20) not null,
updated date default sysdate not null
)
tablespace tblspace
pctused 40
pctfree 10
lob (domain_prop_value) store as (
index lx_info_01 (tablespace idxspace)
tablespace tblspace
);

만족하는 삶이란 어떤 것일까? 다른 말로 하면 행복한 삶은 어떻게 만들어 질까 하는 것이다.

노래를 부를 때, 어떻게 하면 제일 즐겁게 부를 수 있을까?

우선 자신이 고른 노래에 대해 충분히 알고 있어야 하고, 박자와 음정을 놓치지 않아야 하며, 자신이 좋아하는 노래를 택해야 할 것이라 생각한다.

이것을 삶에 적용시켜 보면, 반주는 주변 환경이요, 노래부르는 행위는 내 행동에 대치시킬 수 있다.

내가 모르는 노래를 고르게 되면 부르는 맛이 없다. 남이 불러 듣기 좋았던 노래를 멋모르고 고르게 된다면 중간 중간 알지 못해 끊어먹는 소절들로 인해 충분히 만족하지 못하게 된다. 아는 노래라는 것은 내가 충분히 알고 있는 상황에 비견할 수 있다. 어떤 식으로 주변 환경이 변해왔고, 다음은 어떤 방식으로 변화할지를 알고 있어야 하는 것이다.

다음으로 음정과 박자가 있다. 너무 뒤쳐지는 것이나 너무 앞서나가는 것은 노래를 망치게 된다. 변화에 맞는 조화로운 삶이 필요한 것이다. 너무 뒤쳐지면 끌려가는 삶을 살게 될 것이도, 너무 앞서가면 주변 환경이 따라오는 것을 기다려야 할 것이다. 딱 적당히 내가 한 발 정도만 앞서나가며 반주를 리드할 수 있는 지혜가 필요하다.

자신이 너무 잘 알고 있지만 물린 노래나 너무 잘하는 노래만 하는 것도 문제가 있다. 현재 자신이 만들어 놓은 지위와 환경에 안주하는 것이 이와 같을 것이다. 고인 물은 썩고, 냄비속에 들어간 개구리는 자신이 나중에 뜨거운 물에서 죽을 것이라는 것을 알지 못한다. 매번 조금씩 자신이 완성할 수 있는 만큼의 난이도를 목표로 삶아 정진하는 자세 또한 필요할 것이다.

실패하더라도 도전하는 것이 나중의 큰 자산이 될 수 있다는 진리는 그냥 있는 말이 아니다.

하던 일을 정리하고 조금의 휴식을 취하려고 생각하니 그 동안 참아왔던 Wish List가 날 괴롭힌다.

지금 bb.co.kr 사이트에 저장되어 있는 구매 대상만 기백만원 이상이 되어 있다. 전달 초 집을 새로 옮기면서 평소에 꾸미고 싶었던 가구와 주방 제품들에 눈이 돌아갔고, 방이 (혼자 쓰기에는) 넓다보니 거실 가구도 생각하게 되었다.

거실 가구는 배송 기간도 오래 걸리거니와 쓸만하다 싶은 것들은 그 효용 가치를 의심하게 만드는 극히 높은 가격에 할말을 잃었고, 현재 잠정 보류 상태다.

주방 제품 또한 만만치 않다. 먹는 것과 인테리어를 동시에 만족하는 것들은 거실 가구 만만치 않다.

다음으로 다음달에 예정인 여행 계획에 따라 그동안 미루어 왔던 카메라 관련 준비도 통잔 잔고에 밀려 가슴을 쓰리게 한다.

뭐 돈만 있다면야 이것 저것 마음대로 지를 수 있겠지만 쌓아놓은 돈이 없는 인생이다 보니 모두 벌어서 지출해야 하는 현실에 눈을 감을수는 없지 않은가.

현재 모 백화점 상품권이 50 가까이 모여 있다. 이걸 두고 이걸 돈으로 바꾸면 A를 지를 수 있겠다 하고 질러버린 후, B에 대해서도 같은 생각으로 접근하고, C에 대해서도 상품권으로 대신 지르는 셈이라고 치부하면서 구매한 것들이 여러 개이다. 하지만 상품권을 현금화 하지 못해 아직도 지를 여유가 있는 것처럼 착각하는 자신이 바보같기도 하다.

결국.. 꼭 필요한 것들부터 하나씩 모아가는 것이 제일 나은 것 같다. 그러기 위해서는, 이런 당연한 현실을 위태하게 하는 ‘휴식 시간의 웹서핑’을 당장 그만두어야 할 것이고, 소비는 정말 합리적인 것인지 일주일을 두고 고민한 후에 결정을 내려야 할 것이다.

무턱대로 질러대고, 장롱에 썩히는 케이스는 이제 그만.. ㅜㅠ

오늘과 내일은 집 근처 사무실이 아닌 여행에 가까운 거리에 있는 다른 장소에서 업무 지원을 해줘야 할 일이 있다. 그래서 일산에서부터 분당까지 출퇴근이 불가피 했다.

당초 생각은 2.5시간 정도 되는 거리가 무슨 문제가 될까 싶었다. 평소 읽지 못했던 책 한권과 친구로부터 강탈한 Game Boy Advanced Micro 기기와 함께라면 즐거운 출퇴근이 되지 싶었다.

막상 지하철에 들어서자 그 특유의 수면제 공격이 시작된다. 사람들이 소비한 산소가 이산화 탄소로 바뀌어 내 두뇌를 계속적으로 공격하였고, 결국은 그 공격에 함락되어 버렸다.

출근 혹은 퇴근이 1시간이 넘어갈 경우 몸이 피곤해 지는 이유가 여기에 있지 싶다. 물론 자기 차를 가지고 출퇴근할 경우는 행복한 경우이고 이걸 감내하는 사람 또한 주변에 많다. 하지만 대중 교통 수단, 특히 지하철일 경우 대부분의 사람들이 잠을 참지 못하고, 이것은 업무 시간까지 연결되어 평소의 내 역량을 발휘하지 못하는 지극히 평범한 직장인의 양태로 나타나게 된다.

결국 직장 근처로 집을 옮기고 상쾌한 직장 생활을 하는 것이 제일 좋지 않을까 한다.

온라인 재정의를 통한 LONG -> LOB 변환을 하던 중 Primary Key가 없을 경우 DBMS_REDEFINITION.START_REDEF_TABLE 호출시 다음과 같은 오류가 발생하였다.

ORA-12089: cannot online redefine table “KCUBE”.”TBR_RFM_QXDMMSG” with no primary key

PK가 없는 테이블에 대해서는 안되는 모양이다.. -_-;;

추가
PK가 반드시 존재해야 하는 이유는 Table의 재정의 작업이 MV를 통해 이루어지기 때문이었다. MV를 이용하는 시점은 SYNC_INTERIM_TABLE 호출시로 온라인 작업중 변경된 사항에 대해 SYNC를 맞추는 시점이다. 재정의시 PK를 생성하고 정의가 끝나면 DROP 하는 방식으로 작업을 하면 되겠다.

참고로, PK가 아닌 ROWID 방식으로도 가능하다. DBMS_REDEFINITION.CONS_USE_PK 대신에
DBMS_REDEFINITION.CONS_USE_ROWID를 사용하면 된다.

참고
Oracle Online을 통한 24시간 무정지 작업 구현

업무 중 테이블에 대한 컬럼 드랍 요청이 있어서 작업을 하려고 하니 다음과 같은 오류가 발생하였다.

ORA-39726: unsupported add/drop column operation on compressed tables

Compress된 테이블은 컬럼 추가/삭제가 지원되지 않는다면 말이다. ALTER로 테이블을 NOCOMPRESS로 변경하여도 신규로 들어가는 데이터에 대해서만 적용되지 기존의 압축된 데이터는 그대로이기 때문에 해결책이 되지 않는다.

이럴 경우 해당 테이블의 테이블 스페이스를 다른 곳으로 이동하면서 NOCOMPRESS 옵션을 사용하면 COMPRESS 설정이 풀리게 된다. 만약 파티션의 경우도 마찬가지로 파티션이 위치하는 테이블 스페이스를 다른 곳으로 이동하면 된다. 이후 DDL 작업이 완료된 후 다시 원래의 테이블 스페이스로 COMPRESS 옵션을 주어 원복하면 작업이 완료된다.

파티션된 테이블의 경우는 다음과 같은 방식으로 스크립트 생성이 가능하다.

select 'ALTER TABLE '
|| object_name
|| ' MOVE PARTITION '
|| subobject_name
|| ' TABLESPACE TS_NEW NOCOMPRESS PARALLEL 8 NOLOGGING;'
from user_objects
where object_type = 'TABLE PARTITION'
and object_name in(select tname
from tab)

문제는 다음과 같았다.

사용자가 로그인 화면에서 로그인을 요청하게 되면 사용자 인증을 한 후 사용자 정보를 Request 객체의 Session 영역에 넣어 놓고, 이후 다시 초기 페이지 요청시 사용자 정보가 있으면 바로 본 화면으로 넘어가는 방식으로 코드를 구현하였다.

하지만 로그인 이후 자동으로 본 화면 이동시 사용자 정보를 읽지 못하는 현상을 보였다.

문제는 iBATIS에서 얻어진 결과 객체를 바로 Session에 집어 넣는 실수로 인한 것으로 보여졌다. 아마도 iBATIS의 결과 객체의 레퍼런스를 잡고 있다가 일정 시간 이후에 메모리에서 지우는 이유 때문일 것이다.

그리하여 다음과 같이 기존 코드를 변경하였다.

기존
request.getSession().setAttribute("user", user);

변경
request.getSession().setAttribute("user", new HashMap(user));

iBATIS 결과 객체의 Life Cycle이 어떤 식으로 동작하는지 찾아봐야 할 것 같다.

Spring Framework을 사용하면서, IoC 기반의 코딩을 하기 위해 Interface를 앞단에 두고 이를 구현하는 방식으로 구현한 소스를 좀 뒤집었다.

추가적인 이종의 구현의 필요가 없어 개발의 편의성을 위해 앞단의 Interface를 모두 제거하는 식으로 변경을 하였다. Interface를 둘 경우 Eclipse 소스에서 Ctrl 키를 눌러 참조 구현부로 이동하는 기능이 Interface로 연결되면서 개발상 불편한 것도 변경의 이유가 되었다.

문제는 앞단의 Interface를 지우고 직접 Class를 올리게 되니 Spring에서 Bean을 생성하고 Proxy AOP를 거는 중 다음과 같은 오류가 발생하였다.

Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.

인터넷상에 나와있는 “JDK Dynamic Proxy와 CGLIB Proxy에 대한 이해“를 보면 2가지 종류의 Proxy가 이용되는 것으로 확인된다. 아마도 이전에 Interface 기반에서 JDK Dynamic Proxy가 사용된 것으로 보인다.

직접 Class에 대한 Proxy를 이용하기 위해서 Spring Framework의 “lib/cglib” 위치에 있는 “cglib-nodep-2.1_3.jar” 라이브러리를 Class Path에 추가해 주면 된다.

RSS Flickr

  • 오류가 발생하였습니다. 피드가 사용 불가능일지 모릅니다. 나중에 다시 시도하십시오.

RSS eMotion Blog

  • 오류가 발생하였습니다. 피드가 사용 불가능일지 모릅니다. 나중에 다시 시도하십시오.

  • 128,776 hits