데이터 웨어하우스와 SQL과 데이터분석
주요 메모 사항
JOIN이란?
- sql 조인은 두 개 혹은 그 이상의 테이블들을 공통 필드를 가지고 머지하는데 사용된다.
- Inner Join과 Left Join 으로 95%는 커버 가능함
JOIN시 고려해야할 점
- 먼저 중복 레코드가 없고 Primary Key의 uniqueness가 보장됨을 체크
- 조인하는 테이블간의 관계를 명확하게 정의
- One to one
- 완전한 one to one : user_session_channel& session_timestamp
- 한쪽이 부분집합이 되는 one to one: user_session_channel & session_transaction
- One to one
-
- One to many? (order vs order_items)
- 이 경우 중복이 더 큰 문제됨 -> 증폭!!
- One to many? (order vs order_items)
-
- Many to one? -> 방향만 바꾸면 One to many로 보는 것과 사실상 동일
-
- Many to many?
- 이런 경우는 많지 않으며 이는 one to one이나 one to many로 바꾸는 것이 가능하다면 변환하여 조인하는 것이 덜 위험
- Many to many?
새로운 테이블 두 개
- VitalID가 join column
학부 Oracle 수업에서 Join과 procedure 한 학기를 굴렀더니 아직도 기억에 남아있다 -> 설명 생략
- 위 표의 JOIN 이외에 CROSS JOIN, SELF JOIN 설명이 있었으나 생략 -> 사실상 쓸일이 없음
테이블 생성
%%sql
DROP TABLE IF EXISTS raw_data.vital;
CREATE TABLE raw_data.vital (
UserID int,
VitalID int,
Date date,
Weight int
);
INSERT INTO raw_data.vital VALUES
(100, 1, '2020-01-01', 75),
(100, 3, '2020-01-02', 78),
(101, 2, '2020-01-01', 90),
(101, 4, '2020-01-02', 95);
%%sql
DROP TABLE IF EXISTS raw_data.alert;
CREATE TABLE raw_data.alert (
AlertID int,
VitalID int,
AlertType varchar(32),
Date date,
UserID int
);
INSERT INTO raw_data.alert VALUES
(1, 4, 'WeightIncrease', '2020-01-01', 101),
(2, NULL, 'MissingVital', '2020-01-04', 100),
(3, NULL, 'MissingVital', '2020-01-04', 101);
INNER JOIN
- 양쪽 테이블에서 매치가 되는 레코드들만 리턴함
- 양쪽 테이블의 필드가 모두 채워진 상태로 리턴됨
%%sql
SELECT * FROM raw_data.Vital v
JOIN raw_data.Alert a ON v.vitalID = a.vitalID;
+----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------+
| | userid | vitalid | date | weight | alertid | vitalid | alerttype | date | userid |
|----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------|
| 0 | 101 | 4 | 2020-01-02 | 95 | 1 | 4 | WeightIncrease | 2020-01-01 | 101 |
+----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------+
LEFT JOIN
- 왼쪽 테이블의 레코드는 모두 리턴되며 오른쪽 테이블과 매칭이 되는 레코드들의 경우에는 오른쪽 테이블 레코드들의 컬럼들이 채워진 상태로 리턴됨.
- 매칭이 안되는 왼쪽 테이블 레코드들은 오른쪽 테이블에서 들어오는 필드들은 NULL로 채워짐
%%sql
SELECT * FROM raw_data.Vital v
LEFT JOIN raw_data.Alert a ON v.vitalID = a.vitalID;
+----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------+
| | userid | vitalid | date | weight | alertid | vitalid | alerttype | date | userid |
|----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------|
| 0 | 100 | 1 | 2020-01-01 | 75 | nan | nan | | | nan |
| 1 | 100 | 3 | 2020-01-02 | 78 | nan | nan | | | nan |
| 2 | 101 | 2 | 2020-01-01 | 90 | nan | nan | | | nan |
| 3 | 101 | 4 | 2020-01-02 | 95 | 1 | 4 | WeightIncrease | 2020-01-01 | 101 |
+----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------+
FULL JOIN (OUTER JOIN)
- 왼쪽 테이블과 오른쪽 테이블의 모든 레코드들을 리턴함
- 매칭되는 경우에만 양쪽 테이블들의 모든 필드들이 채워진 상태로 리턴됨
%%sql
SELECT * FROM raw_data.Vital v
FULL JOIN raw_data.Alert a ON v.vitalID = a.vitalID;
+----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------+
| | userid | vitalid | date | weight | alertid | vitalid | alerttype | date | userid |
|----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------|
| 0 | 101 | 4 | 2020-01-02 | 95 | 1 | 4 | WeightIncrease | 2020-01-01 | 101 |
| 1 | nan | nan | | nan | 2 | nan | MissingVital | 2020-01-04 | 100 |
| 2 | nan | nan | | nan | 3 | nan | MissingVital | 2020-01-04 | 101 |
| 3 | 101 | 2 | 2020-01-01 | 90 | nan | nan | | | nan |
| 4 | 100 | 1 | 2020-01-01 | 75 | nan | nan | | | nan |
| 5 | 100 | 3 | 2020-01-02 | 78 | nan | nan | | | nan |
+----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------+
CROSS JOIN
- 조인 조건 없이 두 개 테이블의 내용을 모두 조합한 결과 레코드들을 생성
%%sql
SELECT *
FROM (
SELECT vitalid -- 1,2,3,4
FROM raw_data.vital
)
CROSS JOIN (
SELECT alertid -- 1,2,3
FROM raw_data.alert
);
+----+-----------+-----------+
| | vitalid | alertid |
|----+-----------+-----------|
| 0 | 1 | 1 |
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 3 | 1 |
| 4 | 3 | 2 |
| 5 | 3 | 3 |
| 6 | 2 | 1 |
| 7 | 2 | 2 |
| 8 | 2 | 3 |
| 9 | 4 | 1 |
| 10 | 4 | 2 |
| 11 | 4 | 3 |
+----+-----------+-----------+
#
%%sql
SELECT * FROM raw_data.Vital v
CROSS JOIN raw_data.Alert a;
+----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------+
| | userid | vitalid | date | weight | alertid | vitalid | alerttype | date | userid |
|----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------|
| 0 | 100 | 1 | 2020-01-01 | 75 | 1 | 4 | WeightIncrease | 2020-01-01 | 101 |
| 1 | 100 | 3 | 2020-01-02 | 78 | 1 | 4 | WeightIncrease | 2020-01-01 | 101 |
| 2 | 101 | 2 | 2020-01-01 | 90 | 1 | 4 | WeightIncrease | 2020-01-01 | 101 |
| 3 | 101 | 4 | 2020-01-02 | 95 | 1 | 4 | WeightIncrease | 2020-01-01 | 101 |
| 4 | 100 | 1 | 2020-01-01 | 75 | 2 | nan | MissingVital | 2020-01-04 | 100 |
| 5 | 100 | 3 | 2020-01-02 | 78 | 2 | nan | MissingVital | 2020-01-04 | 100 |
| 6 | 101 | 2 | 2020-01-01 | 90 | 2 | nan | MissingVital | 2020-01-04 | 100 |
| 7 | 101 | 4 | 2020-01-02 | 95 | 2 | nan | MissingVital | 2020-01-04 | 100 |
| 8 | 100 | 1 | 2020-01-01 | 75 | 3 | nan | MissingVital | 2020-01-04 | 101 |
| 9 | 100 | 3 | 2020-01-02 | 78 | 3 | nan | MissingVital | 2020-01-04 | 101 |
| 10 | 101 | 2 | 2020-01-01 | 90 | 3 | nan | MissingVital | 2020-01-04 | 101 |
| 11 | 101 | 4 | 2020-01-02 | 95 | 3 | nan | MissingVital | 2020-01-04 | 101 |
+----+----------+-----------+------------+----------+-----------+-----------+----------------+------------+----------+
SELF JOIN
%%sql
SELECT * FROM raw_data.Vital v1
JOIN raw_data.Vital v2 ON v1.vitalID = v2.vitalID;
+----+----------+-----------+------------+----------+----------+-----------+------------+----------+
| | userid | vitalid | date | weight | userid | vitalid | date | weight |
|----+----------+-----------+------------+----------+----------+-----------+------------+----------|
| 0 | 100 | 1 | 2020-01-01 | 75 | 100 | 1 | 2020-01-01 | 75 |
| 1 | 100 | 3 | 2020-01-02 | 78 | 100 | 3 | 2020-01-02 | 78 |
| 2 | 101 | 2 | 2020-01-01 | 90 | 101 | 2 | 2020-01-01 | 90 |
| 3 | 101 | 4 | 2020-01-02 | 95 | 101 | 4 | 2020-01-02 | 95 |
+----+----------+-----------+------------+----------+----------+-----------+------------+----------+
BOOLEAN NULL 타입 처리
- True or False 는 = 과 is 로 연산 가능
- NULL 은 항상 is 와 is not 으로 연산
숙제 리뷰
OUT JOIN 이 필요한지 테이블 점검
%%sql
select distinct sessionid from raw_data.session_timestamp
minus
select distinct sessionid from raw_data.user_session_channel;
+-------------+
| sessionid |
+-------------+
%%sql
select * from raw_data.session_transaction
where amount <= 0
+----+----------------------------------+------------+----------+
| | sessionid | refunded | amount |
|----+----------------------------------+------------+----------|
| 0 | 3d194d58a6470121c92f29c1ee4c936f | False | 0 |
| 1 | 50aaa83c9c2d1d1f3ebc6c732c1abc8c | False | 0 |
| 2 | 7fbfc161a3b873bf2119c788ed93d1f4 | False | 0 |
| 3 | d288a67e5fe3b80c0ccb9531e87d437a | False | 0 |
| 4 | dfc95d616451863a4fe614534e08261c | False | 0 |
| 5 | 297b51d372955449d68d0b67ffda8c80 | False | 0 |
| 6 | 52660fd5af844425740f3a7bf5151008 | False | 0 |
| 7 | 99e17fbe90095024e6c982c85d43d150 | False | 0 |
| 8 | d572948a93127fa250a9aa8a122a4403 | False | 0 |
| 9 | e00747f11c12e85717de726cc6c2f188 | False | 0 |
+----+----------------------------------+------------+----------+
먼저 유일한 사용자 수부터 세보기
SELECT LEFT(ts, 7) "month",
usc.channel,
COUNT(DISTINCT userid) uniqueUsers
FROM raw_data.user_session_channel usc
JOIN raw_data.session_timestamp t ON t.sessionid = usc.sessionid
GROUP BY 1, 2
ORDER BY 1, 2;
+----+---------+-----------+---------------+
| | month | channel | uniqueusers |
|----+---------+-----------+---------------|
| 0 | 2019-05 | Facebook | 247 |
| 1 | 2019-05 | Google | 253 |
| 2 | 2019-05 | Instagram | 234 |
| 3 | 2019-05 | Naver | 237 |
| 4 | 2019-05 | Organic | 238 |
| 5 | 2019-05 | Youtube | 244 |
| 6 | 2019-06 | Facebook | 414 |
| 7 | 2019-06 | Google | 412 |
| 8 | 2019-06 | Instagram | 410 |
| 9 | 2019-06 | Naver | 398 |
| 10 | 2019-06 | Organic | 416 |
| 11 | 2019-06 | Youtube | 400 |
| 12 | 2019-07 | Facebook | 558 |
| 13 | 2019-07 | Google | 556 |
| 14 | 2019-07 | Instagram | 567 |
| 15 | 2019-07 | Naver | 553 |
| 16 | 2019-07 | Organic | 557 |
| 17 | 2019-07 | Youtube | 564 |
| 18 | 2019-08 | Facebook | 611 |
| 19 | 2019-08 | Google | 610 |
| 20 | 2019-08 | Instagram | 621 |
| 21 | 2019-08 | Naver | 626 |
| 22 | 2019-08 | Organic | 608 |
| 23 | 2019-08 | Youtube | 614 |
| 24 | 2019-09 | Facebook | 597 |
| 25 | 2019-09 | Google | 599 |
| 26 | 2019-09 | Instagram | 588 |
| 27 | 2019-09 | Naver | 592 |
| 28 | 2019-09 | Organic | 592 |
| 29 | 2019-09 | Youtube | 588 |
| 30 | 2019-10 | Facebook | 698 |
| 31 | 2019-10 | Google | 699 |
| 32 | 2019-10 | Instagram | 707 |
| 33 | 2019-10 | Naver | 713 |
| 34 | 2019-10 | Organic | 709 |
| 35 | 2019-10 | Youtube | 705 |
| 36 | 2019-11 | Facebook | 688 |
| 37 | 2019-11 | Google | 688 |
| 38 | 2019-11 | Instagram | 669 |
| 39 | 2019-11 | Naver | 667 |
| 40 | 2019-11 | Organic | 677 |
| 41 | 2019-11 | Youtube | 677 |
+----+---------+-----------+---------------+
Summary Table 만들기
%%sql
--혹시 기존에 생성되어 있으면 삭제
DROP TABLE IF EXISTS adhoc.minsu_monthly_channel_summary;
--Summary Table 생성
CREATE TABLE adhoc.minsu_monthly_channel_summary
AS
SELECT TO_CHAR(ts, 'YYYY-MM') year_month,
usc.channel,
COUNT(DISTINCT usc.userid) unique_users,
COUNT(DISTINCT CASE WHEN amount>0 THEN userid END) paid_users,
ROUND(paid_users*100.0 / NULLIF(unique_users,0) ,2) conversion_rate,
SUM(amount) gross_revenue,
SUM(CASE WHEN refunded is False THEN amount
ELSE 0 END) net_revenue
FROM raw_data.user_session_channel usc
JOIN raw_data.session_timestamp st ON usc.sessionid = st.sessionid
LEFT JOIN raw_data.session_transaction str ON usc.sessionid = str.sessionid
GROUP BY 1, 2;
--정상적으로 생성되었는지 확인
SELECT * FROM adhoc.minsu_monthly_channel_summary;
+----+--------------+-----------+----------------+--------------+-------------------+-----------------+---------------+
| | year_month | channel | unique_users | paid_users | conversion_rate | gross_revenue | net_revenue |
|----+--------------+-----------+----------------+--------------+-------------------+-----------------+---------------|
| 0 | 2019-09 | Facebook | 597 | 27 | 4 | 2270 | 2270 |
| 1 | 2019-07 | Organic | 557 | 22 | 3 | 1600 | 1600 |
| 2 | 2019-08 | Google | 610 | 27 | 4 | 2210 | 1894 |
| 3 | 2019-07 | Youtube | 564 | 36 | 6 | 2210 | 2037 |
| 4 | 2019-05 | Facebook | 247 | 14 | 5 | 1199 | 997 |
| 5 | 2019-05 | Organic | 238 | 17 | 7 | 1846 | 1571 |
| 6 | 2019-09 | Youtube | 588 | 15 | 2 | 1301 | 1301 |
| 7 | 2019-08 | Instagram | 621 | 28 | 4 | 2129 | 2001 |
| 8 | 2019-05 | Youtube | 244 | 9 | 3 | 529 | 529 |
| 9 | 2019-06 | Facebook | 414 | 22 | 5 | 1578 | 1578 |
| 10 | 2019-11 | Instagram | 669 | 25 | 3 | 2116 | 2116 |
| 11 | 2019-10 | Organic | 709 | 31 | 4 | 2762 | 2608 |
| 12 | 2019-09 | Organic | 592 | 22 | 3 | 1267 | 1267 |
| 13 | 2019-09 | Google | 599 | 25 | 4 | 1872 | 1691 |
| 14 | 2019-06 | Instagram | 410 | 21 | 5 | 1462 | 1418 |
| 15 | 2019-06 | Youtube | 400 | 17 | 4 | 1042 | 1042 |
| 16 | 2019-08 | Naver | 626 | 22 | 3 | 1829 | 1551 |
| 17 | 2019-05 | Naver | 237 | 11 | 4 | 867 | 844 |
| 18 | 2019-11 | Youtube | 677 | 45 | 6 | 3532 | 3331 |
| 19 | 2019-08 | Youtube | 614 | 18 | 2 | 987 | 950 |
| 20 | 2019-06 | Organic | 416 | 14 | 3 | 1129 | 940 |
| 21 | 2019-11 | Facebook | 688 | 25 | 3 | 1678 | 1678 |
| 22 | 2019-11 | Naver | 667 | 26 | 3 | 2234 | 1987 |
| 23 | 2019-08 | Facebook | 611 | 18 | 2 | 1009 | 1009 |
| 24 | 2019-11 | Google | 688 | 26 | 3 | 2286 | 2235 |
| 25 | 2019-07 | Facebook | 558 | 32 | 5 | 2222 | 2144 |
| 26 | 2019-10 | Youtube | 705 | 34 | 4 | 2492 | 2319 |
| 27 | 2019-05 | Google | 253 | 10 | 3 | 580 | 580 |
| 28 | 2019-05 | Instagram | 234 | 11 | 4 | 959 | 770 |
| 29 | 2019-11 | Organic | 677 | 34 | 5 | 2626 | 2255 |
| 30 | 2019-10 | Facebook | 698 | 29 | 4 | 1650 | 1641 |
| 31 | 2019-07 | Naver | 553 | 19 | 3 | 1547 | 1547 |
| 32 | 2019-10 | Instagram | 707 | 33 | 4 | 2568 | 2395 |
| 33 | 2019-10 | Naver | 713 | 32 | 4 | 2695 | 2695 |
| 34 | 2019-08 | Organic | 608 | 26 | 4 | 1643 | 1606 |
| 35 | 2019-09 | Instagram | 588 | 20 | 3 | 1260 | 1122 |
| 36 | 2019-06 | Google | 412 | 13 | 3 | 947 | 947 |
| 37 | 2019-07 | Instagram | 567 | 24 | 4 | 1896 | 1766 |
| 38 | 2019-07 | Google | 556 | 21 | 3 | 1558 | 1385 |
| 39 | 2019-09 | Naver | 592 | 21 | 3 | 1996 | 1996 |
| 40 | 2019-10 | Google | 699 | 30 | 4 | 2150 | 2098 |
| 41 | 2019-06 | Naver | 398 | 15 | 3 | 1090 | 1090 |
+----+--------------+-----------+----------------+--------------+-------------------+-----------------+---------------+
원하는 TABLE 생성
%%sql
DROP TABLE IF EXISTS adhoc.minsu_monthly_channel_summary;
CREATE TABLE adhoc.minsu_monthly_channel_summary AS
SELECT LEFT(ts, 7) "month",
channel,
COUNT(DISTINCT usc.userid) uniqueUsers,
COUNT(DISTINCT CASE WHEN amount > 0 THEN usc.userid END) paidUsers,
ROUND(paidUsers::float*100/NULLIF(uniqueUsers, 0),2) conversionRate,
SUM(amount) grossRevenue,
SUM(CASE WHEN refunded is False THEN amount END) netRevenue
FROM raw_data.user_session_channel usc
LEFT JOIN raw_data.session_timestamp t ON t.sessionid = usc.sessionid
LEFT JOIN raw_data.session_transaction st ON st.sessionid = usc.sessionid
GROUP BY 1, 2;
select * from adhoc.minsu_monthly_channel_summary;
+----+---------+-----------+---------------+-------------+------------------+----------------+--------------+
| | month | channel | uniqueusers | paidusers | conversionrate | grossrevenue | netrevenue |
|----+---------+-----------+---------------+-------------+------------------+----------------+--------------|
| 0 | 2019-10 | Organic | 709 | 31 | 4.37 | 2762 | 2608 |
| 1 | 2019-08 | Youtube | 614 | 18 | 2.93 | 987 | 950 |
| 2 | 2019-08 | Organic | 608 | 26 | 4.28 | 1643 | 1606 |
| 3 | 2019-08 | Naver | 626 | 22 | 3.51 | 1829 | 1551 |
| 4 | 2019-06 | Instagram | 410 | 21 | 5.12 | 1462 | 1418 |
| 5 | 2019-09 | Naver | 592 | 21 | 3.55 | 1996 | 1996 |
| 6 | 2019-09 | Google | 599 | 25 | 4.17 | 1872 | 1691 |
| 7 | 2019-10 | Facebook | 698 | 29 | 4.15 | 1650 | 1641 |
| 8 | 2019-11 | Instagram | 669 | 25 | 3.74 | 2116 | 2116 |
| 9 | 2019-07 | Instagram | 567 | 24 | 4.23 | 1896 | 1766 |
| 10 | 2019-07 | Google | 556 | 21 | 3.78 | 1558 | 1385 |
| 11 | 2019-06 | Naver | 398 | 15 | 3.77 | 1090 | 1090 |
| 12 | 2019-06 | Organic | 416 | 14 | 3.37 | 1129 | 940 |
| 13 | 2019-05 | Google | 253 | 10 | 3.95 | 580 | 580 |
| 14 | 2019-09 | Facebook | 597 | 27 | 4.52 | 2270 | 2270 |
| 15 | 2019-09 | Youtube | 588 | 15 | 2.55 | 1301 | 1301 |
| 16 | 2019-11 | Google | 688 | 26 | 3.78 | 2286 | 2235 |
| 17 | 2019-07 | Youtube | 564 | 36 | 6.38 | 2210 | 2037 |
| 18 | 2019-05 | Youtube | 244 | 9 | 3.69 | 529 | 529 |
| 19 | 2019-07 | Facebook | 558 | 32 | 5.73 | 2222 | 2144 |
| 20 | 2019-08 | Instagram | 621 | 28 | 4.51 | 2129 | 2001 |
| 21 | 2019-07 | Naver | 553 | 19 | 3.44 | 1547 | 1547 |
| 22 | 2019-05 | Naver | 237 | 11 | 4.64 | 867 | 844 |
| 23 | 2019-09 | Instagram | 588 | 20 | 3.4 | 1260 | 1122 |
| 24 | 2019-10 | Naver | 713 | 32 | 4.49 | 2695 | 2695 |
| 25 | 2019-06 | Google | 412 | 13 | 3.16 | 947 | 947 |
| 26 | 2019-10 | Instagram | 707 | 33 | 4.67 | 2568 | 2395 |
| 27 | 2019-10 | Google | 699 | 30 | 4.29 | 2150 | 2098 |
| 28 | 2019-06 | Youtube | 400 | 17 | 4.25 | 1042 | 1042 |
| 29 | 2019-11 | Youtube | 677 | 45 | 6.65 | 3532 | 3331 |
| 30 | 2019-06 | Facebook | 414 | 22 | 5.31 | 1578 | 1578 |
| 31 | 2019-11 | Organic | 677 | 34 | 5.02 | 2626 | 2255 |
| 32 | 2019-09 | Organic | 592 | 22 | 3.72 | 1267 | 1267 |
| 33 | 2019-07 | Organic | 557 | 22 | 3.95 | 1600 | 1600 |
| 34 | 2019-05 | Facebook | 247 | 14 | 5.67 | 1199 | 997 |
| 35 | 2019-11 | Facebook | 688 | 25 | 3.63 | 1678 | 1678 |
| 36 | 2019-11 | Naver | 667 | 26 | 3.9 | 2234 | 1987 |
| 37 | 2019-08 | Google | 610 | 27 | 4.43 | 2210 | 1894 |
| 38 | 2019-05 | Organic | 238 | 17 | 7.14 | 1846 | 1571 |
| 39 | 2019-08 | Facebook | 611 | 18 | 2.95 | 1009 | 1009 |
| 40 | 2019-05 | Instagram | 234 | 11 | 4.7 | 959 | 770 |
| 41 | 2019-10 | Youtube | 705 | 34 | 4.82 | 2492 | 2319 |
+----+---------+-----------+---------------+-------------+------------------+----------------+--------------+
COALESCE
- NULL 값을 다른 값으로 바꿔주는 함수
- 다만 COALESCE(exp1, exp2, exp3, ...)
- exp1부터 인자를 하나씩 살펴서 NULL이 아닌 값이 나오면 그걸 리턴함
- 끝까지 갔는데도 모두 NULL이면 최종적으로 NULL을 리턴
%%sql
SELECT
value,
COALESCE(value, 0) -- value가 NULL이면 0을 리턴
FROM raw_data.count_test;
+----+---------+------------+
| | value | coalesce |
|----+---------+------------|
| 0 | nan | 0 |
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 0 | 0 |
| 4 | 0 | 0 |
| 5 | 4 | 4 |
| 6 | 3 | 3 |
+----+---------+------------+
공백 혹은 예약키워드를 필드 이름으로 사용하려면?
- "" 으로 선언하면 된다.
CREATE TABLE keeyong.test (
group int primary key,
'mailing address' varchar(32)
);
공부하며 어려웠던 내용
'프로그래머스 데브코스-데이터 엔지니어 > TIL(Today I Learned)' 카테고리의 다른 글
05/15 26일차 가장 많이 사용되는 AWS 클라우드 (1) (0) | 2023.05.15 |
---|---|
05/12 25일차 데이터 웨어하우스와 SQL과 데이터분석 (5) (0) | 2023.05.12 |
05/10 23일차 데이터 웨어하우스와 SQL과 데이터분석 (3) (1) | 2023.05.10 |
05/09 22일차 데이터 웨어하우스와 SQL과 데이터분석 (2) (1) | 2023.05.09 |
05/08 21일차 데이터 웨어하우스와 SQL과 데이터분석 (1) (0) | 2023.05.08 |