Redshift & SQL
- AWS Console을 통한 Redshift launch
- 관계형 데이터베이스 예제
- Redshift 중심의 SQL (DDL, DML) 소개
- GoogleColab을 통한 실습
AWS 웹 진입 -> Redshift 선택 -> cluster 생성
cluster 설정
( region : Seoul 선택 )
Node type = dc2.large ( 가장 저렴한 )
Number of nodes = 1
Additional configurations의 Use defaults를 비활성
Network and security의 Publicly accessible을 Turn on 시켜줌
( AWS 내부가 아닌 외부 환경 - colab에서도 접근할 수 있게 하기 위함 )
(테이블 소개)
사용자 ID : 보통 웹서비스에서는 등록된 사용자마다 부여하는 유일한 ID
세션 ID : 세션마다 부여되는 ID
세션 : 사용자의 방문을 논리적인 단위로 나눈 것
(일반적으로 google analytics의 정의를 따름)
즉, 하나의 사용자는 여러 개의 세션을 가질 수 있다.
보통 세션의 경우 세션을 만들어낸 접점(경유지)를 채널이란 이름으로 기록해둠
또한 세션이 생긴 시간도 기록함
이 정보를 기반으로 다양한 데이터 분석과 지표 설정이 가능
e.g.) 사용자 ID 100번이 상품을 구매하기까지의 세션 생성
-> 이 세션 중에 상품에 구매에 도달한 기여도가 가장 많은 것을 알아내는 것이 중요함 (데이터 분석)
( 어느 채널(세션)에게 크레딧을 줄 것이냐? )
폴더 : 데이터베이스 (스키마) -> 폴더 내부 : 테이블

테이블 내용

