[DB] DB 모델링

나무나무·2025년 3월 31일

DB

목록 보기
9/9

데이터 모델링

  • 개념적 모델링 : ERD(Entity Relationship Diagram) 생성 단계
    • 고객이 원하는 요구상을 찾아서 시각화 하는 단계라고 생각.
  • 논리적 모델링
    • 개념적 모델링 과정에서 추상화 된 데이터를 구체화하여 개체, 속성을 테이블화 하고 상세화 하는 과정
    • 데이터를 일관성 있게 만들고, 중복 데이터를 최소화 하기 위한 단계
  • 물리적 모델링

엔티티(Entity)

  • 구체적인 실제 관리 대상(유형, 무형 포함)

속성(attribute)

  • 엔티티가 가지고 있는 특징을 의미
  • 엔티티들은 하나 이상의 속성을 반드시 포함한다.
  • 속성이 가지는 값을 속성 값이라고 함.

식별자

  • 엔티티들을 구분해줄 수 있는 속성
  • 주 식별자 : (primary key)
    • 엔티티 데이터들을 서로 구분해줄 수 있는 속성을 의미🔑
    • 중복된 값 없이 유일해야 함.
  • 보조 식별자 : (Candidate Key)
    • 주 식별자 대체 가능한 다른 속성 → Unique key
  • 외래 식별자 : (Foreign Key)
    • 다른 엔티티와의 관계를 연결해주는 속성

관계

  • 엔티티와 엔티티 사이 관계가 존재 → 이걸 선으로 표현할 예정
  • 일대일 관계
    • ex) 학생 한 명당 지도 교수 한 명
  • 일대다 관계 : 관계형 DB에서 가장 보편적인 관계
  • 다대다 관계(불특정 관계)
    • 논리상은 가능하지만 물리적 구현이 불가능함 → 가운데에 교차 엔티티 추가(일대다 ↔ 다대일)

요구 분석 단계

  • 프로젝트에 어떤 요구사항이 있고, 이 기능은 어떻게 구현해야 할지,
  • 실습은 기존 DB를 가지고 그려보는 식으로 진행

Non-Identifying Relationship

  • 비식별 관계 → 점선 표기
  • 참조하는 엔티티의 주 식별자를 가져와서 외래 식별자로 사용하는 관계

Identifying Relationship

  • 식별 관계 → 실선 표기
  • 참조하는 엔티티의 주 식별자 값을 외래 식별자로 씀과 동시에 주 식별자로도 사용하는 관계

외래키를 제공하는 쪽이 부모 엔티티, 참조하는 쪽이 자식 엔티티에 해당됨.

도메인

  • 해당 속성이 갖는 값의 범위임 (남,여) / 등등

논리적 모델링

  • 정규화를 진행 → 이상을 없애고 중복된 데이터를 없애기 위한 과정.

이상

  • 정규화를 안했을 때 뭔가 잘못됐는데? 싶으면 이상
  • 삽입 이상 주문 시 → 주문에 대한 정보만 입력하지 않고 제품에 대한 정보까지 일일이 다 입력해야 함.
  • 수정 이상 주문한 제품 명을 수정할 경우 → 모든 제품에 대한 이름도 전부 찾아서 수정해줘야 함. → 이 경우 수정 안 된 제품이 생길 수 있음
  • 삭제 이상 주문 정보 삭제 시 → 제품 번호, 제품명, 단가까지 전부 삭제됨

제 1 정규화

  • 하나의 속성이 하나의 값을 갖도록 → 중복 제거

제 2 정규화

  • 주 식별자 전체에 종속적이지 않는 속성을 분리함. (주 식별자가 복합 식별자일 경우)
  • 기본키의 일부(부분 기본키)에 종속적인 경우 → 이를 제거하는 과정
  • 강의실은 학번과 관련 없는 속성 / 강의실은 강의 이름과 관련 있는 속성이기 때문에 따로 분리해주어야 함.
  • ‘오라클’ 의 강의실이 바뀔 경우 → ‘오라클’이 있는 강의실을 전부 바꿔주어야 하지만, 따로 분리할 경우 굳이 다 찾아서 바꿀 필요가 없음(갱신 이상 방지 가능)
  • 삽입 시에도 강의실 이름을 직접 일일이 다 추가할 필요 없음

