강사님: 이태희 강사님
4. SQL 고급 문법
4-2 두 테이블을 묶는 조인
조인(Join)
- 두 개의 테이블을 서로 묶어서 하나의 결과를 만들어 내는 것
내부 조인
- 두 테이블을 연결할 때 가장 많이 사용되는 것이 내부 조인
일대다 관계의 이해
- 두 테이블의 조인을 위해서는 테이블이 일대다(one to many) 관계로 연결되어 있어야 함
- ex) market_db의 회원 테이블과 구매 테이블
- 주로 기본 키(Primary Key)와 외래 키(Foreign Key) 관계로 맺어져 있다
- ‘PK-FK’ 관계라고도 부른다
1
2
3
4
SELECT *
FROM buy
INNER JOIN member
ON buy.mem_id = member.mem_id;
내부 조인의 기본
JOIN
이라고만 써도INNER JOIN
으로 인식- 두 개의 테이블(buy, member)을 조인하는 경우 동일한 열 이름이 존재한다면
테이블_이름.열_이름
형식으로 표기해야 한다
1
2
3
4
SELECT member.mem_id, prod_name, mem_name, addr
FROM buy
INNER JOIN member
ON buy.mem_id = member.mem_id;
외부 조인
- 두 테이블에 모두 데이터가 있어야만 나오는 내부 조인과는 달리 외부 조인은 한쪽에만 데이터가 있어도 결과가 나옴
외부 조인의 기본
- 필요한 내용이 한쪽 테이블에만 있어도 결과 추출 가능
SELECT <열 목록>
FROM <첫 번째 테이블(LEFT 테이블)>
<LEFT | RIGHT FULL> OUTER JOIN <두 번째 테이블(RIGHT 테이블>
ON <조인될 조건>
[WHERE 검색 조건];
FULL OUTER JOIN
- 왼쪽 외부 조인과 오른쪽 외부 조인의 합
- 왼쪽이든 오른쪽이든 한쪽에 들어있는 내용이면 출력
기타 조인
상호 조인(카티션 곱: Cartesian product)
- 한쪽 테이블의 모든 행과 다른 쪽 테이블의 모든 행을 조인
- 따라서 상호 조인 결과의 행 개수는 두 테이블의 각 행 개수를 곱한 개수가 된다
자체 조인
- 자신이 자신과 조인한다는 의미
- ex) 회사의 조직 관계 표현할때
1
2
3
4
5
6
-- 1.전체 걸그룹의 평균(AVG)멤버보다 많은 멤버로 이루어진 걸그룹이 구매한 상품명과 걸그룹명을 출력
SELECT m.mem_name, b.prod_name
FROM member m
JOIN buy b
ON m.mem_id = b.mem_id
WHERE m.mem_number > (SELECT AVG(mem_number) FROM member);
1
2
3
4
5
6
7
8
-- 2. 서울에 사는 걸그룹 중에 총 소비금액이 1000원 이상인 걸그룹명과 소비금액을 출력
SELECT m.mem_name 걸그룹명, SUM(b.price*b.amount) 소비금액
FROM member m
JOIN buy b
ON m.mem_id = b.mem_id
WHERE m.addr = "서울"
GROUP BY m.mem_name
HAVING SUM(b.price*b.amount) >= 1000;
웹 크롤링
설치
- 파이썬 (버전 각자 다름)
- 주피터 노트북
실습
1
2
3
!pip install fake_useragent
!pip install requests
!pip install bs4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
ua = UserAgent()
headers = {
"User-Agent":ua.random
}
# print(headers)
res = requests.get("https://news.naver.com/main/main.naver?mode=LSD&mid=shm&sid1=105", headers=headers)
bs = BeautifulSoup(res.text, 'html.parser')
area = bs.select_one("#_rankingList0")
elem = area.select("a.list_tit")
for e in elem:
print(e.text)
print(e.attrs.get("href"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# https://news.naver.com/main/main.naver?mode=LSD&mid=shm&sid1=105
# 분야별 주요뉴스 15개 수집
# 수집한 내용은 article.csv 파일에 저장
# 제목, 공백의 수, 링크
res = requests.get("https://news.naver.com/main/main.naver?mode=LSD&mid=shm&sid1=105", headers=headers)
bs = BeautifulSoup(res.text, 'html.parser')
area = bs.select_one("#right_dailyList")
elem = bs.select("#right_dailyList > div > ul > li > a")
with open("article.csv", "w", encoding="utf-8") as f:
f.write("제목," + "띄어쓰기 개수,"+ "링크\n")
for e in elem:
title = e.text.replace(",", "-") # 제목에 ','가 csv에 혼돈을 줌으로 변환
count = title.count(" ")
link = e.attrs['href']
f.write(f'{title},{count},{link}\n')
- 네이버에서 파이썬을 검색하여, 제목, 내용, 날짜를 csv로 담기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 내 코드
res = requests.get("https://search.naver.com/search.naver?where=view&sm=tab_jum&query=%ED%8C%8C%EC%9D%B4%EC%8D%AC")
bs = BeautifulSoup(res.text, 'html.parser')
li_list = bs.select('#main_pack > section > div > div._list > panel-list > div > more-contents > div > ul > li')
with open("python.csv", "w", encoding="utf-8") as f:
f.write("제목,내용,시간")
for li in li_list:
title=li.select_one("a.api_txt_lines").text.replace(",", "-")
description=li.select_one("div.dsc_txt").text.replace(",", "-")
try:
date=li.select_one("span.sub_txt").text
except:
date=li.select_one("span.date").text
f.write(f'{title},{description},{date}\n')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 강사님 코드
area = bs.select_one(".lst_total")
lis = area.select("._svp_item")
with open("python2.csv", "w", encoding="utf-8") as f:
f.write("제목,내용,시간"+"\n")
for li in lis:
title = li.select_one(".total_tit").text.replace(",", "-")
summary = li.select_one(".dsc_txt").text.replace(",", "-")
date = li.select_one(".date")
if not date:
date=li.select_one(".sub_time")
date = date.text
f.write(f'{title},{summary},{date}\n')
- 네이버 주식 TOP 종목 크롤링
1
2
3
4
5
6
7
res = requests.get("https://finance.naver.com/")
bs = BeautifulSoup(res.text, 'html.parser')
tr_list = bs.select("#_topItems1 > tr")
for tr in tr_list:
th = tr.select_one('th').text
td = tr.select('td')[0].text
print(th, td)