-- : 인라인 한줄짜리 주석. c,java의 //와 동일/*--*/ : 여러 줄에 걸쳐 사용 가능한 주석CREATE TABLE
Primary key 속성을 지정할 수 있으나 무시됨 (OLAP)
Primary key uniqueness
CTAS : 테이블을 만듦과 동시에 내용까지 채워버림
( create + insert를 동시에 )
-> CTAS : CREATE TABLE table_name AS SELECT문
- vs. CREATE TABLE and then INSERT
e.g.)
-- CREATE TABLE skema_name.table_name(...);
CREATE TABLE raw_data.user_session_channel(
userid int,
sessionid varchar(32) primary key,
channel varchar(32)
);
레코드 수정 언어:
INSERT INTO : 테이블에 레코드를 추가하는데 사용
-> 시간이 오래걸리기에, 이를 안쓰고 copy를 사용
( bulk update 명령 : 레코드들을 csv, json으로 저장해놓고 웹 스토리지(S3)에 업로드,
S3에서 내가 원하는 테이블로 bulk로 한번에 업데이트를 진행 )
UPDATE FROM
DELETE FROM ( 테이블의 구조는 남기고 레코드만 제거 )
DROP TABLE ( 테이블 자체 제거 )
DROP TABLE table_name;DROP TABLE IF EXISTS table_name;ALTER TABLE
새로운 컬럼 추가 :
ALTER TABLE table_name ADD COLUMN field_name field_type;기존 컬럼 이름 변경 :
ALTER TABLE table_name RENAME 현재필드이름 to 새필드 이름;기존 컬럼 제거 :
ALTER TABLE table_name DROP COLUMN 필드이름;테이블 이름 변경 :
ALTER TABLE 현재테이블이름 RENAME to 새테이블이름;현업에서 깨끗한 데이터란 존재하지 않음
데이터 일을 한다면 항상 데이터의 품질을 의심하고 체크하는 버릇이 필요
=> unit test를 거친 후에 분석으로 넘어갈 수 있는 자동화 파이프라인을 만들어야한다!!
어느 시점이 되면 너무나 많은 테이블이 존재하게 됨
그 시점부터는 Data Discovery 문제들이 생겨남
이 문제를 해결하기 위한 다양한 오픈소스와 서비스들이 출현
SQL 예제
-- 유일한 채널 이름을 알고 싶은 경우
SELECT DISTINCT channel
FROM raw_data.user_session_channel;
-- 채널별 카운트를 하고 싶은 경우
SELECT channel, COUNT(1)
FROM raw_data.user_session_channel
GROUP BY 1;
-- 테이블의 모든 레코드 수 카운트. COUNT(*)를 써도 무방
SELECT COUNT(1)
FROM raw_data.user_session_channel
-- 채널 이름이 Facebook 경우만 고려해 레코드 수를 카운트
SELECT channel, COUNT(1)
FROM raw_data.user_session_channel
WHERE channel = 'Facebook';
/* CASE WHEN
필드 값의 변환을 위해 사용 가능
ex) CASE WHEN 조건 THEN 참일때 값 ELSE 거짓일때 값 END 필드이름
여러 조건을 사용하여 변환하는 것도 사용 가능
*/
SELECT CASE
WHEN channel in ('Facebook','Instagram') THEN 'Social-Media'
WHEN channel in ('Google','Naver') THEN 'Search-Engine'
ELSE 'Something-Else'
END channel_type
FROM raw_data.user_session_channel;
NULL
값이 존재하지 않음을 나타내는 상수
특수한 문법을 통해 비교
( is NULL, is not NULL )
사칙연산에 사용되는 경우?
-> 결과가 NULL이 됨
COUNT 함수 예시
(table: count_test)
| value |
|---|
| NULL |
| 1 |
| 1 |
| 0 |
| 0 |
| 4 |
| 3 |
SELECT COUNT(1) FROM count_test;
--7
SELECT COUNT(0) FROM count_test;
--7
SELECT COUNT(NULL) FROM count_test;
--0
SELECT COUNT(value) FROM count_test;
--6
SELECT COUNT(DISTINCT value) FROM count_test;
--4
COUNT() 함수에 들어가는 값이 NULL인 경우 세지않고 넘어가고 그 외의 값은 레코드를 센다.
WHERE
IN / NOT IN
( IN 뒤로 오는 조건들에 포함이 되는가 안되는가? )
LIKE and ILIKE
( 문자열 매칭, LIKE : 대소문자 구분, ILIKE : 대소문자 미구분 )
BETWEEN
( 두 조건의 범위 사이 매칭 )
이 오퍼레이터들은 CASE WHEN에서도 사용 가능
STRING Functions
LEFT(str, N) : 문자열 앞부분 N개만 추출
REPLACE(str,exp1,exp2) : 첫번째 인자로 주어진 문자열에서 exp1 문자열을 찾아서 exp2 문자열로 바꿔치기
UPPER(str) : 대문자로 변환
LOWER(str) : 소문자로 변환
LEN(str) : 길이를 리턴
LPAD, RPAD : 문자열을 왼/오른쪽으로 패딩함
SUBSTRING : 시작점을 선언해서 부분 문자열을 추출
ORDER BY
NULLS FIRST / NULLS LAST 를 사용타입 변환
DATE Conversion :
타임존 관련 변환
DATE, TRUNCATE
DATE_TRUNC
EXTRACT or DATE_PART
DATEDIFF
DATEADD
GET_CURRENT, ...
TO_CHAR, TO_TIMESTAMP
Type Casting
1/2의 결과는?
-> 0
:: 오퍼레이터를 사용
cast 함수를 사용
주피터 SQL 엔진 설정
%load_ext sql
%sql postgresql://guest:guest_name!password@redshift_host_name:port_num/db_name
주피터 sql 사용법
# %%sql로 sql 사용을 선언
%%sql
# 평범하게 sql문을 작성하면 됨
SELECT 1/2;
판다스와 연동하는 방법
result = %sql SELECT * FROM raw_data.user_session_channel
df = result.DataFrame()
df.head()
df.groupby(["channel"]).size()
# 위와 동일한 결과
df.groupby(["channel"])["sessionid"].count()