제 3 정규화

  • 주 식별자에 종속적이지 않고, 다른 속성에 종속적인 속성 분리
  • 주 식별자가 아닌 다른 속성에 종속적인 속성을 따로 분리한다고 보면 됨.
  • 할인율은 등급에만 종속적
  • 할인율과 등급 / 등급과 고객 번호로 나눠줌

반 정규화

  • 나누지 않는 것이 효율이 좋은 경우
  • Join 을 많이 해야 하는 경우 → 상황에 따라 정규화 해둔 것을 돌리는 경우도 존재함.
  • 상황에 따라 이상이 발생할 수도 있음

물리적 모델링

  • 논리적 모델링 과정에서 데이터를 표현하고 → 이를 실제 DB에 맞게 구현하는 과정이 물리적 모델링 과정임
    논리적 DB 설계물리적 DB 설계
    엔티티(Entity)테이블(Table)
    속성(Attribute)컬럼(Column)
    주식별자(Primary Identifier)기본키(Primary Key)
    외래식별자(Foreign Identifier)외래키(Foreign Key)
    뷰(View)
    인덱스(Index)
  • 보조 식별자 : 상황에 따라 unique key로 바꾸기도
  • 추가적인 뷰 / 인덱스 생성 가능

테이블(Table)

  • CREATE ALTER, DROP
  • 테이블 생성 : CREATE TABLE 테이블명
  • 예제 코드
    -- 테이블 생성 실습
    
    CREATE TABLE tb_member (
    	mem_no INT,
    	mem_id VARCHAR(20),
    	mem_pass VARCHAR(20), 
    	mem_name VARCHAR(15),
    	enroll_date DATE DEFAULT CURDATE()
    ); 
    
    -- 테스트 테이블에 샘플 데이터 추가
    INSERT INTO tb_member VALUES(1, 'USER1', '1234', '홍길동','2024-12-08');
    INSERT INTO tb_member VALUES(2, 'USER2', '2345', '이몽룡', CURDATE());
    INSERT INTO tb_member VALUES(3, 'USER3', '1234', '성춘향', DEFAULT);
    -- default 값이 삽입될 것. 
    
    INSERT INTO tb_member (mem_no, mem_id) VALUES (4, 'USER4');
    -- 값을 넣지 않는 곳은 전부 default 값으로 삽입되게 됨. 

