July 01, 2022
01.
7강
하나의 테이블에 대한 조작01.1 가로 기반 데이터를 세로 기반으로 변환하기
02.
8강
여러 개의 테이블 조작하기02.1 여러 개의 테이블을 세로로 결합하기
02.2 여러 개의 테이블을 가로로 정렬하기
02.3 조건 플래그를 0과 1로 표현하기
02.4 계산한 테이블에 이름 붙여 재사용하기
02.5 유사 테이블 만들기
세로 기반 데이터를 가로로 변경하는 것보다
열로 표현된 값을 행으로 변환하기
-- 7-12 일련 번호를 가진 피벗 테이블을 사용해 행으로 변환하는 쿼리
select * from quarterly_sales qs ;
select
q.year,
-- Q1 ~ Q4까지의 레이블 이름 출력하기
case
when p.idx = 1 then 'q1'
when p.idx = 2 then 'q2'
when p.idx = 3 then 'q3'
when p.idx = 4 then 'q4'
end as quarter,
-- Q1 ~ Q4까지의 매출 출력하기
case
when p.idx = 1 then q.q1
when p.idx = 2 then q.q2
when p.idx = 3 then q.q3
when p.idx = 4 then q.q4
end as sales
from
quarterly_sales as q
cross join
-- 행으로 전개하고 싶은 열의 수만큼 순번 테이블 만드리
(
select 1 as idx
union all select 2 as idx
union all select 3 as idx
union all select 4 as idx
) as p ;
임의의 길이를 가진 배열을 행으로 전개하기
리턴값이 테이블인 함수를 의미
--- 7-13 임의의 길이를 가진 배열을 행으로 전개하기
select unnest(array['A001', 'A002', 'A003']) as product_id;
--- 7-14 테이블 함수를 사용해 쉼표로 구분된 문자열 데이터를 행으로 전개하는 쿼리
DROP TABLE IF EXISTS purchase_log;
CREATE TABLE purchase_log (
purchase_id integer
, product_ids varchar(255)
);
INSERT INTO purchase_log
VALUES
(100001, 'A001,A002,A003')
, (100002, 'D001,D002')
, (100003, 'A001')
;
select
purchase_id,
product_id
from
purchase_log as p
-- string_to_array 함수로 문자열을 배열로 변환 후 unnest 함수로 테이블로 변환하기
cross join unnest(string_to_array(product_ids,',')) as product_id;
--- 7-15 postgreSQL에서 쉼표로 구분된 데이터를 행으로 전개하는 쿼리
select
purchase_id,
-- 쉼표로 구분된 문자열을 한 번에 행으로 전개하기
regexp_split_to_table(product_ids,',') as product_id
from purchase_log;
Redshift에서 문자열을 행으로 전개하기
--- 7-16 일련 번호를 가진 피벗 테이블을 만드는 쿼리
select *
from(
select 1 as idx
union all select 2 as idx
union all select 3 as idx
)as pivot;
--- 7-17 split_part 함수의 사용 예
select
split_part('A001,A002,A003',',',1) as part_1,
split_part('A001,A002,A003',',',2) as part_2,
split_part('A001,A002,A003',',',3) as part_3
;
split_part 함수는
--- 7-18 문자 수의 차이를 사용해 상품 수를 계산하는 쿼리
select
purchase_id,
product_ids,
-- 상품 ID 문자열을 기반으로 쉼표를 제거하고,
-- 문자 수의 차이를 계산해서 상품 수 구하기
1 + char_length(product_ids)
- char_length(replace(product_ids,',',''))
as product_num
from
purchase_log;
--- 7-19 피벗 테이블을 사용해 문자열을 행으로 전개하는 쿼리
select
l.purchase_id,
l.product_ids,
-- 상품 수만큼 순번 붙이기
p.idx,
-- 문자열을 쉼표로 구분해서 분할하고, idx번째 요소 추출하기
split_part(l.product_ids,',',p.idx) as product_id
from
purchase_log as l
join
(
select 1 as idx
union all select 2 as idx
union all select 3 as idx
)as p
-- 피벗 테이블의 id가 상품 수 이하의 경우 결합하기
on p.idx <=
(1 + char_length(l.product_ids)
- char_length(replace(l.product_ids, ',', '')));
8강
여러 개의 테이블 조작하기업무 데이터를 사용하는 경우
사용자가 어떤 행동을 하는가
를 분석하고 싶으면 이런 테이블을 합쳐서 다뤄어야함사전 준비
DROP TABLE IF EXISTS app1_mst_users;
CREATE TABLE app1_mst_users (
user_id varchar(255)
, name varchar(255)
, email varchar(255)
);
INSERT INTO app1_mst_users
VALUES
('U001', 'Sato' , 'sato@example.com' )
, ('U002', 'Suzuki', 'suzuki@example.com')
;
DROP TABLE IF EXISTS app2_mst_users;
CREATE TABLE app2_mst_users (
user_id varchar(255)
, name varchar(255)
, phone varchar(255)
);
INSERT INTO app2_mst_users
VALUES
('U001', 'Ito' , '080-xxxx-xxxx')
, ('U002', 'Tanaka', '070-xxxx-xxxx')
;
실습 쿼리
--- 8-1 UNION ALL 구문을 사용해 테이블을 세로로 결합하는 쿼리
select 'app1' as app_name, user_id, name, email from app1_mst_users
union all
select 'app2' as app_name, user_id, name, null as email from app2_mst_users;
사전 준비
DROP TABLE IF EXISTS mst_categories;
CREATE TABLE mst_categories (
category_id integer
, name varchar(255)
);
INSERT INTO mst_categories
VALUES
(1, 'dvd' )
, (2, 'cd' )
, (3, 'book')
;
DROP TABLE IF EXISTS category_sales;
CREATE TABLE category_sales (
category_id integer
, sales integer
);
INSERT INTO category_sales
VALUES
(1, 850000)
, (2, 500000)
;
DROP TABLE IF EXISTS product_sale_ranking;
CREATE TABLE product_sale_ranking (
category_id integer
, rank integer
, product_id varchar(255)
, sales integer
);
INSERT INTO product_sale_ranking
VALUES
(1, 1, 'D001', 50000)
, (1, 2, 'D002', 20000)
, (1, 3, 'D003', 10000)
, (2, 1, 'C001', 30000)
, (2, 2, 'C002', 20000)
, (2, 3, 'C003', 10000)
;
실습 쿼리
--- 8-2 여러 개의 테이블을 결합해서 가로로 정렬하는 쿼리
select
m.category_id,
m.name,
s.sales,
r.product_id as sale_product
from
mst_categories as m
join
-- 카테고리별로 매출액 결합하기
category_sales as s
on m.category_id = s.category_id
join
-- 카테고리별로 상품 결합하기
product_sale_ranking as r
on m.category_id = r.category_id;
--- 8-3 마스터 테이블의 행 수를 변경하기 않고 여러 개의 테이블을 가로로 정렬하는 쿼리
select
m.category_id,
m.name,
s.sales,
r.product_id as top_sale_product
from
mst_categories as m
-- left join을 사용해서 결합한 레코드를 남기
left join
-- 카테고리별 매출액 결합하기
category_sales as s
on m.category_id = s.category_id
-- left join을 사용해서 결합하지 못한 레코드를 남김
left join
-- 카테고리별 최고 매출 상품 하나만 추출해서 결합하기
product_sale_ranking as r
on m.category_id = r.category_id
and r.rank =1;
-- 8-4 상관 서브쿼리로 여러 개의 테이블을 가로로 정렬하는 쿼리
select
m.category_id,
m.name,
-- 상관 서브쿼리를 사용해 카테고리별로 매출액 추출하기
(
select s.sales as s
from category_sales as s
where m.category_id = s.category_id
)as sales,
-- 상관 서브쿼리를 사용해 카테고리별로 최고 매출 상품을
-- 하나 추출하기(순위로 따로 압축하지 않아도 됨)
(
select r.product_id
from product_sale_ranking as r
where m.category_id = r.category_id
order by sales desc
limit 1
)as top_sale_product
from
mst_categories as m;
사전 준비
DROP TABLE IF EXISTS mst_users_with_card_number;
CREATE TABLE mst_users_with_card_number (
user_id varchar(255)
, card_number varchar(255)
);
INSERT INTO mst_users_with_card_number
VALUES
('U001', '1234-xxxx-xxxx-xxxx')
, ('U002', NULL )
, ('U003', '5678-xxxx-xxxx-xxxx')
;
DROP TABLE IF EXISTS purchase_log;
CREATE TABLE purchase_log (
purchase_id integer
, user_id varchar(255)
, amount integer
, stamp varchar(255)
);
INSERT INTO purchase_log
VALUES
(10001, 'U001', 200, '2017-01-30 10:00:00')
, (10002, 'U001', 500, '2017-02-10 10:00:00')
, (10003, 'U001', 200, '2017-02-12 10:00:00')
, (10004, 'U002', 800, '2017-03-01 10:00:00')
, (10005, 'U002', 400, '2017-03-02 10:00:00')
;
실습 쿼리
--- 8-5 신용카드 등록과 구매 이력 유무를 0과 1이라는 플래그로 나타내는 쿼리
select
m.user_id,
m.card_number,
count(p.user_id) as purchase_count,
-- 신용 카드 번호를 등록한 경우 1, 등록하지 않은 경우 0으로 표현하기
case when m.card_number is not null then 1 else 0 end as has_card,
-- 구매 이력이 있는 경우 1, 없는 경우 0으로 표현하기
sign(count(p.user_id)) as has_purchased
from
mst_users_with_card_number as m
left join
purchase_log as p
on m.user_id = p.user_id
group by m.user_id, m.card_number;
사전 준비
DROP TABLE IF EXISTS product_sales;
CREATE TABLE product_sales (
category_name varchar(255)
, product_id varchar(255)
, sales integer
);
INSERT INTO product_sales
VALUES
('dvd' , 'D001', 50000)
, ('dvd' , 'D002', 20000)
, ('dvd' , 'D003', 10000)
, ('cd' , 'C001', 30000)
, ('cd' , 'C002', 20000)
, ('cd' , 'C003', 10000)
, ('book', 'B001', 20000)
, ('book', 'B002', 15000)
, ('book', 'B003', 10000)
, ('book', 'B004', 5000)
;
실습 쿼리
--- 8-6 카테고리별 순위를 추가한 테이블에 이름 붙이기
with
product_sale_ranking as(
select
category_name,
product_id,
sales,
row_number() over(partition by category_name order by sales desc) as rank
from
product_sales
)
select * from product_sale_ranking;
--- 8-7 카테고리들의 순위에서 유니크한 순위 목록을 계산하는 쿼리
with
product_sales_ranking as(
select
category_name,
product_id,
sales,
row_number() over(partition by category_name order by sales desc) as rank
from
product_sales
),
mst_rank as(
select distinct rank
from product_sale_ranking
)
select * from mst_rank;
--- 8-8 카테고리들의 순위를 횡단적으로 출력하는 쿼리
with
product_sale_ranking as(
select
category_name,
product_id,
sales,
row_number() over(partition by category_name order by sales desc) as rank
from
product_sales
),
mst_rank as(
select distinct rank
from product_sale_ranking
)
select
m.rank,
r1.product_id as dvd,
r1.sales as dvd_sales
,
r2.product_id as cd,
r2.sales as cd_sales,
r3.product_id as book,
r3.sales as book_sales
from
mst_rank as m
left join
product_sale_ranking r1
on m.rank = r1.rank
and r1.category_name = 'dvd'
left join
product_sale_ranking as r2
on m.rank = r2.rank
and r2.category_name = 'cd'
left join
product_sale_ranking as r3
on m.rank = r3.rank
and r3.category_name = 'book'
order by m.rank;
임의의 레코드를 가진 유사 테이블 만들기
--- 8-9 디바이스 ID와 이름의 마스터 테이블을 만드는 쿼리
with
mst_devices as(
select 1 as device_id, 'PC' as device_name
union all select 2 as device_id, 'SP' as device_name
union all select 3 as device_id, '애플리케이션' as device_name
)
select * from mst_devices;
--- 8-10 의사 테이블을 사용해 코드를 레이블로 변환하는 쿼리
with
mst_devices as(
select 1 as device_id, 'PC' as device_name
union all select 2 as device_id, 'SP' as device_name
union all select 3 as device_id, '애플리케이션' as device_name
)
select
u.user_id,
d.device_name
from
mst_users as u
left join
mst_devices as d
on u.register_device = d.device_id;
VALUES 구문을 사용한 유사 테이블 만들기
--- 8-11 VALUES 구문을 사용해 동적으로 테이블 만드는 쿼리
with
mst_devices(device_id, device_name)as(
values
(1, 'PC'),
(2, 'SP'),
(3, '애플리케이션')
)
select * from mst_devices;
배열형 테이블 함수를 사용한 유사 테이블 만들기
--- 8-12 배열과 explode 함수를 사용해 동적으로 테이블을 만드는 쿼리
with
mst_devices as(
select
-- 배열을 열로 전개하기
d[0] as device_id
d[1] as device_name
from
-- 배열을 테이블로 전개하기
(
select explode(
array(
array('1', 'PC'),
array('1', 'SP'),
array('1', '애플리케이션')
))d
)as t
)select * from mst_devices;
--- 8-13 map 자료형과 explode함수를 사용해 통적으로 테이블을 작성하는 쿼리
with
mst_devices as(
select
--map 자료형의 데이터를 열로 전개하기
d['device_id'] as device_id,
d['device_name'] as device_name
from
-- 배열을 테이블로 전개하기
(
select explode(
array(
map('device_id','1','device_name','PC')
map('device_id','2','device_name','SP')
map('device_id','3','device_name','애플리케이션')
))d
) as t
)
select * from mst_devices;
순번을 사용해 테이블 작성하기
--- 8-14 순번을 가진 유사 테이블 작성하는 쿼리
with
series as(
-- 1부터 5까지 순번 생성하기
select generate_series(1,5) as idx
)
select * from series;
--- 8-15 repeat 함수를 응용해서 순번을 작성하는 쿼리
-- postgreSQL안됨 | hive, sparkSQL만 됨
select
row_number() over(order by x) as idx
from
-- repeat함수와 split 함수를 조합해서 임의의 길이를 가진 배열을 생성하고
-- explode로 전개하기
(select explode(split(repeat('x',5 - 1),'x'))as x)as t;