[LG헬로비전 DX DATA SCHOOL 4기] MySQL(2), Python(1) (250106)

mini_bang·2025년 1월 6일

1. 서브쿼리와 JOIN 활용

(1) customer_id = 5인 고객의 대여 횟수를 출력.

  • 서브쿼리 사용:
 // 실습 코드
-- Q) customer_id=5인 고객의 대여횟수를 구하시오(서브쿼리 이용)
-- 메인 쿼리에서 고객의 ID, 이름, 성과 함께 대여횟수를 출력
SELECT customer_id, first_name, last_name,
	(SELECT count(rental_id)
	 FROM sakila.rental
     WHERE customer_id = c. customer_id) AS rental_count
FROM sakila.customer c
WHERE customer_id=5;
  • JOIN 사용:
// 실습 코드
-- JOIN이용 (속도의 차이? => 서브쿼리가 조금 더 느림)
SELECT
	c.customer_id,
	c.first_name,
    c.last_name,
    count(r.rental_id) AS rental_count
FROM sakila.customer c
INNER JOIN sakila.rental r
ON c.customer_id = r.customer_id
WHERE c.customer_id=5;

=> JOIN이 서브쿼리보다 일반적으로 성능이 더 빠름.

(2) 대여 횟수가 10번 이상인 고객 ID, 이름, 성을 출력.

  • 서브쿼리 사용:
// 실습 코드
-- Q) 영화 대여점의 데이터베이스에서 대여 기록이 10번 이상인 고객을 찾으려고 합니다. 고객 테이블에서 이러한 고객들의 ID, 이름, 성을 출력하세요.
-- 1. customer테이블과 rental테이블을 사용합니다.
-- 2. 대여 횟수가 10번 이상인 고객만 조회합니다.
-- 3. 서브쿼리를 사용하여 대여 횟수를 계산하고, 이를 WHERE절에 사용하세요.
-- 4. 결과를 고객 ID 기준으로 오름차순 정렬하세요.
SELECT
	customer_id,
	first_name,
    last_name
FROM sakila.customer
WHERE customer_id IN (
	SELECT customer_id
    FROM sakila.rental
    GROUP BY customer_id
    HAVING count(rental_id) >= 10
)
ORDER BY customer_id;

(3) 고객별 대여 횟수를 계산해 평균 대여 횟수를 초과한 고객 출력.

  • JOIN 사용:
// 실습 코드
-- Q) 영화 대여점의 데이터베이스에서 대여 횟수가 전체 고객 평균 대여 횟수보다 많은 고객을 찾고자 합니다. 이 고객들의 ID, 이름, 성과 대여 횟수를 출력하세요.
-- 1. customer 테이블과 rental 테이블을 사용합니다.
-- 2. 각 고객별 대여 횟수를 계산합니다.
-- 3. 평균 대여 횟수를 서브쿼리를 통해 구합니다.
-- 4. 대여 횟수가 평균값보다 많은 고객만 출력합니다.
-- 5. 결과를 대여 횟수를 기준으로 내림차순 정렬합니다.
SELECT
	c.customer_id,
    c.first_name,
    c.last_name,
	count(r.rental_id) AS rental_count
FROM sakila.customer c
JOIN sakila.rental r ON c.customer_id = r.customer_id
GROUP BY c.customer_id, c.first_name, c.last_name
HAVING count(r.rental_id) > (
	SELECT AVG(subquery_avg.rental_count)
    FROM (
		SELECT count(r.rental_id) AS rental_count
        FROM sakila.rental r
        GROUP BY r.customer_id
	) AS subquery_avg
)
ORDER BY rental_count DESC;
  • 서브쿼리만 사용:
// 실습 코드
-- JOIN을 안쓰고 서브쿼리로만 작성
SELECT
	c.customer_id,
    c.first_name,
    c.last_name,
    -- rental 테이블에서 고객별 대여횟수를 계산하고 그걸 다시 rental_count 열로 본쿼리에 가져옴
    (SELECT count(rental_id)
     FROM sakila.rental r
     WHERE r.customer_id = c.customer_id) AS rental_count
FROM sakila.customer c
 -- rental 테이블에서 대여횟수를 계산하고,
 -- 평균 대여횟수도 계산하여 두 서브쿼리를 WHERE절의 조건으로 삽입
WHERE (SELECT count(rental_id)
	   FROM sakila.rental r
       WHERE r.customer_id = c.customer_id) > (
       SELECT AVG(rental_count)
       FROM (SELECT count(rental_id) AS rental_count
			 FROM sakila.rental
             GROUP BY customer_id
			) AS subquery_avg
		)