제약 조건

  • 데이터 무결성을 지키기 위한 제한 조건 → 특정 범위의 값만, 어떤 조건을 만족하는 값만 입력되도록
  • Primary Key 제약 조건
    • 중복 데이터 입력 x
    • NULL 값 입력 x
    • 대리키 방식 : 전혀 상관 없는 키를 가지고 기본 키를 생성하는 방식
  • 예제 코드
    CREATE TABLE tb_member(
    	mem_no INT NOT NULL,
    	mem_id VARCHAR(20) NOT NULL,
    	mem_pass VARCHAR(20) NOT NULL,
    	mem_name VARCHAR(15) NOT NULL,
    	enroll_date DATE DEFAULT CURDATE()
    ); 
    
    UPDATE tb_member
    SET mem_id = NULL
    WHERE mem_name = '홍길동';
    -- NOT NULL로 인해 데이터 삽입/갱신이 불가능하다.
    
    -- 제약 조건 실습
    -- 기본키 Primary Key / Unique
    -- UNIQUE 조건은 중복된 값이 들어오면 안돼! 임. NULL 값이 들어올 수 있음ㅇㅇ
    INSERT INTO tb_member VALUES(1, 'USER1', '1234', '홍길동', CURDATE());
    -- 이 경우 동일한 값을 갖는 여러 행이 존재하게 됨. 
    
    CREATE TABLE tb_member(
    	mem_no INT PRIMARY KEY, -- primary key는 노란색 열쇠로 나타나게 됨. 
    	mem_id VARCHAR(20) NOT NULL UNIQUE,  -- unique는 빨간색 열쇠로 나타남.
    	mem_pass VARCHAR(20) NOT NULL,
    	mem_name VARCHAR(15) NOT NULL,
    	enroll_date DATE DEFAULT CURDATE()
    ); 
    
    -- mem_no, mem_id는 기능적으로 동일하지만 의미가 다름.
    INSERT INTO tb_member VALUES (1, 'USER1', '1234', '홍길동', '2024-12-08');
    INSERT INTO tb_member VALUES (2, 'USER2', '1234', '이몽룡', CURDATE());
    -- INSERT INTO tb_member VALUES (1, 'USER3', '1234', '성춘향', DEFAULT);
    -- 에러가 나게 됨. 이미 1이라는 기본키 값이 존재하기 때문에 
    -- INSERT INTO tb_member VALUES (NULL, 'USER3', '1234', '성춘향', DEFAULT);
    -- NULL 값도 허용하지 않기 때문에 에러가 나게 됨. 
    
    CREATE TABLE tb_member(
    	mem_no INT AUTO_INCREMENT PRIMARY KEY,   -- column 영역에서 제약조건 설정
    	mem_id VARCHAR(20) NOT NULL UNIQUE,  
    	mem_pass VARCHAR(20) NOT NULL,
    	mem_name VARCHAR(15) NOT NULL,
    	enroll_date DATE DEFAULT CURDATE()
    ); 
    -- autoincrement : 해당 열의 값을 자동으로 1씩 증가시켜 입력
    INSERT INTO tb_member(mem_id, mem_pass, mem_name) VALUES ('USER1', '1234', '홍길동');
    INSERT INTO tb_member(mem_id, mem_pass, mem_name) VALUES ('USER2', '1234', '이몽룡');
    -- auto increment -> 대리키(GUI)
    
    SELECT UUID();
    -- 고유성이 보장된 숫자.
    -- auto_increment 로 단순하게 증가하는 숫자를 쓰기 싫다면-사용하도록
    
    -- 열 정의 후, 제약 조건을 별도로 지정하는 방법 -> 테이블 영역에서 제약조건 설정
    DROP TABLE tb_member;
    CREATE TABLE tb_member(
    	mem_no INT AUTO_INCREMENT,   -- column 영역에서 제약조건 설정
    	mem_id VARCHAR(20) NOT NULL,  
    	mem_pass VARCHAR(20) NOT NULL,
    	mem_name VARCHAR(15) NOT NULL,
    	enroll_date DATE DEFAULT CURDATE(),
    	-- primary key 제약 조건의 경우 여러개의 열을 묶어서 하나의 기본키 생성이 가능함. 
    	PRIMARY KEY(mem_no),
    	-- UNIQUE(mem_id, mem_id)  -- unique 제약 조건도 여러 개의 열을 묶어서 제약 조건으로 생성할 수 있음.
    	CONSTRAINT uq_tb_member_mem_id UNIQUE(mem_id) -- UNIQUE로 만들어진 애들은 이름이 바뀌어 있음
    ); 
    
    INSERT INTO tb_member(mem_id, mem_pass, mem_name) VALUES ('USER1', '1234', '홍길동');
    INSERT INTO tb_member(mem_id, mem_pass, mem_name) VALUES ('USER2', '1234', '이몽룡');
    
    -- FOREIGN KEY 제약조건
    -- 부모 테이블 생성
    CREATE TABLE tb_member_grade(
    	grade_code VARCHAR(10) PRIMARY KEY,
    	grade_name VARCHAR(10) NOT NULL
    );
    
    INSERT INTO tb_member_grade VALUES('vip', 'VIP 회원');
    INSERT INTO tb_member_grade VALUES('gold', 'gold 회원');
    INSERT INTO tb_member_grade VALUES('silver', 'silver 회원');
    
    CREATE TABLE tb_member(
    	mem_no INT AUTO_INCREMENT PRIMARY KEY,  
    	mem_id VARCHAR(20) NOT NULL UNIQUE,  
    	mem_pass VARCHAR(20) NOT NULL,
    	mem_name VARCHAR(15) NOT NULL,
    	grade_code VARCHAR(10) REFERENCES tb_member_grade(grade_code),   -- tb_member_grade의 grade_code를 참조하겠다-
    	enroll_date DATE DEFAULT CURDATE()
    ); 
    
    -- 외래키는 녹색 열쇠 형태로 만들어짐
    INSERT INTO tb_member(mem_id, mem_pass, mem_name, grade_code) VALUES ('USER1', '1234', '홍길동', 'vip' );
    INSERT INTO tb_member(mem_id, mem_pass, mem_name, grade_code) VALUES ('USER2', '1234', '이몽룡', NULL );
    -- 두 번째 꺼는 Insert가 안될거임 -> 외래키 제약조건은 외래키 열에서 잘못된 값이 들어오는 걸 막아줌
    -- 외래키 열에는 아무 값이나 들어올 수 있는 것이 아닌, 참조하고 있는 테이블의 참조 열의 값만 가져올 수 있음
    -- tb_member_grade 테이블에 grade_code 열에 'bronze'라는 값이 없어서 외래키 제약 조건에 위배되어 에러가 나타나게 된다. 
    
    SELECT m.mem_no,
    		m.mem_id,
    		m.mem_name,
    		mg.grade_name
    FROM tb_member m
    LEFT OUTER JOIN tb_member_grade mg ON m.grade_code = mg.grade_code;

