안녕하세요. 오랜만에 방문하네요.
이번에 아래와 같은 요구사항이 발생하여 쿼리를 짜고 있는데, 결과테이블의 EXCLUDE_RIGHT 부분을 헤매고 있습니다. 게다가 하단의 작성중인 쿼리는 처리속도도 늦어 여러 고수님들의 도움을 구하고자 합니다.
더 나은 방법이 있다면 주저마시고 조언 부탁드립니다. (아래 코드 테이블과 유통권리 테이블은 샘플링한 테이블로 앨범은 약 5만개, 국가전체코드는 약 260개입니다.)
오라클 11G를 사용합니다.
ㅇ 설명
- 앨범별로 유통가능한 국가를 표기하는 것이 목표.
- 미주지역코드 : AMERICA_CODES, 아시아지역코드 : ASIA_CODES, 전체국가코드 : ALL_CODES 로 구분된 코드테이블이 존재하며,
- 중국(CN)은 아시아지역코드에서 따로 빼냈으며, 전체국가코드에는 포함되어 있다. 전체국가코드에는 전세계를 의미하는 'WW'가 존재한다.
- 유통권한 테이블의 METHOD는 유통국가를 포함(INCLUDE)할 것인지 제외(EXCLUDE)할 것인지를 의미
- 1개의 앨범에는 1개 이상의 다수의 국가가 존재할 수 있으나, METHOD는 INCLUDE 또는 EXCLUDE 한 종류만 존재함(예컨데 41 앨범에는 INCLUDE만 존재하고, 50 앨범에는 EXCLUDE 만 존재할 수 있음)
- 39 앨범은 WW (전세계)에 앨범을 유통할 수 있다 =>모든 국가에서 유통가능, 미주권리 'O', 아시아 'O', 중국 'O'
- 40 앨범은 WW (전세계)에 앨범을 유통할 수 없다.=>모든국가에서 유통불가, 미주권리 'X', 아시아 'X', 중국 'X'
- 41 앨범은 WW (전세계), AR 국가에 앨범을 유통할 수 있다. =>모든국가에서 유통가능, 미주권리 'O', 아시아 'O', 중국 'O'
- 50 앨범은 WW (전세계), KR 국가에 앨범을 유통할 수 없다. =>모든국가에서 유통불가능, 미주권리 'X', 아시아 'X', 중국 'X'
- 61 앨범은 AR, HT, KR에서만 앨범을 유통할 수 있다. => 미주권리 'AR,HT', 아시아 'KR', 중국 'X'
- 70 앨범은 AR 에서만 앨범을 유통할 수 없다. '-' 표시는 해당 국가는 빼고 유통 할 수 있다는 의미다. => 미주권리 '-AR', 아시아 'O', 중국 'O'
- EXCLUDE_RIGHT는 유통 할 수 없는 모든 국가코드를 표시한다. 모든 국가에 유통할 수 없는 경우에도 국가코드를 모두 표시한다. WW 로 축약해도 무방, 국가코드의 정렬순서는 알파벳순서 또는 미정렬도 무방함
ㅇ 앨범테이블
CREATE TABLE ALBUM AS WITH ALBUM AS (SELECT '39' ALBUM_ID, '앨범명39' ALBUM_NAME FROM DUAL UNION ALL SELECT '40' ALBUM_ID, '앨범명40' ALBUM_NAME FROM DUAL UNION ALL SELECT '41' ALBUM_ID, '앨범명41' ALBUM_NAME FROM DUAL UNION ALL SELECT '50' ALBUM_ID, '앨범명50' ALBUM_NAME FROM DUAL UNION ALL SELECT '61' ALBUM_ID, '앨범명61' ALBUM_NAME FROM DUAL UNION ALL SELECT '70' ALBUM_ID, '앨범명70' ALBUM_NAME FROM DUAL UNION ALL SELECT '71' ALBUM_ID, '앨범명71' ALBUM_NAME FROM DUAL UNION ALL SELECT '80' ALBUM_ID, '앨범명80' ALBUM_NAME FROM DUAL UNION ALL SELECT '90' ALBUM_ID, '앨범명90' ALBUM_NAME FROM DUAL UNION ALL SELECT '91' ALBUM_ID, '앨범명91' ALBUM_NAME FROM DUAL) SELECT * FROM ALBUM;
ㅇ 코드 테이블
CREATE TABLE CODE AS WITH CODE AS (SELECT 'AMERICA_CODES' CODE_ID, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT 'AMERICA_CODES' CODE_ID, 'HT' CODE_VAL FROM DUAL UNION ALL SELECT 'AMERICA_CODES' CODE_ID, 'US' CODE_VAL FROM DUAL UNION ALL SELECT 'AMERICA_CODES' CODE_ID, 'AI' CODE_VAL FROM DUAL UNION ALL SELECT 'ASIA_CODES' CODE_ID, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT 'ASIA_CODES' CODE_ID, 'VN' CODE_VAL FROM DUAL UNION ALL SELECT 'ASIA_CODES' CODE_ID, 'PH' CODE_VAL FROM DUAL UNION ALL SELECT 'ASIA_CODES' CODE_ID, 'HK' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'HT' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'US' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'AI' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'VN' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'PH' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'HK' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'CN' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'WW' CODE_VAL FROM DUAL) SELECT * FROM CODE;
ㅇ 유통권리 테이블
CREATE TABLE ALBUM_RIGHT AS WITH ALBUM_RIGHT AS (SELECT '39' ALBUM_ID, 'INCLUDE' METHOD, 'WW' CODE_VAL FROM DUAL UNION ALL SELECT '40' ALBUM_ID, 'EXCLUDE' METHOD, 'WW' CODE_VAL FROM DUAL UNION ALL SELECT '41' ALBUM_ID, 'INCLUDE' METHOD, 'WW' CODE_VAL FROM DUAL UNION ALL SELECT '41' ALBUM_ID, 'INCLUDE' METHOD, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT '50' ALBUM_ID, 'EXCLUDE' METHOD, 'WW' CODE_VAL FROM DUAL UNION ALL SELECT '50' ALBUM_ID, 'EXCLUDE' METHOD, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT '61' ALBUM_ID, 'INCLUDE' METHOD, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT '61' ALBUM_ID, 'INCLUDE' METHOD, 'HT' CODE_VAL FROM DUAL UNION ALL SELECT '61' ALBUM_ID, 'INCLUDE' METHOD, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT '70' ALBUM_ID, 'EXCLUDE' METHOD, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT '71' ALBUM_ID, 'EXCLUDE' METHOD, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT '80' ALBUM_ID, 'EXCLUDE' METHOD, 'US' CODE_VAL FROM DUAL UNION ALL SELECT '80' ALBUM_ID, 'EXCLUDE' METHOD, 'AI' CODE_VAL FROM DUAL UNION ALL SELECT '90' ALBUM_ID, 'EXCLUDE' METHOD, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT '90' ALBUM_ID, 'EXCLUDE' METHOD, 'HT' CODE_VAL FROM DUAL UNION ALL SELECT '90' ALBUM_ID, 'EXCLUDE' METHOD, 'US' CODE_VAL FROM DUAL UNION ALL SELECT '91' ALBUM_ID, 'EXCLUDE' METHOD, 'CN' CODE_VAL FROM DUAL ) SELECT * FROM ALBUM_RIGHT;
ㅇ 희망하는 결과 테이블
ALBUM_ID | ALBUM_NAME | AMERICA_RIGHT | ASIA_RIGHT | CHINA_RIGHT | EXCLUDE_RIGHT |
39 | 앨범명39 | O | O | O | |
40 | 앨범명40 | X | X | X | AR,HT,US,AI,KR,VN,PH,HK,CN |
41 | 앨범명41 | O | O | O | |
50 | 앨범명50 | X | X | X | AR,HT,US,AI,KR,VN,PH,HK,CN |
61 | 앨범명61 | AR,HT | KR | X | US,AI,VN,PH,HK,CN |
70 | 앨범명70 | -AR | O | O | AR |
71 | 앨범명71 | O | -KR | O | KR |
80 | 앨범명80 | -AI,-US | O | O | AI,US |
90 | 앨범명90 | -AR,-HT,-US | O | O | AR,HT,US |
91 | 앨범명91 | O | O | X | CN |
ㅇ 본인이 작성중인 쿼리(아직 EXCLUDE_RIGHT 미완성 ㅠ_ㅠ)
SELECT ALBUM_ID , METHOD , CASE WHEN WORLD = 'WW' THEN 'O' WHEN WORLD = '-WW' THEN 'X' ELSE NVL(AMERICA, DECODE(METHOD, 'INCLUDE', 'X', 'O')) END AMERICA , CASE WHEN WORLD = 'WW' THEN 'O' WHEN WORLD = '-WW' THEN 'X' ELSE NVL(ASIA, DECODE(METHOD, 'INCLUDE', 'X', 'O')) END ASIA , CASE WHEN WORLD = 'WW' THEN 'O' WHEN WORLD = '-WW' THEN 'X' WHEN CHINA = 'CN' THEN 'O' WHEN CHINA = '-CN' THEN 'X' ELSE DECODE(METHOD, 'INCLUDE', 'X', 'O') END CHINA FROM ( SELECT ALBUM_ID , METHOD , LISTAGG(AMERICA, ',') WITHIN GROUP (ORDER BY CODE_VAL) AMERICA , LISTAGG(ASIA, ',') WITHIN GROUP (ORDER BY CODE_VAL) ASIA , MAX(WORLD) WORLD , MAX(CHINA) CHINA FROM ( SELECT ALBUM_ID , CODE_VAL , METHOD , CASE WHEN CODE_VAL = 'WW' THEN DECODE(METHOD, 'EXCLUDE', '-') || CODE_VAL END WORLD , CASE WHEN CODE_VAL IN (SELECT CODE_VAL FROM CODE WHERE CODE_ID = 'AMERICA_CODES') THEN DECODE(METHOD, 'EXCLUDE', '-') || CODE_VAL END AMERICA , CASE WHEN CODE_VAL IN (SELECT CODE_VAL FROM CODE WHERE CODE_ID = 'ASIA_CODES') THEN DECODE(METHOD, 'EXCLUDE', '-') || CODE_VAL END ASIA , CASE WHEN CODE_VAL = 'CN' THEN DECODE(METHOD, 'EXCLUDE', '-') || CODE_VAL END CHINA FROM ALBUM_RIGHT ) GROUP BY ALBUM_ID , METHOD)
WITH ALBUM AS (SELECT '39' ALBUM_ID, '앨범명39' ALBUM_NAME FROM DUAL UNION ALL SELECT '40' ALBUM_ID, '앨범명40' ALBUM_NAME FROM DUAL UNION ALL SELECT '41' ALBUM_ID, '앨범명41' ALBUM_NAME FROM DUAL UNION ALL SELECT '50' ALBUM_ID, '앨범명50' ALBUM_NAME FROM DUAL UNION ALL SELECT '61' ALBUM_ID, '앨범명61' ALBUM_NAME FROM DUAL UNION ALL SELECT '70' ALBUM_ID, '앨범명70' ALBUM_NAME FROM DUAL UNION ALL SELECT '71' ALBUM_ID, '앨범명71' ALBUM_NAME FROM DUAL UNION ALL SELECT '80' ALBUM_ID, '앨범명80' ALBUM_NAME FROM DUAL UNION ALL SELECT '90' ALBUM_ID, '앨범명90' ALBUM_NAME FROM DUAL UNION ALL SELECT '91' ALBUM_ID, '앨범명91' ALBUM_NAME FROM DUAL), CODE AS (SELECT 'AMERICA_CODES' CODE_ID, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT 'AMERICA_CODES' CODE_ID, 'HT' CODE_VAL FROM DUAL UNION ALL SELECT 'AMERICA_CODES' CODE_ID, 'US' CODE_VAL FROM DUAL UNION ALL SELECT 'AMERICA_CODES' CODE_ID, 'AI' CODE_VAL FROM DUAL UNION ALL SELECT 'ASIA_CODES' CODE_ID, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT 'ASIA_CODES' CODE_ID, 'VN' CODE_VAL FROM DUAL UNION ALL SELECT 'ASIA_CODES' CODE_ID, 'PH' CODE_VAL FROM DUAL UNION ALL SELECT 'ASIA_CODES' CODE_ID, 'HK' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'HT' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'US' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'AI' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'VN' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'PH' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'HK' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'CN' CODE_VAL FROM DUAL UNION ALL SELECT 'ALL_CODES' CODE_ID, 'WW' CODE_VAL FROM DUAL), ALBUM_RIGHT AS (SELECT '39' ALBUM_ID, 'INCLUDE' METHOD, 'WW' CODE_VAL FROM DUAL UNION ALL SELECT '40' ALBUM_ID, 'EXCLUDE' METHOD, 'WW' CODE_VAL FROM DUAL UNION ALL SELECT '41' ALBUM_ID, 'INCLUDE' METHOD, 'WW' CODE_VAL FROM DUAL UNION ALL SELECT '41' ALBUM_ID, 'INCLUDE' METHOD, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT '50' ALBUM_ID, 'EXCLUDE' METHOD, 'WW' CODE_VAL FROM DUAL UNION ALL SELECT '50' ALBUM_ID, 'EXCLUDE' METHOD, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT '61' ALBUM_ID, 'INCLUDE' METHOD, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT '61' ALBUM_ID, 'INCLUDE' METHOD, 'HT' CODE_VAL FROM DUAL UNION ALL SELECT '61' ALBUM_ID, 'INCLUDE' METHOD, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT '70' ALBUM_ID, 'EXCLUDE' METHOD, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT '71' ALBUM_ID, 'EXCLUDE' METHOD, 'KR' CODE_VAL FROM DUAL UNION ALL SELECT '80' ALBUM_ID, 'EXCLUDE' METHOD, 'US' CODE_VAL FROM DUAL UNION ALL SELECT '80' ALBUM_ID, 'EXCLUDE' METHOD, 'AI' CODE_VAL FROM DUAL UNION ALL SELECT '90' ALBUM_ID, 'EXCLUDE' METHOD, 'AR' CODE_VAL FROM DUAL UNION ALL SELECT '90' ALBUM_ID, 'EXCLUDE' METHOD, 'HT' CODE_VAL FROM DUAL UNION ALL SELECT '90' ALBUM_ID, 'EXCLUDE' METHOD, 'US' CODE_VAL FROM DUAL UNION ALL SELECT '91' ALBUM_ID, 'EXCLUDE' METHOD, 'CN' CODE_VAL FROM DUAL ), CODES AS -- ALL_CODES 관련 수정해야하지 않을지요. (SELECT CODE_ID, CODE_VAL FROM CODE WHERE CODE_ID != 'ALL_CODES' UNION ALL SELECT 'ALL_CODES', 'WW' FROM DUAL UNION ALL SELECT 'CHINA', 'CN' FROM DUAL) SELECT ALBUM_ID, AMERICA, ASIA, CHINA, REGEXP_REPLACE( CASE WHEN AMERICA || ASIA || CHINA = 'OOO' THEN NULL WHEN AMERICA || ASIA || CHINA = 'XXX' THEN D.CODES WHEN METHOD = 'EXCLUDE' THEN REPLACE(DECODE(AMERICA, 'O', NULL, 'X', (SELECT LISTAGG(CODE_VAL, ',') WITHIN GROUP(ORDER BY CODE_VAL) FROM CODES WHERE CODE_ID = 'AMERICA_CODES'), AMERICA) || DECODE(ASIA, 'O', NULL, 'X', (SELECT LISTAGG(CODE_VAL, ',') WITHIN GROUP(ORDER BY CODE_VAL) FROM CODES WHERE CODE_ID = 'ASIA_CODES'), ASIA) || DECODE(CHINA, 'O', NULL, 'X', (SELECT LISTAGG(CODE_VAL, ',') WITHIN GROUP(ORDER BY CODE_VAL) FROM CODES WHERE CODE_ID = 'CHINA'), CHINA), '-', '') ELSE REGEXP_REPLACE(D.CODES, REPLACE(AMERICA || '|' || ASIA || '|' || CHINA,',','|')) END,',+',',') EXCLUDE_RIGHT FROM (SELECT ALBUM_ID, NVL(MAX(AMERICA), DECODE(MAX(METHOD), 'EXCLUDE', 'O', 'X')) AMERICA, NVL(MAX(ASIA), DECODE(MAX(METHOD), 'EXCLUDE', 'O', 'X')) ASIA, NVL(MAX(CHINA), DECODE(MAX(METHOD), 'EXCLUDE', 'O', 'X')) CHINA, MAX(METHOD) METHOD FROM (SELECT CASE WHEN METHOD = 'INCLUDE' AND INSTR(CODES2, 'WW') > 0 THEN 'O' WHEN METHOD = 'EXCLUDE' AND INSTR(CODES2, 'WW') > 0 THEN 'X' WHEN METHOD = 'INCLUDE' AND A.CODE_ID = 'AMERICA_CODES' AND CNT1 = CNT2 THEN 'O' WHEN METHOD = 'EXCLUDE' AND A.CODE_ID = 'AMERICA_CODES' AND CNT1 = CNT2 THEN 'X' WHEN METHOD = 'INCLUDE' AND A.CODE_ID = 'AMERICA_CODES' THEN CODES2 WHEN METHOD = 'EXCLUDE' AND A.CODE_ID = 'AMERICA_CODES' THEN CODES2 END AMERICA, CASE WHEN METHOD = 'INCLUDE' AND INSTR(CODES2, 'WW') > 0 THEN 'O' WHEN METHOD = 'EXCLUDE' AND INSTR(CODES2, 'WW') > 0 THEN 'X' WHEN METHOD = 'INCLUDE' AND A.CODE_ID = 'ASIA_CODES' AND CNT1 = CNT2 THEN 'O' WHEN METHOD = 'EXCLUDE' AND A.CODE_ID = 'ASIA_CODES' AND CNT1 = CNT2 THEN 'X' WHEN METHOD = 'INCLUDE' AND A.CODE_ID = 'ASIA_CODES' THEN CODES2 WHEN METHOD = 'EXCLUDE' AND A.CODE_ID = 'ASIA_CODES' THEN CODES2 END ASIA, CASE WHEN METHOD = 'INCLUDE' AND INSTR(CODES2, 'WW') > 0 THEN 'O' WHEN METHOD = 'EXCLUDE' AND INSTR(CODES2, 'WW') > 0 THEN 'X' WHEN METHOD = 'INCLUDE' AND A.CODE_ID = 'CHINA' AND CNT1 = CNT2 THEN 'O' WHEN METHOD = 'EXCLUDE' AND A.CODE_ID = 'CHINA' AND CNT1 = CNT2 THEN 'X' WHEN METHOD = 'INCLUDE' AND A.CODE_ID = 'CHINA' THEN CODES2 WHEN METHOD = 'EXCLUDE' AND A.CODE_ID = 'CHINA' THEN CODES2 END CHINA, A.* FROM (SELECT ALBUM_ID, B.CODE_ID, A.METHOD, LISTAGG(DECODE(A.METHOD, 'EXCLUDE', '-') || A.CODE_VAL, ',') WITHIN GROUP(ORDER BY A.CODE_VAL) CODES2, COUNT(DISTINCT A.CODE_VAL) CNT2 FROM ALBUM_RIGHT A, CODES B WHERE A.CODE_VAL = B.CODE_VAL GROUP BY ALBUM_ID, B.CODE_ID, A.METHOD) A, (SELECT CODE_ID, LISTAGG(CODE_VAL, ',') WITHIN GROUP(ORDER BY CODE_VAL) CODES1, COUNT(DISTINCT CODE_VAL) CNT1 FROM CODES GROUP BY CODE_ID) B WHERE A.CODE_ID = B.CODE_ID) GROUP BY ALBUM_ID) C, (SELECT LISTAGG(CODE_VAL, ',') WITHIN GROUP(ORDER BY CODE_VAL) CODES FROM CODES WHERE CODE_ID != 'ALL_CODES') D ORDER BY ALBUM_ID
각 행마다 code 테이블에 접근하는 것 때문에 성능저하가 있지 않나해서
바꿔보았습니다.
code에 all_codes는 'ww'만 남기는 게 효율적이지 않나 생각합니다.
'cn'은 따로 code_id를 따구요.