ORDER BY rental_count DESC;

2. 메모리 엔진 테이블 생성 및 해시 인덱스

(1) 메모리 엔진 테이블 생성 및 단일 키 조회

// 실습 코드
use my_database;

-- 메모리 엔진 테이블 생성.
CREATE TABLE hash_example(
	id INT NOT NULL,
    value VARCHAR(100),
    primary key(id)
) ENGINE = MEMORY;

INSERT INTO hash_example (id, value) values
(1, 'Alice'),
(2, 'Bob'),
(3, 'Charlie'),
(4, 'David'),
(5, 'Eve');

-- 해시 인덱스 확인
SHOW index FROM hash_example;

-- 단일 키 조회
SELECT * FROM hash_example WHERE id=3;

(2) 해시 충돌 예제

// 실습 코드
-- 해시 충돌
CREATE table hash_collision_example(
	id INT NOT NULL,
	value VARCHAR(100),
	hash_value INT NOT NULL,
	primary key (hash_value)
);

INSERT INTO hash_collision_example (id, value, hash_value)
values
(1, "Alice", MOD(1, 3)),
(2, "Bob", MOD(2, 3)),
(3, "Charlie", MOD(3, 3)),
(4, "David", MOD(4, 3)),	-- 충돌 발생
(5, "Eve", MOD(5, 3));		-- 충돌 발생
-- primary key에 의해 동일한 해시 값이 있으면 삽입 오류가 발생함.
-- => B-Tree로 해결

3. 이진 트리 테이블 생성

(1) 이진 트리 테이블 생성 및 데이터 삽입

// 실습 코드
CREATE table BinaryTree(
	id INT AUTO_INCREMENT primary key, -- 노드의 고유 ID
    value INT NOT NULL, -- 노드의 값
    left_child INT DEFAULT NULL, -- 왼쪽 자식 노드 ID
    right_child INT DEFAULT NULL -- 오른쪽 자식 노드 ID
);

-- 데이터 삽입하기
INSERT INTO BinaryTree(value) values (10);

-- 루트의 왼쪽 자식 추가
INSERT INTO BinaryTree(value) values (5);
UPDATE BinaryTree SET left_child = last_insert_id() WHERE id=1;

-- 루트의 오른쪽 자식 추가
INSERT INTO BinaryTree(value) values (15);
UPDATE BinaryTree SET right_child = last_insert_id() WHERE id=1;

SELECT * FROM BinaryTree;	-- 삽입된 트리를 표로 확인

4. B-Tree 테이블 생성

(1) B-Tree 테이블 생성 및 데이터 삽입

  • B-Tree 인덱스:
    1) 데이터를 정렬된 트리 구조로 저장하여 빠른 검색이 가능하게 함.
    2) N개의 자식을 가질 수 있도록 고안됨.
    3) 좌우 자식 간의 균형이 맞지 않을 경우 매우 비효율적이라, 항상 균형을 맞춘다는 의미에서 균형트리(balanced Tree)라고 불림.
// 실습 코드
-- 1. 테이블 생성
use my_database;
CREATE table BTreeExample(
	id INT auto_increment primary key, 	-- 각 데이터의 고유 ID
	value INT NOT NULL,	-- 데이터 값
    INDEX (value)	-- B-tree 인덱스 생성
);

-- 2. 값 삽입
-- 초기 값을 삽입
INSERT INTO BTreeExample (value) values (10), (20);
-- 값 삽입 (예: 30, 분할 발생)
INSERT INTO BTreeExample (value) values (30);
-- 값 삽입 (예: 40, 50, 추가 분할 발생)
INSERT INTO BTreeExample (value) values (40), (50);
-- 값 삽입 (예: 25)
INSERT INTO BTreeExample (value) values (25);

-- 3. 데이터 확인
SELECT * FROM BTreeExample ORDER BY value;

-- 4. 데이터 검색
-- 성적 검색
SELECT * FROM BTreeExample WHERE value BETWEEN 20 AND 40;

5. B+Tree 테이블 생성

(1) B+Tree 테이블 생성 및 데이터 삽입

  • B+Tree 인덱스:
    1) B-Tree와 기본 구조는 동일하나, leaf node들간 linked list형태로 연결되어 있음.