외래키 제약 조건(FOREIGN KEY)

  • 외래키 : 외래 식별자 → 다른 테이블과 관계를 맺기 위해 사용되는 열
  • 참조 무결성 : 두 테이블 사이의 데이터 일관성 → 외래키 값에 엉뚱한 값이 들어오지 않도록 막아줌.
  • 외래키가 참조하는 테이블의 열은 중복된 값이 없는 열만 가능함.
  • ON UPDATE CASCADE : 외래 키 값을 업데이트할 경우 행 전체가 업데이트 됨
  • ON UPDATE SET NULL : 부모 테이블 값을 업데이트 할 경우, 자식 테이블은 똑같이 업데이트 되지 않고 NULL 값을 갖게 된다.
  • ON DELETE CASCADE : 외래 키 값을 삭제할 때 참조하는 쪽의 값 전체가 사라짐 → vip 회원을 지운다고 했을 때, vip 회원 전부가 다 사라지게 되는 셈. → 한 번에 데이터가 전부 사라지기 때문에 이런게 있다- 정도로만 참고
  • ON DELETE SET NULL : 부모 키의 값을 지울 경우 참조하는 테이블의 값이 NULL로 바뀌게 됨.

CHECK 제약 조건

  • 열에 입력되는 데이터를 점검함
  • 제약 조건을 설정한 뒤에는 조건에 위배되는 값은 입력되지 못한다.
  • 예제 코드
    -- CHECK 제약 조건
    
    CREATE TABLE tb_member(
    	mem_no INT AUTO_INCREMENT PRIMARY KEY,  
    	mem_id VARCHAR(20) NOT NULL UNIQUE,  
    	mem_pass VARCHAR(20) NOT NULL,
    	mem_name VARCHAR(15) NOT NULL,
    	gender CHAR(2),
    	age TINYINT, 
    	grade_code VARCHAR(10) REFERENCES tb_member_grade(grade_code), 
    	enroll_date DATE DEFAULT CURDATE()
    ); 
    
    INSERT INTO tb_member(mem_id, mem_pass, mem_name, gender, age, grade_code) 
    VALUES ('USER1', '1234', '홍길동', '남남', 36, 'vip' );
    
    INSERT INTO tb_member(mem_id, mem_pass, mem_name, gender, age, grade_code) 
    VALUES ('USER2', '1234', '이몽룡', '남자', 30, NULL );
    
    INSERT INTO tb_member(mem_id, mem_pass, mem_name, gender, age, grade_code) 
    VALUES ('USER3', '1234', '성춘향', '남남', -28, 'silver' );
    
    SELECT *
    FROM tb_member;
    
    -- 위의 쿼리는 아무 문제가 없지만, 성별은 (남자, 여자) 중 하나로, 나이는 0~150 사이의 값을 갖도록 설정해 두고 싶다. 
    
    DROP TABLE tb_member;
    
    CREATE TABLE tb_member(
    	mem_no INT AUTO_INCREMENT PRIMARY KEY,  
    	mem_id VARCHAR(20) NOT NULL UNIQUE,  
    	mem_pass VARCHAR(20) NOT NULL,
    	mem_name VARCHAR(15) NOT NULL,
    	gender CHAR(2) CHECK(gender IN ('남자', '여자')),
    	age TINYINT, 
    	grade_code VARCHAR(10) REFERENCES tb_member_grade(grade_code), 
    	enroll_date DATE DEFAULT CURDATE(),
    	-- 열을 지정한 뒤에 제약 조건을 지정하는 경우
    	CONSTRAINT ck_th_member_age CHECK(age BETWEEN 0 AND 150)    -- 제약 조건 이름은 ck_th_member_age가 됨
    ); 
    
    INSERT INTO tb_member(mem_id, mem_pass, mem_name, gender, age, grade_code) 
    VALUES ('USER1', '1234', '홍길동', '남남', 36, 'vip' );
    -- 위와 동일한 insert문을 사용하게 되면 gender에 걸려있는 제약조건에 의해 에러가 났다는 의미
    INSERT INTO tb_member(mem_id, mem_pass, mem_name, gender, age, grade_code) 
    VALUES ('USER2', '1234', '이몽룡', '남자', 30, NULL );
    -- 얘는 문제 없이 insert 됨
    INSERT INTO tb_member(mem_id, mem_pass, mem_name, gender, age, grade_code) 
    VALUES ('USER3', '1234', '성춘향', '여자', -28, 'silver' );
    -- 얘는 제약 조건 위배 -> ck_tb_member_age 의 제약 조건에 위배되었기 때문에 오류가 발생했다 
    
    -- 성별, 나이에 유효하지 않은 값들은 삽입이 불가능함 .
    

