합쳐서 하나
CREATE TABLE PriceByAge
(product_id VARCHAR(32) NOT NULL,
low_age INTEGER NOT NULL,
high_age INTEGER NOT NULL,
price INTEGER NOT NULL,
PRIMARY KEY (product_id, low_age),
CHECK (low_age < high_age));
INSERT INTO PriceByAge VALUES('제품1', 0 , 50 , 2000);
INSERT INTO PriceByAge VALUES('제품1', 51 , 100 , 3000);
INSERT INTO PriceByAge VALUES('제품2', 0 , 100 , 4200);
INSERT INTO PriceByAge VALUES('제품3', 0 , 20 , 500);
INSERT INTO PriceByAge VALUES('제품3', 31 , 70 , 800);
INSERT INTO PriceByAge VALUES('제품3', 71 , 100 , 1000);
INSERT INTO PriceByAge VALUES('제품4', 0 , 99 , 8900);
제품을 연령별로 관리하는 테이블이 있다고 할 때 0 ~ 100세 까지 모든 연령이 가지고 놀 수 있는 제품을 구하라
먼저 처음 생각해본것은 상품별로 그룹을 맺고 low_age의 max값과 high_age의 min값을 구하여 더한 다음 100과 같거나 크다면
상품을 구하도록 함.
select
case
when max(low_age) + min(high_age) >= 100
then product_id
end
from priceByAge
group by product_id
위와 같이 구현하면 null상품이 나오긴 하지만 null도 같이 나오기 때문에 having에 조건을 넣어줌
select
producti_id
from priceByAge
group by product_id
having
max(low_age) + min(high_age) >= 100 ;
위와 같이 구현하는것도 결과는 나오지만 더 좋은 방법은 high+age에서 low_age를 뺀 값을 모두 SUM해서 +1 을 해서 101이 나오면 됨
select product_id
from priceByAge
group by product_id
having sum(high_age - low_age + 1 ) = 101;
응용 문제
- 호텔에서 10일 이상 묶는 방번호 구하기
CREATE TABLE HotelRooms
(room_nbr INTEGER,
start_date DATE,
end_date DATE,
PRIMARY KEY(room_nbr, start_date));
INSERT INTO HotelRooms VALUES(101, '2008-02-01', '2008-02-06');
INSERT INTO HotelRooms VALUES(101, '2008-02-06', '2008-02-08');
INSERT INTO HotelRooms VALUES(101, '2008-02-10', '2008-02-13');
INSERT INTO HotelRooms VALUES(202, '2008-02-05', '2008-02-08');
INSERT INTO HotelRooms VALUES(202, '2008-02-08', '2008-02-11');
INSERT INTO HotelRooms VALUES(202, '2008-02-11', '2008-02-12');
INSERT INTO HotelRooms VALUES(303, '2008-02-03', '2008-02-17');
select room_nbr, sum(end_date - st
from hotelrooms
group by room_nbr
having sum(end_date - start_date) >= 10;
자르기
CREATE TABLE Persons
(name VARCHAR(8) NOT NULL,
age INTEGER NOT NULL,
height FLOAT NOT NULL,
weight FLOAT NOT NULL,
PRIMARY KEY (name));
INSERT INTO Persons VALUES('Anderson', 30, 188, 90);
INSERT INTO Persons VALUES('Adela', 21, 167, 55);
INSERT INTO Persons VALUES('Bates', 87, 158, 48);
INSERT INTO Persons VALUES('Becky', 54, 187, 70);
INSERT INTO Persons VALUES('Bill', 39, 177, 120);
INSERT INTO Persons VALUES('Chris', 90, 175, 48);
INSERT INTO Persons VALUES('Darwin', 12, 160, 55);
INSERT INTO Persons VALUES('Dawson', 25, 182, 90);
INSERT INTO Persons VALUES('Donald', 30, 176, 53);
- 이름 앞글자로 자르기
select count(*),substr(name, 0, 1)
from persons
group by substr(name, 0, 1);
파티션
- GROUP_BY 구로 잘라 만든 하나하나의 부분 집합을 파티션이라고 함
- 나이 기준으로 어린이(20세 미만), 성인(20 ~69세), 노인(70세 이상)
처음 생각한것은 case 구문으로 age를 비교하여 테이블을 만들고 그 테이블을 한번 감싸서 group by 하는것으로 해결 하였다.
select p.age_label, count(p.age_label) from (
select
name
,case
when age < 20
then '어린이'
when age between 20 and 69
then '성인'
when age > 70
then '노인'
end as age_label
from persons
) p
group by p.age_label;
다른 방법으로는 group by 에 case구문을 넣고 파티션을 하면 된다.
사실 간결한지는 잘 모르겠다
Partition By로 자르기
- Partition By 를 사용하면 Group By를 쓸 때 처럼 집약 기능이 없다 이것 빼면 다른건 동일 하다
select name, age,
case
when age < 20
then '어린이'
when age between 20 and 69
then '성인'
when age > 70
then '노인'
end as age_label,
rank() over(partition by case
when age < 20
then '어린이'
when age between 20 and 69
then '성인'
when age > 70
then '노인'
end order by age) age_rank
from persons
ORDER BY age_label desc , age_rank;