// 실습 코드
-- 1. 테이블 생성
use my_database;
CREATE table BPlusTreeExample(
	id INT auto_increment primary key,	-- 고유 ID
    value INT NOT NULL,		-- 값
    data VARCHAR(100),		-- 실제 데이터
    INDEX (value)			-- B+Tree 인덱스 생성
);

-- 2. 데이터 삽입
INSERT INTO BPlusTreeExample (value, data) values (10, 'Data A');
INSERT INTO BPlusTreeExample (value, data) values (20, 'Data B');
INSERT INTO BPlusTreeExample (value, data) values (30, 'Data C');
INSERT INTO BPlusTreeExample (value, data) values (40, 'Data D');
INSERT INTO BPlusTreeExample (value, data) values (50, 'Data E');

-- 3. 특정 데이터 검색
-- 값이 30인 데이터를 검색
SELECT * FROM BPlusTreeExample WHERE value=30;

-- 4. 특정 범위 검색(B+Tree 리프 노드가 연결되어 있어 범위 검색이 빠름.)
-- 값이 20 이상 40 이하인 데이터를 검색
SELECT * FROM BPlusTreeExample WHERE value BETWEEN 20 AND 40;

(2) 인덱스가 없는 경우

// 실습 코드
use my_database;
-- 기존 테이블 삭제
DROP TABLE IF EXISTS NoIndexTable;
DROP TABLE IF EXISTS IndexedTable;

-- NoIndexTable 생성 (인덱스 없음)
CREATE table NoIndexTable(
	id INT auto_increment primary key,
    value INT NOT NULL,
    data VARCHAR(100)
);

-- indexedTable 생성 (B+Tree 인덱스 있음)
CREATE table IndexedTable(
	id INT auto_increment primary key,
    value INT NOT NULL,
    data VARCHAR(100),
    Index idx_value(value)	-- B+Tree 인덱스 생성
);

-- 특정 값(678282)에 집중된 데이터 1,000,000개 삽입
INSERT INTO NoIndexTable (value, data)
SELECT 678282, CONCAT('Data_', FLOOR(RAND() * 1000000))
FROM dual
LIMIT 1000000;

-- 기타 값 추가 (100,000개)
INSERT INTO NoIndexTable (value, data)
SELECT FLOOR(RAND() * 1000000), CONCAT('Data_', FLOOR(RAND() * 1000000))
FROM dual
LIMIT 100000;

-- 고르게 분포된 데이터 10,000,000개 삽입
INSERT INTO IndexedTable (value, data)
SELECT FLOOR(RAND() * 1000000), CONCAT('Data_', FLOOR(RAND() * 1000000))
FROM dual
LIMIT 10000000;

-- NoIndexTable 데이터 분포 확인
SELECT value, COUNT(*) AS occurrences
FROM NoIndexTable
GROUP BY value
ORDER BY occurrences DESC
LIMIT 10;

-- IndexedTable 데이터 분포 확인
SELECT value, COUNT(*) AS occurrences
FROM IndexedTable
GROUP BY value
ORDER BY occurrences DESC
LIMIT 10;

-- NoIndexTable에서 특정 값 검색
SELECT * FROM NoIndexTable WHERE value = 678282;

-- IndexedTable에서 특정 값 검색
SELECT * FROM IndexedTable WHERE value = 678282;

-- 프로파일링 활성화(실행 시간을 비교하기 위해)
SET profiling = 1;

-- NoIndexTable 검색
SELECT * FROM NoIndexTable WHERE value = 678282;

-- IndexedTable 검색
SELECT * FROM IndexedTable WHERE value = 678282;

-- 실행 시간 확인
SHOW PROFILES;

정리

카테고리문법설명
SELECTSELECT column1, column2 FROM table특정 컬럼 데이터를 조회
WHEREWHERE condition조건에 맞는 데이터만 조회
ORDER BYORDER BY column [ASC / DESC]데이터를 지정된 컬럼 기준으로 정렬 (기본값은 오름차순 ASC, 내림차순은 DESC 사용)
GROUP BYGROUP BY column데이터를 그룹화
HAVINGHAVING condition그룹화된 데이터 조건 필터링
JOININNER JOIN table ON condition두 테이블의 데이터 결합
SUBQUERY(SELECT column FROM table WHERE condition)쿼리 내에서 다른 쿼리를 실행
COUNTCOUNT(column)특정 컬럼의 개수를 계산
AVGAVG(column)특정 컬럼 값의 평균 계산
INSERTINSERT INTO table (column1, column2) VALUES (value1, value2)테이블에 데이터 삽입
UPDATEUPDATE table SET column1 = value1 WHERE condition기존 데이터 수정
DELETEDELETE FROM table WHERE condition조건에 맞는 데이터를 삭제
CREATE TABLECREATE TABLE table_name (column_name data_type constraints)새로운 테이블 생성
PRIMARY KEYPRIMARY KEY(column)테이블의 고유 식별자 설정
FOREIGN KEYFOREIGN KEY(column) REFERENCES other_table(column)다른 테이블과의 참조 관계 설정
INDEXCREATE INDEX index_name ON table(column)특정 컬럼에 인덱스 생성
ENGINEENGINE = MEMORY테이블 저장 엔진 지정
MODMOD(value, divisor)나머지 계산
LAST_INSERT_IDLAST_INSERT_ID()마지막으로 삽입된 AUTO_INCREMENT 값 반환