뷰(View)

  • 테이블과 비슷한 모습 → 일종의 ‘가상의 테이블’
  • 내부 SELECT 문을 통해 원하는 조건만 조회해서 보여주는 테이블 같은 느낌
  • 보안적인 부분에서 원하는 부분만 보여질 수 있도록 권한을 부여하는 방식처럼 사용함.

뷰 생성 : CREATE VIEW AS SELECT ~

인덱스(Index)

  • 데이터 조회 시 데이터에 빠르게 조회하는데 도움을 주는 도구 정도로 생각하면 됨.
  • ex) 책에서 내용을 찾고 싶으면 목차를 이용하는 것처럼~
  • 인덱스를 생성해서 사용한다면 더 빠른 응답 속도를 얻을 수 있음. 시스템 전체 성능 향상
    • 데이터 변경이 많이 일어나면 오히려 인덱스로 인해 속도가 저하될 수 있으므로 제대로 사용해야 함.

클러스터형 인덱스

  • 테이블 당 “한 개” 생성 가능 → 그렇기 때문에 우리가 직접 만든다기 보단 기본 키가 지정된 열에 클러스터형 인덱스가 자동으로 생성된다고 생각하면 됨.
  • primary key를 생성하고, 이걸로 클로스터형 인덱스를 생성한다고 생각하면 됨.
  • 우리가 입력한 데이터들을 오름차순으로 정렬해줌. → 데이터가 입력되는 즉시 바로 정렬이 됨.
  • 기준이 되는 Column을 가지고 행을 정렬해줌.

보조 인덱스

  • 테이블 당 여러 개 생성 가능
  • 얘는 기준 열을 가지고 정렬해주지는 않음. → 원하는 데이터가 어느 위치에 있는지만 알려줌
  • Unique 제약 조건을 걸어줄 경우 → 보조 인덱스가 생성된다고 보면 좋음.

→ 직접 인덱스를 생성하고자 한다면 ALTER TABLE 이나 CREATE INDEX 등의 구문을 통해 생성함. → 이런 방식으로 생성된 인덱스는 다 보조 인덱스라고 생각하면 됨.

→ 여러 개의 열을 묶어서 하나의 인덱스로 만들 수도 있음.

