안녕하세요 늑대양입니다 :)
이번 주는 지난 주에 이어서 SQL 학습을 진행합니다!
오늘은 [AI 데이터 사이언티스트 취업 완성 과정]의 20일차 일과를 정리하여 안내해드리도록 하겠습니다.
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/001.gif)
Day 20 시간표:
- 오프라인 강의: 빅데이터 추출/가공을 위한 SQL 기초
- 오프라인 강의: 빅데이터 추출/가공을 위한 SQL 기초
- 선택학습
Day 20. 빅데이터 추출/가공을 위한 SQL 기초
빅데이터 추출/가공을 위한 SQL 기초:
지난 시간 요약 정리:
# DML : DATA : C(insert into), R(select from), U(update set), D(delete from)
# DDL : DATABASE, TABLE : C(create), R(show, desc), U(alter), D(drop)
# 테이블 생성 : 필드명, 데이터타입, 제약조건
# 데이터 타입 : number(int, float), string(char, varchar, test), datetime(datetime, timestamp)
# 제약조건
# not null, unique, primary key, default, auto_increment
# foregin key
# on update, on delete
# functions : round(), concat(), count(), distinct(), date_format()
# group by : 특정 컬럼(중복결합), 다른 컬럼(결합함수)
# 결합함수 : sum(), min(), max(), avg(), count()
group by 예제:
# Country 테이블에서 대륙별 대륙이름과 총인구수를 출력
USE world;
SELECT continent, population
FROM country;
SELECT continent, sum(population) AS population
FROM country
GROUP BY continent;
# 인구수가 5억 이상인 대륙이름과 대륙의 총인구수를 출력
# HAVING : 쿼리 실행 후에 조건에 따라 데이터를 필터링
SELECT continent, sum(population) AS population
FROM country
GROUP BY continent
HAVING population >= 50000 * 10000;
group by 예제 2:
# sakila 데이터베이스에서 년월별 매출 결과 출력
# GROUP BY, date_format()
USE sakila;
SELECT *
FROM payment;
SELECT payment_date, amount
FROM payment;
SELECT date_format(payment_date, "%Y-%m") AS monthly, amount
FROM payment;
SELECT date_format(payment_date, "%Y-%m") AS monthly, sum(amount)
FROM payment
GROUP BY monthly;
SELECT date_format(payment_date, "%H") AS monthly, sum(amount) AS amount
FROM payment
GROUP BY monthly
ORDER BY amount DESC;
Join:
Join 실습:
# JOIN : 특정 컬럼을 기준으로 2개의 테이블을 결합하는 방법
# inner, left, right, outer
DROP DATABASE test;
CREATE DATABASE test;
USE test;
# 테이블 생성
CREATE TABLE user(
user_id INT PRIMARY KEY AUTO_INCREMENT
, name VARCHAR(20)
);
CREATE TABLE addr(
user_id INT
, aname VARCHAR(10)
);
# 데이터 추가
INSERT INTO user(name)
VALUES ("a"), ("b"), ("c");
SELECT * FROM user;
INSERT INTO addr(user_id, aname)
VALUES (1, "seoul"), (2, "pusan"), (4, "daegu"), (5, "seoul");
SELECT * FROM addr;
# JOIN 원리
SELECT * FROM user
JOIN addr; (3*4 결과 출력)
SELECT * FROM user
JOIN addr
ON user.user_id = addr.user_id; (default 옵션으로 이너 조인 실행!)
SELECT user.user_id, user.name, addr.aname
FROM user
JOIN addr
ON user.user_id = addr.user_id;
Join 실습 2:
# Left join
SELECT user.user_id, user.name, addr.aname
FROM user
LEFT JOIN addr
ON user.user_id = addr.user_id;
# Right join
SELECT addr.user_id, user.name, addr.aname
FROM user
RIGHT JOIN addr
ON user.user_id = addr.user_id;
# 참고용도!
# inner join : join 안쓰고 ,로 join (단점은 inner 조인만 가능!)
# SELECT user.user_id, user.name, addr.aname
# FROM user, addr, addr2, addr3
# WHERE user.user_id = addr.user_id;
Join 실습 3:
# 국가코드, 국가이름, 도시이름, 국가인구수, 도시인구수 출력
USE world;
SELECT country.code, country.name AS country_name, city.name AS city_name
, country.population, city.population
FROM country
JOIN city
ON country.code = city.countrycode;
# 도시화율(도시인구수/국가인구수) 출력
SELECT country.code, country.name AS country_name, city.name AS city_name
, country.population, city.population
, round(city.population / country.population * 100, 2) as city_ratio
FROM country
JOIN city
ON country.code = city.countrycode;
# 국가의 인구수 4000만명 이상인 국가만 출력
SELECT country.code, country.name AS country_name, city.name AS city_name
, country.population, city.population
, round(city.population / country.population * 100, 2) as city_ratio
FROM country
JOIN city
ON country.code = city.countrycode
HAVING country.population >= 4000 * 10000;
# 도시화율이 높은 5개의 국가의 도시를 출력 : order by > limit
SELECT country.code, country.name AS country_name, city.name AS city_name
, country.population, city.population
, round(city.population / country.population * 100, 2) as city_ratio
FROM country
JOIN city
ON country.code = city.countrycode
HAVING country.population >= 4000 * 10000
ORDER BY city_ratio DESC
limit 5;
Join 실습 4:
# Outer join
# union : 두 개의 쿼리를 실행한 결과를 붙여서 출력 : 중복제거
USE test;
SELECT name FROM user;
SELECT aname FROM addr;
SELECT name FROM user
UNION # UNION ALL 을 사용하면 중복을 허용할 수도 있음!
SELECT aname FROM addr;
# Outer join (= left join + UNION + right join)
# Left join
SELECT user.user_id, user.name, addr.aname
FROM user
LEFT JOIN addr
ON user.user_id = addr.user_id
UNION
# Right join
SELECT addr.user_id, user.name, addr.aname
FROM user
RIGHT JOIN addr
ON user.user_id = addr.user_id;
Sub Query 실습:
# Sub Query : 쿼리 안에 쿼리 작
# select, from, where
USE world;
# 전체 국가수, 전체 도시수를 1개의 row로 출력
SELECT (SELECT count(*) FROM country) AS country_count
, (SELECT count(*) FROM city) AS city_count;
# from : 900만 이상의 도시인구를 가진 도시의 국가코드, 국가이름, 도시이름, 도시인구수 출력
# join > having
SELECT country.code, country.name, city.name, city.population
FROM country
JOIN city
ON country.code = city.countrycode
HAVING city.population >= 900 * 10000;
# sub query
# where(condition) > join
SELECT country.code, country.name, city.name, city.population
FROM country
JOIN (SELECT * FROM city WHERE population >= 900 * 10000) as city
ON country.code = city.countrycode;
# 참고용
# 과연 어떤식으로 쿼리를 짜야할까?
# 1번(Join, having 방식): 239*4097
# 2번(Sub query 방식): 239*6
# 1번 방식보다 2번 방식이 더 효율적
Sub Query 실습 2:
# Where : 한국의 인구수 보다 많은 국가의 국가코드, 국가이름, 인구수 출력
SELECT code, name, population
FROM country
WHERE code = "KOR";
# 46844000
SELECT code, name, population
FROM country
WHERE population >= 46844000;
SELECT code, name, population
FROM country
WHERE population >= (
SELECT population
FROM country
WHERE code = "KOR"
);
Index:
cf) scp 명령을 활용하여 로컬 데이터를 AWS EC2 서버로 전송!
# scp 명령어 사용
$ scp -i <pem 파일> ~/Downloads/<전달할 파일> ubuntu@<EIP>:~/
Index 실습:
# employees.sql 파일을 DB에 부어주고 워크벤치에서 확인!
USE employees;
SELECT * FROM salaries;
# Index : 데이터의 검색 속도를 빠르게 하는 방법
# Index의 장점: 검색 속도가 빨라짐!
# Index의 단점: 저장 공간을 10% 더 사용, insert, update, delete 속도가 느려짐
SELECT count(*) FROM salaries;
# Show index
SHOW INDEX FROM salaries;
# 클러스터형 인덱스: 데이터 자체가 인덱스로 사용되어 검색 속도 향상에 큰 영향을 주지 않습니다!
# 행 데이터를 자신의 열을 기준으로 정렬해주는 기능을 함!
# no index : speed of query # 0.777sec
SELECT * FROM salaries WHERE to_date < "1986-01-01";
# set index
CREATE INDEX tdate on salaries (to_date);
SHOW INDEX FROM salaries;
# is index : speed of query # 0.026sec!!!
SELECT * FROM salaries WHERE to_date < "1986-01-01";
# drop index
DROP INDEX tdate ON salaries;
# show index
SHOW INDEX FROM salaries;
Index 실습 2(Explain):
# explain : 실행계획 : 쿼리를 실행하기 전에 쿼리가 어떻게 실행될지 미리 확인할 수 있는 기능
# ALL : Full search! (약간은 무식한 방법..)
EXPLAIN
SELECT * FROM salaries WHERE to_date < "1986-01-01";
# set index & explain!
CREATE INDEX tdate on salaries (to_date);
SHOW INDEX FROM salaries;
EXPLAIN
SELECT * FROM salaries WHERE to_date < "1986-01-01";
# index 사용시 주의사항 : where에서 많이 사용하는 필드를 인덱스로 설정하자!!
Query 결과를 파일로 저장하는 실습:
USE world;
SELECT * FROM country WHERE population >= 4000 * 10000;
Query 결과를 파일로 저장해보자!:
오늘 학습한 내용!
- group by
- having
- join
- union
- index
오후 시간에 학습할 내용!
- trigger
- backup
- jupyter notebook
trigger 실습:
# trigger 실습
USE test;
SHOW TABLES;
DROP table addr;
DROP table user;
SHOW TABLES;
# 테이블 생성
CREATE TABLE chat(
chat_id INT PRIMARY KEY AUTO_INCREMENT
, msg VARCHAR(100) NOT NULL
);
# 백업 테이블 생성
CREATE TABLE chatBackup(
backup_id INT PRIMARY KEY AUTO_INCREMENT
, chat_id INT NOT NULL
, msgBackup VARCHAR(100) NOT NULL
, backupDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
SHOW TABLES;
# 트리거 확인
SHOW TRIGGERS;
# DELIMITER 구문이 다른 쿼리에 영향을 줄 수 있으므로 새 탭에서 작성!
DELIMITER |
CREATE TRIGGER backup
BEFORE DELETE ON chat
FOR EACH ROW BEGIN
INSERT INTO chatBackup(chat_id, msgBackup)
VALUES (old.chat_id, old.msg);
END |
# 확인 작업
SHOW TRIGGERS;
# 데이터 추가
INSERT INTO chat(msg)
VALUES ("hello"), ("hi"), ("my name is sainthm!");
SELECT * FROM chat;
# 데이터 삭제
DELETE FROM chat
WHERE msg LIKE "h%"
limit 5;
SELECT * FROM chat;
SELECT * FROM chatBackup;
# 참고용 - 초기화 명령어 tuncate
# TRUNCATE chat;
# TRUNCATE chatBackup;
# 복원 방법
INSERT INTO chat
SELECT chat_id, msgBackup as msg
FROM chatBackup;
SELECT * FROM chat;
# 백업 테이블 초기화 및 확인
TRUNCATE chatBackup;
SELECT * FROM chatBackup;
Backup:
Backup의 종류:
Hot Backup:
- 데이터 베이스를 중지하지 않은 상태로 데이터 백업
- 백업하는 동안 서비스가 실행
- 백업하는 동안 데이터가 변경되어 완전한 백업이 안될수 있음
Cold Backup:
- 데이터 베이스를 중지한 상태로 데이터 백업
- 안정적으로 백업이 가능
- 백업하는 동안 서비스가 중단되어야 함
Logical Backup:
- SQL 문으로 백업
- 느린 속도의 백업과 복원
- 디스크 용량을 적게 사용
- 작업시 시스템 자원을 많이 사용
- 문제 발생에 대한 파악이 쉬움
- 서버 OS 호환이 잘됨
Physical Backup:
- 파일 차체를 백업
- 빠른 속도의 백업과 복원
- 디스크 용량 많이 사용
- 작업시 시스템 자원을 적게 사용
- 문제 발생에 대한 파악과 검토가 어려움
- 서버 OS 호환이 잘안될수 있음
Backup 실습:
Backup 실습 2:
Crontab 활용:
Crontab 개요: 유닉스 OS 계열에서 특정 시간에 특정 작업을 해야하는 경우 사용하는 스케쥴러입니다.
데이터베이스 백업 커맨드 활용:
# 참고용
# test_bak.sql 파일 내용 전문
-- MySQL dump 10.13 Distrib 8.0.30, for Linux (x86_64)
--
-- Host: localhost Database: test
-- ------------------------------------------------------
-- Server version 8.0.30-0ubuntu0.20.04.2
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `chat`
--
DROP TABLE IF EXISTS `chat`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `chat` (
`chat_id` int NOT NULL AUTO_INCREMENT,
`msg` varchar(100) NOT NULL,
PRIMARY KEY (`chat_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `chat`
--
LOCK TABLES `chat` WRITE;
/*!40000 ALTER TABLE `chat` DISABLE KEYS */;
INSERT INTO `chat` VALUES (1,'hello'),(2,'hi'),(3,'my name is sainthm!');
/*!40000 ALTER TABLE `chat` ENABLE KEYS */;
UNLOCK TABLES;
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
/*!50003 SET character_set_client = utf8mb4 */ ;
/*!50003 SET character_set_results = utf8mb4 */ ;
/*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ;
DELIMITER ;;
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `backup` BEFORE DELETE ON `chat` FOR EACH ROW BEGIN
INSERT INTO chatBackup(chat_id, msgBackup)
VALUES (old.chat_id, old.msg);
END */;;
DELIMITER ;
/*!50003 SET sql_mode = @saved_sql_mode */ ;
/*!50003 SET character_set_client = @saved_cs_client */ ;
/*!50003 SET character_set_results = @saved_cs_results */ ;
/*!50003 SET collation_connection = @saved_col_connection */ ;
--
-- Table structure for table `chatBackup`
--
DROP TABLE IF EXISTS `chatBackup`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `chatBackup` (
`backup_id` int NOT NULL AUTO_INCREMENT,
`chat_id` int NOT NULL,
`msgBackup` varchar(100) NOT NULL,
`backupDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`backup_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `chatBackup`
--
LOCK TABLES `chatBackup` WRITE;
/*!40000 ALTER TABLE `chatBackup` DISABLE KEYS */;
/*!40000 ALTER TABLE `chatBackup` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2022-09-20 19:32:50
날짜와 명령어를 활용한 백업:
#!/bin/bash
BD=`date +%Y%m%d_%H%M --date=today`
FILE=${BD}.sql
mysqldump -u root -ptest test > ~/backup/$FILE
EC2에 jupyter notebook 설치 실습:
scp를 활용하여 pyenv.sh 파일 업로드:
scp -i ~/<키파일> pyenv.sh ubuntu@<Public IP>:~/
pyenv.sh 100% 933 135.2KB/s 00:00
# 참고용
# pyenv.sh 전문
#!/bin/bash
# set env variable
LANG="en_US.utf8"
LC_COLLATE="C"
LC_CTYPE="C"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL="C"
# install pyenv
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev git
curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init --path)"\nfi' >> ~/.bashrc
source ~/.bashrc
# install python
pyenv install 3.8.5
# virtualenv
sudo apt-get install -y python-virtualenv
pyenv virtualenv 3.8.5 python3
pyenv global python3
# update pip
pip install --upgrade pip
# show pyenv versions
pyenv versions
# ipython jupyter 설치!
pip install ipython jupyter
# 패스워드 생성 작업
ipython
# In[1]
from notebook.auth import passwd
# In[2]
passwd()
Enter password:
Verify password:
# Out[2]
# Out[2]값으로 나오는 'argon2 xxx' 부분을 복사!!
argon2:$argon2id$v~~~~~
# 설정 파일 생성!
jupyter notebook --generate-config
# 설정 파일 편집!
sudo vim .jupyter/jupyter_notebook_config.py
# .jupyter/jupyter_notebook_config.py 의 아래에 내용 추가!
# [shift] + [g] 키를 통해 문서 최하단으로 이동 가능!
# 설정 파일 수정
c.NotebookApp.ip = '10.0.0.50' # private IP 주소
c.NotebookApp.open_browser = False
c.NotebookApp.password = 'argon2:$argon2id$v=19$m=10240,t=10,p=8$a1DM9G6swGgObNu9Exkmog$NcsQHrIR7yhmhgxpAzNf/M39sPyrOoWpPQg1S/7qlHc' # 복사한 argon2 값 입력!
# notebooks 폴더 생성 및 이동
mkdir notebooks; cd notebooks
# jupyter notebook 실행!
jupyter notebook
cf) 파이썬 코드 작성시 실수 할만한 사항들:
# 부동소수점 문제
data1, data2 = 0.1, 0.2
data1 + data2 == 0.3
> False
# 반올림
round(data1 + data2, 1) == 0.3
> True
# 고정소수점 연산
from decimal import Decimal
float(Decimal(str(data1)) + Decimal(str(data2)))
> 0.3
# 파이썬의 철학?!
import this
> The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
# scope
data 1 = 10
def change():
data1 = 20
change()
print(data1)
>10
def change():
global data1
data1 = 20
print(data1)
>20
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/003.gif)
긴 글 읽어주셔서 감사합니다 :)
'AI > [부트캠프] 데이터 사이언티스트 과정' 카테고리의 다른 글
[Megabyte School : AI 데이터 사이언티스트 취업 완성 과정] Day 23. (2) | 2022.09.23 |
---|---|
[Megabyte School : AI 데이터 사이언티스트 취업 완성 과정] Day 22. (2) | 2022.09.22 |
[Megabyte School : AI 데이터 사이언티스트 취업 완성 과정] Day 19. (0) | 2022.09.19 |
[Megabyte School : AI 데이터 사이언티스트 취업 완성 과정] Day 18. (2) | 2022.09.16 |
[Megabyte School : AI 데이터 사이언티스트 취업 완성 과정] Day 14. (0) | 2022.09.08 |