파이썬

1. 기본

  • 알파벳, 숫자, 언더스코어(_)로 선언 가능
  • 대소문자 구분됨
  • 특별한 의미가 있는 예약어는 쓰지 않는다.
  • 파이썬은 들여쓰기에 민감함(주피터는 알아서 고쳐줌)

(1) 산술 연산자(+, -, *, /, //, %, **), print문

// 실습 코드
# 산술연산자
1+1 #더하기
2-3 #뺄셈
4*6 #곱하기
3/4 #나누기
3//4  #몫
3%5 #나머지
3**5  #제곱

# Q) 10의 제곱을 출력해보자.
10**2
10*10

a=3
b=5

print(a+b)
print("a+b")

(2) 변수의 주소(id)

// 실습 코드
id(a) # CPU 주소(코랩에서의)

(3) 증감 연산자(+=, -=, *=, /=)

// 실습 코드
a=1

a += 1  # 증감연산자(실행 할때마다 적용, 코랩에서 왼쪽의 [번호]가 실행된 순서번호)
print(a)

a -= 1
print(a)

a *= 1
print(a)

a /= 5
print(a)

(4) 데이터 타입(type) 및 형 변환(int, float, str, len)

// 실습 코드
# 변수의 타입 확인 및 형 변환
a = 1
b = 1
print(a, b)
type(a)

c = 3.5
d = 4.9
print(c, d)
type(c)

print(a+c, b+d)

d = int(c)  # 형 변환
print(d)
type(d)

a = 10
type(a)
float(a)    # 현재 a는 int이다. 형 변환을 아직 안했으므로.

(4) 문자열 길이(len) 계산 및 문자열 슬라이싱(문자열[시작:끝])

  • 문자열의 길이를 계산할 때에는 len함수를 사용하여 아래 예제 코드와 같이 계산한다.
  • 문자열에서 특정 문자열만을 가져오고 싶을 때에는 문자열[시작:끝]을 이용하여 아래와 같이 슬라이싱한다.(인덱스는 0부터 시작하며, 끝번호에 해당하는 문자는 읽어오지 않으니 끝번호는 항상 +1 하여 적용해야한다!!)
// 실습 코드
str_5 = '인덱싱을 하기 위한 string입니다.'
len(str_5)

# string만 슬라이싱 해오기
str_5[11:17]	# str_5[11] = 's', str_5[16] = 'g'
# str_5[11:16] = 'strin' 이므로 끝번호에 +1을 해주어야 한다.

정리

주제구문설명
산술 연산자+, -, *, /덧셈, 뺄셈, 곱셈, 나눗셈을 수행합니다.
//나눗셈의 몫을 반환합니다.
%나눗셈의 나머지를 반환합니다.
**거듭제곱을 계산합니다.
변수와 출력print(변수명)변수의 값을 출력합니다.
print("문자열")문자열 그대로를 출력합니다.
변수의 주소 확인id(변수명)변수의 메모리 주소를 반환합니다.
증감 연산자+=, -=, *=, /=변수의 값에 대해 덧셈, 뺄셈, 곱셈, 나눗셈 후 결과를 변수에 저장합니다.
데이터 타입 확인type(변수명)변수의 데이터 타입을 확인합니다.
형 변환int(값)값을 정수형으로 변환합니다.
float(값)값을 실수형으로 변환합니다.
str(값)값을 문자열로 변환합니다.
문자열 길이len(문자열)문자열의 길이를 반환합니다.
문자열 슬라이싱문자열[시작:끝]문자열에서 시작 인덱스부터 끝-1까지의 부분 문자열을 반환합니다.

0개의 댓글