인덱스는 고유 인덱스 vs 비고유 인덱스로 나눌 수 있음

  • 고유 인덱스 : 인덱스 생성 시 열을 지정 → 이 값이 중복된 값이 없는 애들
  • 비고유 인덱스 : 중복 값 존재 가능

※ 주의 사항 ※

  • 인덱스는 검색 속도를 높이기 때문에 WHERE 같은 조건 절에서 자주 사용되는 열에 인덱스를 만들어주어야 한다.
  • 기본키 / 외래키 등등
  • 데이터 중복도가 높다 : 같은 데이터가 많이 들어 있다는 의미 → 중복도가 높은 경우는 mariaDB가 인덱스를 쓰지 않을 수 있음 → 인덱스로 큰 효과를 볼 수 없어 크게 의미가 없음
  • JOIN에 사용되는 열에도 인덱스를 생성해주는 것이 좋다.
  • DML(INSERT, UPDATE, DELETE) 작업이 얼마나 자주 일어나는지를 고려해주어야 함. → 해당 작업이 많이 일어날수록 인덱스 정렬을 위한 데이터 수정이 자주 일어나겠지 → 많이 일어나는 경우는 인덱스를 생성하지 않는 편이 좋을 수도

→ 터미널에서 sql문 일괄 실행 → mariadb -u root -p 0000 → source employee.sql → 일괄 실행

  • 예제 코드
    -- 인덱스 실습
    -- 검색 속도가 빨라지는 것을 확인
    -- 사이트에서 제공하는 용량이 큰 데이터들을 다뤄본다. 
    
    SELECT *
    FROM employees;
    
    EXPLAIN SELECT * FROM employees ;
    -- EXPLAIN : 해당 쿼리문을 실행할 때의 실행 계획을 확인할 수 있음
    -- type에 ALL 이라고 되어 있음 -> 이는 인덱스를 사용하지 않고 전체 데이터를 검색했다는 것을 의미함. -> full table scan
    
    SELECT * FROM employees WHERE emp_no = 48730;
    EXPLAIN SELECT * FROM employees WHERE emp_no = 48730; 
    -- 이 경우 type은 const, possible key는 primary가 됨. -> const : cluster 형 인덱스인 프라이머리라는 데이터를 찾아옴
    -- 인덱스를 이용해 검색헀다-> 전체 데이터 조회 보다 속도가 훨씬 빠르게 조회됨.
    
    SELECT *
    FROM employees 
    WHERE first_name = 'moon';
    EXPLAIN SELECT * FROM employees WHERE first_name = 'moon';
    -- type은 all로 나오게 됨. -> 해당 행을 다 확인할 때까지 rows : 298803개의 데이터를 검색했다고 생각하면 됨. 
    -- 데이터가 많으면 많을 수록 더 많은 데이터를 검색하게 됨. 
    -- first_name 은 제약 조건이 정해져 있는 애가 아니기 때문에 index가 자동으로 완성된 애가 아님. 
    
    -- 실습 : first_name 을 통해 인덱스를 생성해볼 것. -> first_name에는 중복된 값이 존재하기 때문에 비고유 인덱스로 생성할거임
    -- 인덱스 생성 
    CREATE INDEX idx_employees_fisrt_name
    ON employees(first_name);
    
    ANALYZE TABLE employees; -- Msg_text : OK 
    -- first_name에 해당되는 인덱스는 녹색으로 나타나게 됨. 
    
    SELECT * FROM employees WHERE first_name = 'Moon';
    EXPLAIN SELECT * FROM employees WHERE first_name = 'Moon';
    -- type이 ref형으로 나오게 됨. -> 보조 인덱스를 싱행시켰다- 
    -- 사용 가능한 인덱스(possible_keys)에는 idx_employees_first_name이 나오게 됨. 
    -- rows에는 243개가 나오게 됨. -> 실제 검색에는 243번 밖에는 안썼다는 의미임. 
    
    -- 데이터가 압도적으로 많지는 않기 때문에 속도에서 큰 차이가 명확히 나지는 않지만,
    -- rows 개수로 차이가 명확히 드러남 
    
    EXPLAIN SELECT * FROM employees WHERE first_name = 'moon' AND last_name = 'Yetto'; 
    -- last_name이 Yetto인 경우 -> moon을 조회했을 때와 마찬가지로 243개를 조회함
    
    CREATE INDEX idx_employees_firstname_lastname
    ON employees(first_name, last_name);
    -- 인덱스가 저장되어 있는 페이지도 존재하다 보니 데이터베이스의 크기가 조금 늘어나게 됨.
    
    EXPLAIN SELECT * FROM employees WHERE first_name = 'moon' AND last_name = 'Yetto'; 
    -- 사용 가능한 인덱스가 idx_employees_fisrt_name,idx_employees_firstname_lastname로 2개 있었고, 
    -- rows는 243개에서 1개로 줄게 됨. -> 단 한 번의 탐색으로 데이터를 찾아왔다는 의미. 
    -- 열 두개를 묶어서 하나의 인덱스로 생성해주면 되겠지
    
    -- 테이블에 지정된 인덱스 확인
    SHOW INDEX FROM employees; -- employees 테이블에 저장된 인덱스 종류 확인
    -- Non_unique : 고유 인덱스(0)인지 비고유 인덱스(1)인지?
    -- Seq_in_index : 인덱스 저장 순번이라고 보면 됨(column_name -> first_name -> last_name)
    -- Cardinality : 데이터 수 정도로 생각
    -- index_typ : BTREE라는 알고리즘으로 저장되어 있음.
    
    -- 인덱스 삭제
    DROP INDEX idx_employees_firstname_lastname ON employees;
    DROP INDEX idx_employees_fisrt_name ON employees;
    
    -- 인덱스가 있는데도 사용하지 않는 경우 
    -- 1) 전체 데이터를 Select 할 때
    SELECT * FROM employees;
    EXPLAIN SELECT * FROM employees;
    -- 이 경우 인덱스가 있어도 사용하지 않음
    
    SELECT * FROM employees WHERE emp_no < 250000;
    EXPLAIN SELECT * FROM employees WHERE emp_no < 250000;
    -- 전체 데이터 개수는 약 30만개/ -> 조건으로는 25만개 밑으로 조회할 경우 
    -- 사용 가능한 키로 primary key가 있는데도 인덱스를 사용하지 않음
    
    SELECT * FROM employees WHERE emp_no < 100000;
    EXPLAIN SELECT * FROM employees WHERE emp_no < 100000;
    -- 이 경우에서는 primary key를 이용해서 조회함. -> type은 range
    -- 범위를 지정해서 선택할 때 -> 전체 데이터의 30% 이상을 조회할거면 차라리 전체 데이터를 조회하는게 빠르겠다고 판단,
    -- 이 경우 인덱스를 사용하지 않음. 
    
    SELECT * FROM employees WHERE emp_no = 100000; 
    EXPLAIN SELECT * FROM employees WHERE emp_no = 100000; 
    -- 위의 경우에서는 인덱스로 기본키를 사용, 딱 한 개만 조회하게 됨. 
    SELECT * FROM employees WHERE emp_no * 1 = 100000; 
    EXPLAIN SELECT * FROM employees WHERE emp_no * 1 = 100000; 
    -- 인덱스의 기준이 되는 열에 함수나 산술 연산을 하게 될 경우 인덱스를 사용하지 않음. 
    
    -- 중복되는 데이터가 많은 열을 인덱스 기준으로 두는 것도 의미 없음
    -- 인덱스 추가
    ALTER TABLE employees ADD INDEX idx_employees_gender (gender); 
    ANALYZE TABLE employees;
    
    SELECT * FROM employees WHERE gender = 'M'; -- 조회는 전체 데이터의 절반 이상 
    EXPLAIN SELECT * FROM employees WHERE gender = 'M';
    -- possible key는 있지만 type은 all로 나타나 됨 -> 중복 데이터가 많아서 인덱스를 생성해서 검색해도 사용하지는 않음.
    
    ALTER TABLE employees DROP INDEX idx_employees_gender;
    
profile
백엔드 개발자 나무입니다

0개의 댓글