-- 비효율적으로 작성된 SQL
INSERT INTO 월별요금납부실적
(고객번호, 납입월, 지로, 자동이체, 신용카드, 핸드폰, 인터넷)
SELECT K.고객번호
, '200903' 납입월
, A.납입금액 지로
, B.납입금액 자동이체
, C.납입금액 신용카드
, D.납입금액 핸드폰
, E.납입금액 인터넷
FROM 고객 K
,(SELECT 고객번호, 납입금액
FROM 월별납입방법별집계
WHERE 납입원 = '200903'
AND 납입방법코드 = 'A') A
,(SELECT 고객번호, 납입금액
FROM 월별납입방법별집계
WHERE 납입원 = '200903'
AND 납입방법코드 = 'B') B
,(SELECT 고객번호, 납입금액
FROM 월별납입방법별집계
WHERE 납입원 = '200903'
AND 납입방법코드 = 'C') C
,(SELECT 고객번호, 납입금액
FROM 월별납입방법별집계
WHERE 납입원 = '200903'
AND 납입방법코드 = 'D') D
,(SELECT 고객번호, 납입금액
FROM 월별납입방법별집계
WHERE 납입원 = '200903'
AND 납입방법코드 = 'E') E
WHERE A.고객번호(+) = K.고객번호
AND B.고객번호(+) = K.고객번호
AND C.고객번호(+) = K.고객번호
AND D.고객번호(+) = K.고객번호
AND E.고객번호(+) = K.고객번호
AND NVL(A.납입금액,0) + NVL(B.납입금액,0) + NVL(C.납입금액,0) + NVL(D.납입금액,0) + NVL(E.납입금액,0) > 0
-- 효율적으로 작성된 SQL
INSERT INTO 월별요금납부실적
(고객번호, 납입월, 지로, 자동이체, 신용카드, 핸드폰, 인터넷)
SELECT K.고객번호
, 납입월
, NVL(SUM(CASE WHEN 납입방법코드 = 'A' THEN 납입금액 END), 0) 지로
, NVL(SUM(CASE WHEN 납입방법코드 = 'B' THEN 납입금액 END), 0) 자동이체
, NVL(SUM(CASE WHEN 납입방법코드 = 'C' THEN 납입금액 END), 0) 신용카드
, NVL(SUM(CASE WHEN 납입방법코드 = 'D' THEN 납입금액 END), 0) 핸드폰
, NVL(SUM(CASE WHEN 납입방법코드 = 'E' THEN 납입금액 END), 0) 인터넷
FROM 월별납입방법별집계
WHERE 납입월 = '200903'
GROUP BY 고객번호, 납입월;
create table copy_t ( no number, no2 varchar2(2) ) ;
select * from emp a, copy_t b
where b.no <= 2;
-- emp 테이블을 2배로 복제
select * from emp a, (select rownum no from dual connect by level <= 2) b;
카드상품분류와 고객등급 기준으로 거래실적을 집계하면서 소계까지 한번에 구하는 쿼리
select a.카드상품분류
,(case when b.no = 1 then a.고객등급 else '소계' end) as 고객등급
, sum(a.거래금액) as 거래금액
from (select 카드.카드상품분류 as 카드상품분류
, 고객.고객등급 as 고객등급
, sum(거래금액) as 거래금액
from 카드월실적
, 카드
, 고객
where 실적년월 = '201008'
and 카드.카드번호 = 카드월실적.카드번호
and 고객,고객번호 = 카드.고객번호
group by 카드.카드상품분류, 고객.고객등급) a
, copy_t b
where b.no <= 2
group by a.카드상품분류, b.no, (case when b.no = 1 then a.고객등급 else '소계' end)
카드상품분류 | 고객등급 | 거래금액 |
---|---|---|
상품분류A | VIP | 500000000 |
상품분류A | 일반 | 300000000 |
상품분류B | VIP | 100000000 |
상품분류B | 일반 | 50000000 |
카드상품분류 | 고객등급 | 거래금액 | b.no |
---|---|---|---|
상품분류A | VIP | 500000000 | 1 |
상품분류A | 일반 | 300000000 | 1 |
상품분류B | VIP | 100000000 | 1 |
상품분류B | 일반 | 50000000 | 1 |
상품분류A | VIP | 500000000 | 2 |
상품분류A | 일반 | 300000000 | 2 |
상품분류B | VIP | 100000000 | 2 |
상품분류B | 일반 | 50000000 | 2 |
카드상품분류 | 고객등급 | 거래금액 |
---|---|---|
상품분류A | VIP | 500000000 |
상품분류A | 일반 | 300000000 |
상품분류A | 소계 | 800000000 |
상품분류B | VIP | 100000000 |
상품분류B | 일반 | 50000000 |
상품분류B | 소계 | 150000000 |
-- 상품, 연월 기준으로 group by를 먼저 수행하고 나면 두 집합은 1:1 관계가 되므로
-- Full Outer Join을 통해 원하는 결과집합을 얻을 수 있음.
select nvl(a.상품, b.상품) as 상품
, nvl(a.계획연월, b.판매연월) as 연월
, nvl(계획수량, 0) 계획수량
, nvl(판매수량, 0) 판매수량
from (select 상품
, 계획연월
, sum(계획수량) 계획수량
from 부서별판매계획
where 계획연월 between '200901' and '200903'
group by 상품, 계획연월 ) a
full outer join
(select 상품
, 판매연월
, sum(판매수량) 판매수량
from 채널별판매실적
where 판매연월 between '200901' and '200903'
group by 상품, 판매연월 ) b
on a.상품 = b.상품
and a.계획연월 = b.판매연월
-- DBMS와 버전에 따라 Full Outer Join 시 테이블이 반복 액세스 되는 비효율이 있기도 함.
drop table dept_sell_plan;
create table dept_sell_plan(prod varchar2(1), yyyymm varchar2(6), dept varchar2(2), amt number);
insert into dept_sell_plan values('A', '200901', 'A1', 100);
insert into dept_sell_plan values('A', '200902', 'A1', 120);
insert into dept_sell_plan values('A', '200903', 'A1', 150);
insert into dept_sell_plan values('A', '200901', 'B1', 200);
insert into dept_sell_plan values('A', '200902', 'B1', 300);
insert into dept_sell_plan values('B', '200901', 'A1', 400);
insert into dept_sell_plan values('B', '200901', 'B1', 500);
insert into dept_sell_plan values('B', '200902', 'B1', 600);
insert into dept_sell_plan values('C', '200901', 'C1', 800);
insert into dept_sell_plan values('C', '200902', 'C1', 900);
--create unique index dept_idx1 on dept_sell_plan(prod, yyyymm, dept);
drop table channel_sell;
create table channel_sell(prod varchar2(1), yyyymm varchar2(6), channel varchar2(2), amt number);
insert into channel_sell values('A', '200901', 'AA', 200);
insert into channel_sell values('A', '200902', 'AA', 150);
insert into channel_sell values('A', '200901', 'BB', 500);
insert into channel_sell values('B', '200901', 'BB', 300);
insert into channel_sell values('B', '200902', 'BB', 200);
insert into channel_sell values('B', '200903', 'BB', 100);
insert into channel_sell values('C', '200901', 'CC', 400);
insert into channel_sell values('C', '200902', 'CC', 500);
insert into channel_sell values('C', '200903', 'CC', 600);
--create unique index channel_idx1 on channel_sell(prod, yyyymm, channel);
select nvl(a.prod, b.prod) as prod
, nvl(a.yyyymm, b.yyyymm) as yyyymm
, nvl(a.amt, 0) plan_amt
, nvl(b.amt, 0) amt
from (select prod
, yyyymm
, sum(amt) amt
from dept_sell_plan
where yyyymm between '200901' and '200903'
group by prod, yyyymm ) a
full outer join
(select prod
, yyyymm
, sum(amt) amt
from channel_sell
where yyyymm between '200901' and '200903'
group by prod, yyyymm ) b
on a.prod = b.prod
and a.yyyymm = b.yyyymm
order by prod, yyyymm
;
-- 11g부터 _optimizer_native_full_outer_join = FORCE 로 설정되어 있으므로 Union All과 Anti Join이 사라짐.
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 400 | 10 (40)| 00:00:01 |
| 1 | SORT ORDER BY | | 10 | 400 | 10 (40)| 00:00:01 |
| 2 | VIEW | VW_FOJ_0 | 10 | 400 | 9 (34)| 00:00:01 |
|* 3 | HASH JOIN FULL OUTER| | 10 | 400 | 9 (34)| 00:00:01 |
| 4 | VIEW | | 9 | 180 | 4 (25)| 00:00:01 |
| 5 | HASH GROUP BY | | 9 | 180 | 4 (25)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| CHANNEL_SELL | 9 | 180 | 3 (0)| 00:00:01 |
| 7 | VIEW | | 10 | 200 | 4 (25)| 00:00:01 |
| 8 | HASH GROUP BY | | 10 | 200 | 4 (25)| 00:00:01 |
|* 9 | TABLE ACCESS FULL| DEPT_SELL_PLAN | 10 | 200 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("A"."PROD"="B"."PROD" AND "A"."YYYYMM"="B"."YYYYMM")
6 - filter("YYYYMM">='200901' AND "YYYYMM"<='200903')
9 - filter("YYYYMM">='200901' AND "YYYYMM"<='200903')
-- no_native_full_outer_join 사용
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8 | 320 | 17 (30)| 00:00:01 |
| 1 | SORT ORDER BY | | 8 | 320 | 17 (30)| 00:00:01 |
| 2 | VIEW | | 8 | 320 | 16 (25)| 00:00:01 |
| 3 | UNION-ALL | | | | | |
|* 4 | HASH JOIN OUTER | | 7 | 280 | 9 (34)| 00:00:01 |
| 5 | VIEW | | 7 | 140 | 4 (25)| 00:00:01 |
| 6 | HASH GROUP BY | | 7 | 91 | 4 (25)| 00:00:01 |
|* 7 | TABLE ACCESS FULL| DEPT_SELL_PLAN | 10 | 130 | 3 (0)| 00:00:01 |
| 8 | VIEW | | 1 | 20 | 4 (25)| 00:00:01 |
| 9 | HASH GROUP BY | | 1 | 20 | 4 (25)| 00:00:01 |
|* 10 | TABLE ACCESS FULL| CHANNEL_SELL | 1 | 20 | 3 (0)| 00:00:01 |
| 11 | HASH GROUP BY | | 1 | 29 | 8 (25)| 00:00:01 |
|* 12 | HASH JOIN ANTI | | 1 | 29 | 7 (15)| 00:00:01 |
|* 13 | TABLE ACCESS FULL | CHANNEL_SELL | 1 | 20 | 3 (0)| 00:00:01 |
|* 14 | TABLE ACCESS FULL | DEPT_SELL_PLAN | 10 | 90 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("A"."YYYYMM"="B"."YYYYMM"(+) AND "A"."PROD"="B"."PROD"(+))
7 - filter("YYYYMM">='200901' AND "YYYYMM"<='200903')
10 - filter("YYYYMM">='200901' AND "YYYYMM"<='200903')
12 - access("PROD"="PROD" AND "YYYYMM"="YYYYMM")
13 - filter("YYYYMM">='200901' AND "YYYYMM"<='200903')
14 - filter("YYYYMM">='200901' AND "YYYYMM"<='200903')
31 개의 행이 선택되었습니다.
구분 | 상품 | 연월 | 판매부서 | 판매채널 | 계획수량 | 실적수량 |
---|---|---|---|---|---|---|
계획 | 상품A | 200901 | 10 | 10000 | ||
계획 | 상품A | 200901 | 20 | 5000 | ||
계획 | 상품A | 200901 | 10 | 20000 | ||
계획 | 상품B | 200901 | 10 | 20000 | ||
계획 | 상품B | 200902 | 30 | 15000 | ||
계획 | 상품C | 200901 | 30 | 15000 | ||
계획 | 상품C | 200903 | 20 | 20000 | ||
실적 | 상품A | 200901 | 대리점 | 7000 | ||
실적 | 상품A | 200903 | 온라인 | 8000 | ||
실적 | 상품B | 200902 | 온라인 | 12000 | ||
실적 | 상품B | 200903 | 위탁 | 19000 | ||
실적 | 상품C | 200901 | 대리점 | 13000 | ||
실적 | 상품C | 200902 | 위탁 | 18000 |
-- 상품, 연월 기준으로 group by 하면서 계획수량과 실적수량을 집계
select 상품, 연월, nvl(sum(계획수량), 0) as 계획수량, nvl(sum(실적수량), 0) as 실적수량
from (select 상품
, 계획연월 as 연월
, 계획수량
, to_number(null) as 실적수량
from 부서별판매계획
where 계획연월 between '200901' and '200903'
union all
select 상품
, 판매연월 as 연월
, to_number(null) as 계획수량
, 판매수량
from 채널별판매실적
where 판매연월 between '200901' and '200903'
) a
group by 상품, 연월;
상품 | 연월 | 계획수량 | 판매수량 |
---|---|---|---|
상품A | 200901 | 10000 | 7000 |
상품A | 200901 | 5000 | 0 |
상품A | 200901 | 20000 | 8000 |
상품B | 200901 | 20000 | 0 |
상품B | 200902 | 15000 | 12000 |
상품B | 200903 | 0 | 19000 |
상품C | 200901 | 15000 | 13000 |
상품C | 200902 | 0 | 18000 |
상품C | 200903 | 20000 | 0 |
-- 관심 종목에 대해 사용자가 입력한 거래일시 이후 거래 데이터를 페이징 처리 방식으로 조회
SELECT *
FROM (SELECT ROWNUM NO
, 거래일시
, 체결건수
, 체결수량
, 거래대금
, COUNT(*) OVER () CNT ------------------------- (1)
FROM (SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM 시간별종목거래
WHERE 종목코드 = :isu_cd -- 사용자가 입력한 종목코드
AND 거래일시 >= :trd_time -- 사용자가 입력한 거래일자 또는 거래일시
ORDER BY 거래일시 ---------------------------- (2)
)
WHERE ROWNUM <= :page * :pgsize + 1 ---------------- (3)
)
WHERE NO BETWEEN (:page - 1) * :pgsize + 1 AND :pgsize * :page
-- 다음 버튼 클릭시
SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM (SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM 시간별종목거래 A
WHERE :페이지이동 = 'NEXT'
AND 종목코드 = :isu_cd
AND 거래일시 >= :trd_time
ORDER BY 거래일시
)
WHERE ROWNUM <= 11
-- 이전 버튼 클릭시
SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM (SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM 시간별종목거래 A
WHERE :페이지이동 = 'PREV'
AND 종목코드 = :isu_cd
AND 거래일시 <= :trd_time
ORDER BY 거래일시 DESC
)
WHERE ROWNUM <= 11
ORDER BY 거래일시 ------------- (1)
(1) 11건에 대해서만 소트 연산 수행함.
-- 다음 버튼 클릭시
SELECT TOP 11
거래일시
, 체결건수
, 체결수량
, 거래대금
FROM 시간별종목거래 A
WHERE :페이지이동 = 'NEXT'
AND 종목코드 = :isu_cd
AND 거래일시 >= :trd_time
ORDER BY 거래일시;
-- 이전 버튼 클릭시
SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM (SELECT TOP 11
거래일시
, 체결건수
, 체결수량
, 거래대금
FROM 시간별종목거래 A
WHERE :페이지이동 = 'PREV'
AND 종목코드 = :isu_cd
AND 거래일시 <= :trd_time
ORDER BY 거래일시 DESC
)
ORDER BY 거래일시
SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM (SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM 시간별종목거래 A
WHERE :페이지이동 = 'NEXT'
AND 종목코드 = :isu_cd
AND 거래일시 >= :trd_time
ORDER BY 거래일시
)
WHERE ROWNUM <= 11
UNION ALL
SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM (SELECT 거래일시
, 체결건수
, 체결수량
, 거래대금
FROM 시간별종목거래 A
WHERE :페이지이동 = 'PREV'
AND 종목코드 = :isu_cd
AND 거래일시 <= :trd_time
ORDER BY 거래일시 DESC
)
WHERE ROWNUM <= 11
ORDER BY 거래일시
select 일련번호
, 측정값
,(select max(상태코드)
from 장비측정
where 일련번호 <= o.일련번호
and 상태코드 is not null) 상태코드
from 장비측정 o
order by 일련번호
select 일련번호
, 측정값
,(select /*+ index_desc(장비측정 장비측정_idx) */
상태코드
from 장비측정
where 일련번호 <= o.일련번호
and 상태코드 is not null
and rownum <= 1) 상태코드
from 장비측정 o
order by 일련번호
select 일련번호
, 측정값
, last_value(상태코드 ignore nulls) over(order by 일련번호 rows between unbounded preceding
and current row) 상태코드
from 장비측정
order by 일련번호
with 위험고객카드 as (select 카드.카드번호
from 고객
, 카드
where 고객.위험고객여부 = 'Y'
and 고객.고객번호 = 카드발급.고객번호)
select v.*
from (select a.카드번호 as 카드번호
, sum(a.거래금액) as 거래금액
, null as 현금서비스잔액
, null as 해외거래금액
from 카드거래내역 a
, 위험고객카드 b
where 조건
group by a.카드번호
union all
select a.카드번호 as 카드번호
, null as 현금서비스잔액
, sum(amt) as 현금서비스금액
, null as 해외거래금액
from (select a.카드번호 as 카드번호
, sum(a.거래금액) as amt
from 현금거래내역 a
, 위험고객카드 b
where 조건
group by a.카드번호
union all
select a.카드번호 as 카드번호
, sum(a.결재금액) * -1 as amt
from 현금결재내역 a
, 위험고객카드 b
where 조건
group by a.카드번호
) a
group by a.카드번호
union all
select a.카드번호 as 카드번호
, null as 현금서비스잔액
, null as 현금서비스금액
, sum(a.거래금액) as 해외거래금액
from 해외거래내역 a
, 위험고객카드 b
where 조건
group by a.카드번호
) v
- 강좌 URL : http://www.gurubee.net/lecture/2408
- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^
- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.