쿼리 작성과 연관된 시스템 변수, MySQL 연산자 및 내장 함수, SELECT, INSERT, DELETE, UPDATE, DDL, 쿼리 성능 테스트에 대해 알아보자
대소문자 구분
, 문자열 표기 방법
등 SQL 작성 규칙은 MySQL 서버 설정에 따라 달라짐
sql_mode
: 여러 개의 값 동시에 설정, SQL 문장 작성 규칙
과 데이터 타입 변환
및 기본값
제어 등
STRICT_ALL_TABLES & STRICT_TRANS_TABLES
: INSERT
나 UPDATE
로 데이터 변경
시 칼럼 타입과 저장되는 값 타입이 다를 때 자동
으로 타입 변경
수행하는데, 적절히 변환이 어렵
거나 칼럼에 저장될 값이 없거나
값의 길이가 최대 길이보다 큰 경우
이때 문장을 계속 실행할지 아니면 에러를 발생시킬지 결정 => 엄격한 모드
ANSI_QUOTES
: 홑따옴표
만 문자열 값 표기, 쌍따옴표
는 칼럼명이나 테이블명 표기ONLY_FULL_GROUP_BY
: SELECT절
에 GROUP BY절에 명시된 칼럼
과 집계 함수
만 사용 가능PIPE_AS_CONCACT
: ||
를 OR
이 아닌 문자열 연결 연산자
로 사용PAD_CHAR_TO_FULL_LENGTH
: CHAR
칼럼값을 가져올 때 뒤쪽 공백 제거 X
NO_BACKSLASH_ESCAPES
: 역슬래시
를 이스케이프 용도로 사용 X
IGNORE_SPACE
: 스토어드 프로시저
나 함수명
과 괄호 사이
에 있는 공백
도 이름
으로 간주하는데, 해당 옵션 사용시 공백 무시
REAL_AS_FLOAT
: REAL
타입은 DOUBLE
타입의 동의어로 사용되나, 해당 옵션 사용시 FLOAT
의 동의어로 변경NO_ZERO_IN_DATE & NO_ZERO_DATE
: 잘못된 날짜
저장 불가능ANSI
: SQL 표준
에 맞게 동작하게 만들어줌TRADITIONAL
: STRICT
보다 조금 더 엄격
한 방법으로, 여러 모드 조합
으로 만들어진 모드lower_case_table_names
=> 1
로 설정 : 모두 소문자
로만 저장, 대소문자
구분 X
=> 2
로 설정 : 저장
은 대소문자 구분, 쿼리
에선 구분 X
=> 0
(기본값) : DB나 테이블명에 대해 대소문자 구분
예약어
와 같은 키워드로 테이블이나 칼럼 생성시 역따옴표
나 쌍따옴표
로 감싸야 함
SQL의 각 키워드
는 표기된 순서
로만 사용
대문자
로 표기된 단어는 모두 키워드
=> 키워드는 대소문자 구분X
로 사용 가능
이탤릭체
로 표현한 단어는 사용자
가 선택해서 작성하는 토큰
=> 테이블명
이나 칼럼명
등
대괄호
는 선택 사항
파이프
는 앞과 뒤 키워드나 표현식 중 단 하나만 선택
해서 사용 가능
중괄호
는 괄호 내 아이템 중 반드시 하나 사용
해야 함
...
는 앞에 명시된 키워드나 표현식 조합이 반복
될 수 있음을 나타냄
SQL 표준
: 문자열은 홑따옴표(')
select * from departments where dept_no='d001';
select * from departments where dept_no="d001"; -- MySQL
홑따옴표
포함시 홑따옴표 두 번 연속
해서 입력하면 됨
쌍따옴표
포함시 쌍따옴표 두 번 연속
해서 입력하면 됨
select * from departments where dept_no='d''001';
select * from departments where dept_no='d""001';
select * from departments where dept_no="d'001"; -- MySQL
select * from departments where dept_no="d""001"; -- MySQL
예약어
충돌 피하기 위해 역따옴표(``)
사용
select `column` from tab_test;
-- 다른 DBMS는 쌍따옴표나 대괄호로 감쌈
비교 대상
이 문자열
이면 문자열 값
을 숫자 값
으로 자동 변환
정해진 형태의 날짜 포맷
으로 표기하면 자동으로 DATE
나 DATETIME
값으로 변환
select * from dept_emp where from_date='2011-04-29'; -- 문자열을 DATE로 변환
select * from dept_emp where from_date=str_to_date('2011-04-29','%Y-%m-%d'); --위랑 같음
BOOL
이나 BOOLEAN
은 TINYINT
타입에 대한 동의어
=> FALSE
또는 TRUE
형태로 비교하거나 값 저장 가능
=> FALSE
: 0
=> TRUE
: 1
create table tb_bool (bool_value boolean);
insert into tb_bool values (false), (true), (1), (2), (3) ;
select * from tb_bool;
select * from tb_bool where bool_value in (false, true);
<=>
: =
와 같으며 부가적으로 null값
에 대한 비교
수행
select 1=1, null=null, 1=null;
select 1<=>1, null<=>null, 1<=>null;
한 쪽만 Null
이라면 FALSE 반환
같지 않다
TRUE
또는 FALSE
연산의 결과를 반대로
만드는 연산자
select !1;
select not (1=1);
AND
는 &&
, OR
은 ||
와 같으나, 다른 용도로 사용될 수 있는 &&
와 ||
는 사용 자제
=> 우선순위는 AND
가 더 높음
일반적으론 /
사용, 정수
부분만 가져오려면 DIV
, 나머지
를 가져오려면 %
또는 MOD
사용
select 29/9;
select 29 div 9;
select mod(29,9); -- 또는 29 mod 9
문자열 값이 어떤 패턴
을 만족하는지 확인하는 연산자
RLIKE
는 REGEXP
와 똑같음
REGEXP
좌측
에 비교 대상 문자열
값 또는 문자열 칼럼, 우측
에 정규 표현식
사용
select 'abc' regexp '^[x-z]'; -- x, y, z로 시작하는지 검증
^
: 문자열의 시작
표시$
: 문자열의 끝
표시[]
: 문자
그룹
표시, 안에 표시된 문자 중 하나
인지 확인()
: 문자열
그룹 표시, 안에 표시된 문자열
모두 있는지 확인|
: |
로 연결된 문자열
중 하나인지 확인.
: 어떠한 문자든지 1개의 문자
표시*
: 앞에 표시된 정규 표현식이 0
또는 1번 이상
반복+
: 1번 이상
반복?
: 0
또는 1번만
[0-9]* : 0~9까지 숫자만 0 또는 1번 이상 반복
^Tear : Tear 문자열로 시작하는 정규 표현
Tear$ : Tear 문자열로 끝나는 정규 표현
^Tear$ : Tear와 같은 문자열
REGEXP
조건 비교는 인덱스 레인지 스캔
불가
어떤 상수 문자열
이 있는지 없는지 정도 판단
select 'abcdef' like '%abc';
와일드카드 문자
%
: 0
또는 1개 이상
의 모든 문자에 일치_
: 정확히 1개
의 문자에 일치%
또는 _
문자 자체 비교시 escape 절
설정
select 'a%' like 'a/%' escape '/';
와일드카드
문자가 뒤쪽
에 있다면 인덱스 레인지 스캔
가능
select * from dept_emp
where dept_no='d003' and emp_no=10001;
select * from dept_emp
where dept_no between 'd003' and 'd005' and emp_no=10001;
between
은 범위 비교
이고, IN
은 동등 비교 연산자
와 비슷
하다.
between
으로 범위 검색시 emp_no
조건은 범위를 줄이지 못함
select * from dept_emp
where dept_no in ('d003', 'd004', 'd005')
and emp_no=10001;
-- emp_no도 작업 범위 줄이는 용도로 사용 가능
여러 칼럼
으로 인덱스가 만들어져 있는데, 인덱스 앞쪽
에 있는 칼럼 선택도
가 떨어질 때 IN
으로 변경하면, 쿼리 성능 개선
-- 세미 조인 최적화를 통해 더 빠른 쿼리로 알아서 변환
select *
from dept_emp
where dept_no in(
select dept_no
from departments
where dept_no between 'd003' and 'd005')
and emp_no=10001;
여러 개의 값에 대해 동등 비교 연산
수행
=> 여러 번
의 동등 비교로 실행해 빠르게 처리됨
explain
select *
from dept_emp
where (dept_no, emp_no) in (('d001', 10017), ('d002', 10144), ('d003', 10054));
NOT IN
은 인덱스 풀 스캔
으로 표시되는데, 부정형 비교
여서 인덱스
를 이용해 처리 범위
를 줄이는 조건으로 사용할 수 없기 때문
사용자 정의 함수
: C/C++ API
를 이용해 사용자가 원하는 기능을 직접 함수
로 만들어 추가
=> 스토어드 프로그램
으로 작성되는 함수랑은 다름
IFNULL()
: 칼럼이나 표현식 값이 NULL
인지 비교하고, NULL
이면 다른 값
으로 대체
ifnull(비교하려는 칼럼, 대체할 값);
-- 첫 번째가 null이 아니면 첫 번째 인자 값 반환
ISNULL()
: 칼럼 값이 NULL
인지 아닌지 비교, null
이면 true(1)
, null이 아니면
false(0)
반환
isnull(칼럼);
select ifnull(null, 1);
select isnull(1/0);
현재의 시간
을 반환
NOW()
: 하나의 SQL에서 같은 값
가짐
SYSDATE()
: 하나의 SQL에서도 호출되는 시점
에 따라 결과값 달라짐
select now(), sleep(2), now();
select sysdate(), sleep(2), sysdate();
SYSDATE() 함수
는 문제
가 있다
레플리카 서버
에서 안정적으로 복제X비교되는 칼럼
은 인덱스 효율적으로 사용X
explain
select emp_no, salary, from_date, to_date
from salaries
where emp_no=10001 and from_date>sysdate();
emp_no
칼럼만 인덱스
를 사용할 수 있어 key_len
이 4바이트
가 됨
SYSDATE()
함수는 함수가 호출될 때마다
다른 값
을 반환하므로 인덱스 스캔
시에도 매번 비교되는 레코드
마다 함수 실행
해야 함
sysdate-is-now
를 설정해 SYSDATE()
가 NOW()
와 동일
하게 작동하도록 설정 권장
DATE_FORMAT()
: DATETIME
타입의 칼럼이나 값을 원하는 형태의 문자열로 변환
할 때
지정문자 | 내용 |
---|---|
%Y | 4자리 연도 |
%m | 2자리 숫자 표시 월 |
%d | 일자 |
%H | 두자리 숫자 표시 시 (00~23) |
%i | 분 |
%s | 초 |
select date_format(now(), '%Y-%m-%d') as current_dt;
STR_TO_DATE()
: 문자열
을 DATETIME
타입으로 변환
select str_to_date('2024-05-10', '%Y-%m-%d') as current_dt;
DATE_ADD()
, DATE_SUB()
: 특정 날짜
에서 연도나 월일 또는 시간 등을 더하거
나 뺄
때
date_add(연산 수행 날짜, interval n [YEAR, MONTH, ...]);
select date_add(now(), interval 1 day) as tomorrow;
단위 | 의미 |
---|---|
MICROSECOND | 마이크로 초 |
QUARTER | 분기 |
WEEK | 주 |
UNIX_TIMESTAMP()
: '1970-01-01 00:00:00'
으로부터 경과된 초
의 수 반환
FROM_UNIXTIME()
: 타임스탬프 값
을 DATETIME
타입으로 변환하는 함수
select unix_timestamp();
select from_unixtime(unix_timestamp());
RPAD()
, LPAD()
: 문자열의 좌측
또는 우측
에 문자를 덧붙여서
지정된 길이의 문자열로 만듦
RPAD(문자열, 적용 후 문자열 길이, 패딩할 문자);
LPAD ..;
RTRIM()
, LTRIM()
, TRIM()
: 연속된 공백 문자
제거
select rpad('Close', 10,'_');
select ltrim(' Close') as name;
여러 개의 문자열을 연결
해서 하나의 문자열
로 반환
select concat('Georgi', 'Christian', CAST(2 as char)) as name;
CONCAT_WS()
: 문자열 연결시 구분자
넣어줌
select concat_ws(',','Georgi', 'hi') as name;
-- 첫 번째 인자 : 구분자로 사용할 문자
그룹 함수
중 하나로, 값들을 먼저 정렬
한 후 연결
하거나 구분자
설정 가능
select group_concat(dept_no) from departments;
select group_concat(dept_no separator '|') from departments;
select group_concat(dept_no order by emp_no desc)
from dept_emp
where emp_no between 100001 and 100003;
select group_concat(distinct dept_no order by emp_no desc)
from dept_emp
where emp_no between 100001 and 100003;
연결
을 위해 제한적인 메모리 버퍼 공간
사용
select emp_no, first_name,
case gender when 'M' then 'Man'
when 'F' then 'Woman'
else 'Unkonw' end as gender
from employees
limit 10;
select emp_no, first_name,
case when hire_date<'1995-01-01' then 'Old'
else 'New' end as employee_type
from employees
limit 10;
case when
절이 일치하는 경우에만 then
이하 표현식 실행됨
명시적
으로 타입 변환
cast(변환할 값 as 데이터 타입)
select cast('1234' as signed integer) as converted_integer;
convert()
함수는 cast()함수
와 같이 타입 변환
하는 용도와, 문자열
의 문자 집합 변환
용도로 사용
select convert(1-2, unsigned);
select convert('ABC' using 'utf8mb4');
HEX()
: 이진값
을 16진수 문자열
로 변환
UNHEX()
: 16진수 문자열
을 이진값
으로 변환
MD5
와 SHA
모두 비대칭형 암호화 알고리즘
으로, 해시 값
을 만들어내는 함수
SHA()
는 SHA-1 암호화 알고리즘
사용, MD5
는 메시지 다이제스트 알고리즘
사용
MD5
는 입력된 문자열
의 길이를 줄이는 용도
로 사용
select sha2('abc',256);
디버깅 용도로 잠깐 대기
하거나 일부러 쿼리 실행을 오랜 시간 유지
하고자 할 때 사용
select sleep(1.5)
from employees
where emp_no between 10001 and 10010;
sleep() 함수
는 레코드 건수만큼
sleep 함수를 호출
하기 때문에 10*1.5초로 15초동안 쿼리 실행
BENCHMARK()
: 디버깅
이나 간단한 함수의 성능 테스트용
으로 유용
benchmark(반복 수행 횟수, 표현식(스칼라값))
스칼라값
: 하나의 칼럼
을 가진 하나의 레코드
select benchmark(10000000,md5('abcdefghijk'));
IPv4 주소
를 문자열이 아닌 부호 없는 정수 타입
에 저장할 수 있도록 함
INET_ATON()
: 문자열
로 구성된 주소를 정수형
으로 변환
INET_NTOA()
: 정수형
의 주소를 .
으로 구분된 문자열
로 변환
INET6_ATON()
: IP주소를 binary 타입
으로 변환
INET6_NTOA()
: binary타입
의 주소를 문자열
로 변환
select hex(inet6_aton('10.0.5.9'));
기본적으로 JSON 데이터
표시 방법이 단순 텍스트 포맷
이라 가독성이 떨어지는 것을 변환
select doc from employee_docs where emp_no=10005;
select json_pretty(doc) from employee_docs where emp_no=10005;
JSON
은 텍스트 기반이라 실제 디스크에 저장할 때 BSON(Binary JSON)
포맷을 사용한다.
=> 저장 공간 크기
예측을 위한 함수
select emp_no, json_storage_size(doc) from employee_docs limit 2;
특정 필드의 값
을 가져오는 함수
json_extract(칼럼 또는 json 도큐먼트 자체, 필드 json 경로);
select emp_no, json_extract(doc, "$.first_name") from employee_docs;
JSON_UNQUOTE()
: 따옴표 없이
값만 가져옴
->
: JSON_EXTRACT()
와 동일한 기능
->>
: JSON_UNQUOTE()
와 JSON_EXTRACT()
조합
JSON 도큐먼트
또는 지정된 JSON 경로
에 JSON 필드
를 가지고 있는지를 확인
select emp_no
from employee_docs
where JSON_CONTAINS(doc, '{"first_name":"Christian"}');
첫번째 인자로 주어진 도큐먼트
에서 두 번째 인자의 json 오브젝트
가 존재하는지
검사
세번째 인자
는 선택
으로, json 경로
명시시 해당 경로에 오브젝트
존재하는지 여부 체크
칼럼 값
을 이용해 json 오브젝트
생성
select json_object("empNo", emp_no,
"salary", salary,
"fromDate",from_date,
"toDate",to_date) as as_json
from salaries limit 3;
group by
절과 함께 사용되는 집계 함수
로서, json 배열
또는 도큐먼트
생성 함수
select dept_no, json_objectagg(emp_no, from_date) as agg
from dept_manager
where dept_no in ('d001', 'd002', 'd003')
group by dept_no;
select dept_no, json_arrayagg(emp_no) as agg
from dept_manager
where dept_no in ('d001', 'd002', 'd003')
group by dept_no;
json_objectagg()
함수는 2개의 인자
가 필요하며, 첫 번째 인자는 키
, 두 번째 인자는 값
으로 사용해 키-밸류 쌍
으로 반환
json 데이터 값
들을 모아서 rdbms 테이블
을 만들어 반환
=> 반환 레코드 건수는 원본 테이블
과 동일
select e2.emp_no, e2.first_name, e2.gender
from employee_docs e1,
JSON_TABLE(doc, "$" COLUMNS (emp_no int PATH "$.emp_no",
gender char(1) PATH "$.gender",
first_name varchar(20) PATH "$.first_name") as e2
where e1.emp_no in (10001, 10002);
emp_no | first_name | gender |
---|---|---|
10001 | Georgi | M |
10002 | Bezalel | F |
order by
가 사용됐을 때 첫 번째 테이블
만 읽어서 정렬
수행 후, 나머지 테이블
을 읽을 때 나타남
인덱스된 칼럼의 값 자체를 변환하지 않고
그대로 사용
범위 비교 조건
이 있다면, 그 다음 조건은 체크 조건
으로 사용됨
or 조건
으로 연결된 SQL문은 풀 테이블 스캔
을 진행
=> 풀 테이블 스캔+인덱스 레인지 스캔
보다는 풀 테이블 스캔
한 번이 더 빠르기 때문
group by절
에 명시된 칼럼의 순서
가 인덱스
구성 칼럼 순서
와 같으면 인덱스 이용 가능인덱스
구성 칼럼 중 뒤쪽
에 있는 칼럼은 명시되지 않아도 인덱스 사용 가능하나, 앞쪽
칼럼은 꼭 있어야 함group by절
에 명시된 칼럼이 하나라도 인덱스에 없으면
전혀 인덱스 사용 불가조건절에 col_1
이나 col_2
가 동등 비교 조건
으로 사용된다면 group by
절에 col_1
이나 col_2
빠져도 인덱스
사용 가능할 때 있음
(col_2
가 빠지려면 col_1
도 where절
에 있어야 함)
order by절
의 모든 칼럼
이 오름차순
이거나 내림차순
일 때만 인덱스 사용 가능
order by절
의 칼럼들이 인덱스에 정의된 칼럼
의 왼쪽
부터 일치해야 함
where절
과 order by절
이 동시에 같은 인덱스
이용 : 가장 빠른 성능where절만 인덱스
사용 : where절
의 조건에 일치하는 레코드 건수
가 많지 않을 때order by절만 인덱스
사용 : 아주 많은 레코드를 조회해서 정렬할 경우where절
에서 동등 비교 조건
으로 비교된 칼럼과 order by 절
에 명시된 칼럼이 순서대로 왼쪽
부터 일치
해야 함
모두 하나의 인덱스를 사용하려면 group by절
에 명시된 칼럼
과 order by절
에 명시된 칼럼
의 순서
와 내용
이 모두 같아야
함
둘 중 하나라도 인덱스
를 이용할 수 없을 땐 둘 다 인덱스 이용 불가
null 값
이 포함된 레코드도 인덱스
로 관리
null 비교
하려면 is null(또는 <=>)
연산자 사용
explain
select * from titles
where to_date is null;
ix_todate 인덱스
를 ref 방식
으로 이용
문자열 칼럼
이나 숫자 칼럼
을 비교할 땐 반드시 그 타입
에 맞는 상숫값 사용 권장
DATE 타입
: 날짜만
저장
DATETIME
과 TIMESTAMP
: 날짜
와 시간
저장
TIME
: 시간만
저장
문자열 값
을 자동으로 datetime 타입
의 값으로 변환
해서 비교 수행
select count(*)
from employees
where hire_date>'2011-07-23';
date()
: datetime 타입
의 값에서 시간 부분
은 버리고
날짜 부분만 반환
select count(*)
from employees
where hire_date>DATE(NOW());
만약 datetime
을 date
로 만들지 않는다면 date
타입의 값을 서버가 datetime
으로 변환해서 같은 타입으로 만든 다음 비교 수행
인덱스 사용 여부에 영향 X
칼럼이 datetime 타입
이라면 from_unixtime()
함수를 이용해 timestamp 값
을 datetime
으로 바꿔야 함
timestamp
타입 칼럼이라면 unix_timestamp()
함수를 이용해 datetime
을 timestamp
로 변환해서 비교해야 함
boolean in_trainsaction;
if(in_transaction && has_modified()){
commit();
}
Short-circuit Evaluation
: 여러개의 표현식이 and
또는 or
논리 연산자로 연결된 경우 선행 표현식의 결과
에 따라 후행 표현식
을 평가할지 말지 결정하는 최적화
select count(*) from salaries;
select count(*) from salaries
where convert_tz(from_date,'+00:00','+09:00')>'1991-01-01';
select count(*) from salaries
where to_date<'1985-01-01';
select count(*) from salaries
where convert_tz(from_date,'+00:00','+09:00')>'1991-01-01'
and to_date<'1985-01-01';
select count(*) from salaries
where to_date<'1985-01-01'
and convert_tz(from_date,'+00:00','+09:00')>'1991-01-01';
유니크한 값
의 조회를 위해 사용
LIMIT
: 쿼리 결과에서 지정된 순서에 위치
한 레코드만 가져오고자 할 때 사용
select * from employees
where emp_no between 10001 and 10010
order by first_name
limit 0, 5;
LIMIT
은 where 조건
이 아니기 때문에 항상 쿼리 마지막
에 실행
LIMIT
에 필요한 레코드 건수만 준비되면 즉시 쿼리 종료
group by
와 함께 limit
사용시 group by
가 끝난 후 limit 수행
가능하므로 실질적으로 작업 내용을 크게 줄여주진 못함
select distinct first_name
from employees
limit 0,10;
풀 테이블 스캔
과 동시에 중복 제거 작업(임시 테이블 사용)
을 진행
limit n; --상위 n개
limit n, m; -- n부터 m개
limit 10, 10;
--- 11번째 부터 10개
limit 제한사항
표현식
이나 서브쿼리
사용 불가결과 레코드의 건수
반환
count() 함수
에 칼럼명
이나 표현식
이 인자로 사용되면 null이 아닌 레코드 건수
만 반환
드라이빙 테이블
을 읽을 땐 인덱스 탐색 작업
을 단 한번만 수행하고, 그 이후부턴 스캔
드리븐 테이블
에선 인덱스 탐색 작업
과 스캔 작업
을 드라이빙 테이블
에서 읽은 레코드 건수
만큼 반복
select *
from employees e, dept_emp de
where e.emp_no=de.emp_no;
옵티마이저
는 조인 칼럼
에 인덱스
가 있는 것을 드리븐 테이블
로 선택하며, 둘 다 있을 경우 레코드 건수
에 따라 최적으로 선택
두 칼럼 모두 인덱스가 없는 경우
는 해시 조인
으로 처리되며, 레코드 건수가 적은 테이블
을 드라이빙 테이블
로 선택
조인 칼럼 간
의 비교에 각 칼럼의 데이터 타입
이 일치하지 않으면 인덱스 효율적으로 이용 불가
조인 시 문제가 되는 경우
char 타입
과 int 타입
의 비교처럼 데이터 타입의 종류
가 완전히 다른 경우문자 집합
이나 콜레이션
이 다른 경우부호 존재 여부
가 다른 경우테이블의 데이터가 일관되지 않은 경우
에만 아우터 조인
이 필요
MySQL은 where절
에 안티 조인 효과
를 기대하는 경우를 제외하곤 left join
을 inner join
으로 자동변환
데이터 무결성
을 보장하기 위해 외래키
를 생성
지연된 조인
: 조인이 실행되기 이전에 group by
나 order by
를 처리하는 방식
=> limit
이 함께 사용돼야 더 큰 효과
explain
select e.*
from (select s.emp_no
from salaries s
where s.emp_no between 10001 and 13000
group by s.emp_no
order by sum(s.salary) desc
limit 10) x,
employees e
where e.emp_no=x.emp_no;
특정 그룹
별로 서브쿼리
를 실행해 그 결과와 조인
하는 것이 가능
=> 서브쿼리
에서 외부 쿼리
의 테이블 칼럼 참조 가능
LATERAL 키워드
를 가진 서브쿼리는 조인 순서
상 후순위
로 밀림
NL 조인
대신 해시 조인
이 사용되면 쿼리 결과의 레코드 정렬 순서가 달라짐
rollup
: group by
와 함께 사용하며 그룹별
로 소계
select dept_no, count(*)
from dept_emp
group by dept_no with rollup;
select first_name, last_name, count(*)
from employees
group by first_name, last_name with rollup;
GROUPING() 함수
: 그룹 레코드
에 표시되는 null
을 사용자가 변경할 수 있도록 함
select if(grouping(first_name), 'All first_name', first_name) as first_name,
if(grouping(last_name), 'All last_name', last_name) as last_name,
count(*)
from employees
group by first_name, last_name with rollup;
피봇(Pivot)
은 sum()
이나 count()
와 case when..end
를 이용해 구현
-- 부서별 사원 수 확인
select dept_no, count(*)
from dept_emp
group by dept_no;
-- 레코드를 칼럼으로 변환
select
sum(case when dept_no='d001' then emp_count else 0 end) as count_d001,
sum(case when dept_no='d002' then emp_count else 0 end) as count_d002,
sum(case when dept_no='d003' then emp_count else 0 end) as count_d003,
sum(case when dept_no='d004' then emp_count else 0 end) as count_d004,
sum(case when dept_no='d005' then emp_count else 0 end) as count_d005,
sum(case when dept_no='d006' then emp_count else 0 end) as count_d006,
sum(case when dept_no='d007' then emp_count else 0 end) as count_d007,
sum(case when dept_no='d008' then emp_count else 0 end) as count_d008,
sum(case when dept_no='d009' then emp_count else 0 end) as count_d009,
sum(emp_count) as count_total
from (
select dept_no, count(*) as emp_count from dept_emp group by dept_no
) tb_derived;
-- 전체 사원수와 입사 연도별 사원 수 구함
select de.dept_no,
sum(case when e.hire_date between '1980-01-01' and '1989-12-31' then 1 else 0 end) as cnt_1980,
sum(case when e.hire_date between '1990-01-01' and '1999-12-31' then 1 else 0 end) as cnt_1990,
sum(case when e.hire_date between '2000-01-01' and '2009-12-31' then 1 else 0 end) as cnt_2000,
count(*) as cnt_total
from dept_emp de, employees e
where e.emp_no=de.emp_no
group by de.dept_no;
어떤 순서로 정렬
할지 결정
1개
또는 그 이상 여러 개 칼럼
으로 정렬
수행 가능
각 칼럼의 정렬 순서
가 오름차순
과 내림차순
이 혼용되면 인덱스 사용 불가
=> 인덱스 생성 자체
를 혼용
해서 생성
함수 기반 인덱스
를 이용해 정렬
가능
인덱스
를 사용할 수 있다면 크게 주의할 사항 없음
레코드
는 무조건 1건
존재
서브쿼리 결과
가 0건
이면, 서브쿼리 결과는 null로 채워져서 반환
select emp_no, (select dept_name from departments where dept_name='Sales1')
from dept_emp limit 10;
래터럴 조인
을 사용하면, 인덱스
를 통해 정렬
된 결과를 가져올 수 있음에도, Using filesort
가 실행되는 버그 있음
서브쿼리 결과
를 임시 테이블
로 저장하고, 필요할 때 다시 임시 테이블을 읽음
=> from 절
의 서브쿼리를 외부 쿼리
로 병합
하는 최적화
explain
select *
from (select * from employees) y;
show warnings \g
from 절
의 서브쿼리
가 외부 쿼리
로 병합되지 못하는 경우
order by 절
을 가진 경우, 외부 쿼리가 group by
나 distinct
를 사용하지 않는다면, 서브쿼리 정렬 조건
을 외부 쿼리
로 병합
이전까진 서브쿼리 외부 조건
으로 쿼리 실행을 하고, 최종적
으로 서브쿼리
를 체크 조건
으로 사용
=> 성능 저하 심각
explain
select * from dept_emp de
where de.emp_no=(select e.emp_no
from employees e
where e.first_name='Georgi' and e.last_name='Facello' limit 1);
5.5 이전까진 dept_emp
테이블을 풀 스캔
하면서 서브쿼리 조건
에 일치하는지 여부
를 체크
5.5 부터는 서브쿼리
를 먼저 실행한 후 상수
로 변환해, 상숫값을 서브쿼리로 대체
해서 처리
단일 값 비교
가 아닌 튜플 비교 방식
을 사용하면, 서브쿼리
가 먼저 처리되어 상수화
되긴 하지만 외부 쿼리
는 인덱스 사용 불가
=> ex. ~~where (emp_no, from_date)= (select emp_no, from_date ~~);
세미 조인
: where 절
에 사용된 in (subquery)
형태의 조건
안티 세미 조인
: not in
형태의 조건
=> 인덱스
제대로 활용 불가
이름
을 가지는 임시 테이블
로, SQL 문장 종료시 자동으로 CTE 임시 테이블
은 삭제
with cte1 as (select * from departments)
select * from cte1;
cte 쿼리
는 with절
로 정의
cte
로 생성된 임시 테이블은 다른 cte 쿼리
에서 참조 가능
with recursive cte(no) as (
select 1 -- 비 재귀적 파트
union all
select (no+1) from cte where no<5 --재귀적 파트
)
select * from cte;
비 재귀적 쿼리 파트
와 재귀적 쿼리 파트
로 구분
=> 이 둘을 union
또는 union all
로 연결하는 형태로 반드시 작성
cte 테이블
의 모든 레코드를 조회하는 것이 아니라, 직전 단계
에서 만들어진 결과만을 참조
재귀 쿼리
가 반복을 멈추는 조건
은 재귀 파트 쿼리의 결과
가 0건일 때까지
-- CTE 임시 테이블의 칼럼명 변경
with cte1(fd1, fd2, fd3) as (select * from departments)
select * from cte1;
# Adil(id=123)의 상위 조직장 찾는 쿼리
with recursive managers as
(select *, 1 as lv from employees where id=123
union all
select e.*, lv+1 from managers m
inner join employees e on e.id=m.manager_id and m.manager_id is not null
)
select * from managers
order by lv desc;
조회하는 현재 레코드
를 기준으로 연관된 레코드 집합의 연산 수행
=> 레코드 건수
는 변하지 않음
이 순서를 벗어나는 쿼리 작성하고자 한다면 from 절
의 서브쿼리
사용해야 함
aggregate_func() over (<partition> <order>) as window_func_column
-- 부서별 입사 순위
select de.dept_no, e.emp_no, e.first_name, e.hire_date,
rank() over(partition by de.dept_no order by e.hire_date) as hire_date_rank
from employees e inner join dept_emp de on de.emp_no=e.emp_no
order by de.dept_no, e.hire_date;
aggregate_func() over(<partition> <order> <frame>) as window_func_column
-- 프레임 : 연산 범위 제한하는 역할
frame:
{ ROWS|RANGE } {frame_start|frame_between}
#ROWS : 레코드의 위치를 기준으로 프레임을 생성
#RANGE : ORDER BY 절에 명시된 칼럼을 기준으로 값의 범위로 프레임 생성
current row
: 현재 레코드unbounded preceding
: 파티션의 첫
번째 레코드unbounded following
: 파티션의 마지막
레코드expr preceding
: 현재 레코드로부터 n번째 이전
레코드expr following
: 현재 레코드로부터 n번째 이후
레코드rows
로 구분되면 expr
에는 레코드 위치 명시
range
로 구분되면 expr
에는 칼럼과 비교할 값
이 설정돼야 함
=> 10 preceding
: 현재 레코드로부터 10건 이전부터
=> interval 5 day preceding
: 현재 레코드의 칼럼값보다 5일 이전 레코드부터
select emp_no, from_date, salary,
min(salary) over(order by from_date range interval 1 year preceding) as min_1
from salaries
where emp_no=10001;
order by 사용 시
: range between unbounded preceding and current row
order by 미 사용 시
: range between unbounded preceding and unbounded following
자동으로 프레임이 파티션의 전체 레코드로 설정
cume_dist()
dense_rank()
lag()
lead()
ntile()
percent_rank()
rank()
row_number()
집계 함수
는 over() 절
없이 단독으로 사용 가능
비 집계 함수
는 반드시 over() 절
을 가지고 있어야 함
집계 함수 | 설명 |
---|---|
avg() | 평균 값 반환 |
bit_and() | and 비트 연산 결과 반환 |
bit_or() | or 비트 연산 결과 반환 |
bit_xor() | xor 비트 연산 결과 반환 |
count() | 건수 반환 |
json_arrayagg() | 결과를 json 배열로 반환 |
json_objectagg() | 결과를 json object 배열로 반환 |
비 집계 함수 | 설명 |
---|---|
cume_dist() | 누적 분포 값 반환 |
dense_rank() | 랭킹 값 반환(동일값 동일 순위) |
first_value() | 파티션의 첫 번째 레코드 값 |
lag() | n번째 이전 레코드 값 |
last_value() | 파티션의 마지막 레코드 값 |
lead() | n번째 이후 레코드 값 |
nth_value() | n번째 값 |
ntile() | 전체 건수를 n 등분 |
percent_rank() | 퍼센트 랭킹 값 |
rank() | 랭킹 값 |
row_number() | 레코드 순번 |
rank()
: 동점인 레코드가 두 건 이상인 경우, 그 다음 레코드를 동점 레코드 수만큼 증가시킨 순위 반환
dense_rank()
: 동점인 레코드를 1건
으로 가정
row_number()
: 정렬된 순서대로 레코드 번호 부여
lag()
: n번째 이전
레코드 반환
lead()
: n번째 이후
레코드 반환
select from_date, salary,
lag(salary,5) over (order by from_date) as prior,
lead(salary,5,-1) over (order by from_date) as next
from salaries
where emp_no=10001;
윈도우 함수
는 인덱스
를 전혀 활용하지 못해 테이블 풀 스캔
for share
: select 쿼리로 읽은 레코드에 대해 읽기 잠금
=> 다른 세션에서 해당 레코드 변경 불가(읽기 가능)
for update
: select 쿼리로 읽은 레코드에 대해 쓰기 잠금
=> 다른 세션에서 해당 레코드 변경 및 읽기 불가(for share절 사용하는 select)
select * from employees where emp_no=10001 for share;
select * from employees where emp_no=10001 for update;
두 옵션 모두 자동 커밋
이 비활성화
된 상태 또는 begin
명령이나 start transaction
명령으로 트랜잭션이 시작된 상태에서만 잠금 유지
for share
이나 for update
가 없는 단순한 select
는 대기 없이 실행
select *
from employees e
inner join dept_emp de on de.emp_no=e.emp_no
inner join departments d on d.dept_no=de.dept_no
for update;
innodb
는 3개 테이블
에서 읽은 레코드에 대해 모두 쓰기 잠금
을 건다
of 테이블
절을 추가해 해당 테이블에 대해서만 잠금
걸 수 있음
select *
from employees e
inner join dept_emp de on de.emp_no=e.emp_no
inner join departments d on d.dept_no=de.dept_no
for update of e;
nowait
: 이미 레코드가 잠겨진 상태라면 무시
하고 즉시 에러
반환
skip locked
: 다른 트랜잭션에 의해 잠겨진 상태라면, 잠긴 레코드는 무시
하고 잠금이 걸리지 않은 레코드만
가져옴
insert ignore 옵션
과 insert ... on duplicate key update 옵션
모두 유니크 인덱스
나 프라이머리 키
에 대해 중복 레코드
를 어떻게 처리할지 결정
=> ignore 옵션
은 추가로 에러 핸들링
기능도 포함
저장하는 레코드의 프라이머리 키
나 유니크 인덱스 칼럼 값
이 이미 테이블에 존재
하는 레코드와 중복
되는 경우, 저장하는 레코드의 칼럼이 테이블의 칼럼과 호환되지 않는 경우
모두 무시
하고 다음 레코드 처리
insert ignore into salaries
select emp_no, (salary+100), '2020-01-01','2022-01-01'
from salaries where to_date>='2020-01-01';
데이터 타입
이 일치하지 않을 경우, 칼럼의 기본 값
으로 insert
=> 만약 null
입력시, 숫자 칼럼 기본 값인 0
을 대신 insert
프라이머리 키
나 유니크 인덱스
의 중복
발생시 update 문장
역할 수행
=> 중복된 레코드 존재시, 기존 레코드 삭제하지 않고 update
하는 방식
=> replace 문장
: delete
와 insert
의 조합
insert into daily_statistics (target_date, stat_name, stat_value)
values (date(now()), 'visit', 1)
on duplicate key update stat_value=stat_value+1;
count(*)
와 같은 group by 결과
참조 불가능
=> values() 함수
사용
values() 함수
: 실제 저장하려고 했던 값이 무엇인진 몰라도, 칼럼에 insert하고자 했던 값 가져옴
on duplicate key update stat_value=stat_value+values(stat_value);
-- stat_value 칼럼에 insert 하려고 했던 값 다시 가져옴
# 8.0.20 이후 지원X 예정
#인라인 뷰 사용 또는
insert ...
from ()
on duplicate~~
#별칭 부여
insert ~~ as new
on duplicate~~ +new.stat_value;
데이터를 빠르게 적재
=> insert 명령
과 비교했을 때 매우 빠름
단점
단일 스레드
로 실행단일 트랜잭션
으로 실행데이터 파일을 여러 개
로 준비해서 load data 문장
을 실행하거나, insert .. select 문장
으로 적재하는 것이 좋음
LOAD DATA
[LOW_PRIORITY | CONCURRENT] [LOCAL]
INFILE 'file_name'
[REPLACE | IGNORE]
INTO TABLE tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[CHARACTER SET charset_name]
[{FIELDS | COLUMNS}
[TERMINATED BY 'string']
[[OPTIONALLY] ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES
[STARTING BY 'string']
[TERMINATED BY 'string']
]
[IGNORE number {LINES | ROWS}]
[(col_name_or_user_var
[, col_name_or_user_var] ...)]
[SET col_name={expr | DEFAULT}
[, col_name={expr | DEFAULT}] ...]
insert 문장 성능
은 쿼리 문장 자체보다 테이블 구조
에 의해 많이 결정
하나의 insert 문장으로 여러 레코드를 insert한다면, insert될 레코드들을 프라이머리 키
값 기준으로 미리 정렬
해서 insert
문장 구성
프라이머리 키 선정
은 select 성능
과 insert 성능
의 대립되는 두 가지 요소 중에 하나를 선택
select
보다는 insert
에 최적화된 테이블 생성
단조 증가
또는 단조 감소
되는 값으로 프라이머리 키
선정세컨더리 인덱스 최소화
자동 증가(Auto Increment)
칼럼을 이용하면 클러스터링되지 않는 테이블
의 효과 얻음
create table(
id bigint not null auto_increment,
...
primary key(id)
);
자동 증가 값
을 프라이머리 키
로 해서 테이블 생성하면, 가장 빠른 Insert
보장
자동 증가 값
의 채번
을 위해 잠금
필요
=> auto-inc 잠금
last_insert_id()
: 현재 커넥션
에서 가장 마지막에 증가된 auto_increment
값
MySQL에는 여러 테이블
을 조인
해서 한 개 이상 테이블의 레코드를 변경
하거나 삭제
가능
MySQL에서는 order by
절과 limit
절을 동시에 사용해 특정 칼럼
으로 정렬해서, 상위 몇 건만 변경 및 삭제 가능
delete from employees order by last_name limit 10;
두 개 이상의 테이블을 조인
해 조인된 결과 레코드를 변경 및 삭제
하는 쿼리
=> 조인된 테이블 중 특정 테이블의 칼럼 값
을 다른 테이블의 칼럼
에 업데이트 시
update tb_test1 t1, employees e
set t1.first_name=e.first_name #employees 테이블의 칼럼 값을 t1 테이블에 복사
where e.emp_no=t1.emp_no;
join update
문에서는 group by
나 order by
사용 불가
=> 서브쿼리
로 그룹핑 후 조인하기
straight_join
키워드 : 조인 키워드
로 사용되기도 함
# 레코드 별로 서로 다른 값 업데이트
update user_lvel ul
inner join (values row(1,1), row(2,4))
new_user_level(user_id, user_lv) #두 건의 레코드를 가지는 임시 테이블 생성
on new_user_level.user_id=ul.user_id
set ul.user_lv=ul.user_lv+new_user_level.user_lv;
join delete
문장에는 삭제할 테이블
명시해야 함
delete e #employees 테이블 레코드만 삭제
from employees e, dept_emp de, departments d
where e.emp_no=de.emp_no and de.dept_no=d.dept_no and d.dept_no='d001';
DBMS 서버의 모든 오브젝트
를 생성
하거나 변경
하는 쿼리
이전까진 테이블 구조 변경
하는 동안 다른 커넥션
에서 DML 실행 불가
=> 8.0 이후 MySQL 서버에 내장된 온라인 DDL 기능
으로 처리 가능
온라인 DDL
: 스키마를 변경
하는 작업 도중에도 다른 커넥션
에서 해당 테이블의 데이터를 변경
하거나 조회
하는 작업 가능하게 해줌
=> algorithm
과 lock
옵션을 이용
스키마 변경에 적합한 알고리즘 찾는 순서
algorithm=instant
로 스키마 변경 가능한지 확인 후, 가능하면 선택algorithm=inplace
로 스키마 변경 가능한지 확인 후, 가능하면 선택algorithm=copy
알고리즘 선택스키마 변경 알고리즘
의 우선순위
가 낮을수록, 스키마 변경을 위해 더 큰 잠금
과 많은 작업
을 필요로 함
instant
: 테이블의 데이터는 전혀 변경하지 않고, 메타데이터
만 변경하고 작업 완료inplace
: 임시 테이블로 데이터를 복사하지 않고
스키마 변경 실행copy
: 변경된 스키마를 적용한 임시 테이블
을 생성하고, 테이블의 레코드를 모두 임시 테이블
로 복사
한 후 임시 테이블 rename
해서 스키마 변경 완료Lock 옵션
none
: 아무런 잠금을 걸지 않음shared
: 읽기 잠금
을 걸고 스키마 변경 실행쓰기
는 불가함exclusive
: 쓰기 잠금
을 걸고 스키마 변경inplace 알고리즘
을 사용하면, 내부적으로 테이블 리빌드
가 필요할 수 있음
=> 프라이머리 키
를 추가하는 경우, 데이터 파일
에서 레코드의 저장 위치
가 바뀌어야 하기 때문
=> Data Reorganizing
또는 Table Rebuild
모든 스키마 변경 작업이 온라인
으로 가능한 것이 아니므로, 온라인
으로 처리될 수 있는지 아니면 테이블의 읽고 쓰기
가 대기
하게 되는지 확인
https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html
임시 테이블
로 레코드를 복사하진 않더라도 내부적
으로 테이블의 모든 레코드를 리빌드
해야 하는 경우 많음
inplace 스키마 변경
이 지원되는 스토리지 엔진의 테이블인지 확인inplace 스키마 변경
준비변경
및 새로운 DML 로깅
로그 적용
( DML 로그를 테이블에 적용)commit
)inplace 알고리즘
으로 온라인 스키마 변경이 진행되는 동안 새로 유입된 DML 쿼리
들에 의해 변경되는 데이터를 온라인 변경 로그
에 쌓아두었다가 실제 테이블로 일괄 적용
DML
이 많이 실행되는 경우, 또는온라인 변경 로그
공간이 부족한 경우테이블 구조
에서는 아무런 문제가 안되지만 alter table
이후 테이블 구조
에는 적합하지 않은 레코드가 insert
또는 update
시낮은 잠금 옵션
이 사용된 경우lock=none
으로 실행되더라도, 변경 작업
의 처음
과 마지막
에 잠금
필요한데 획득하지 못하고 타임 아웃
발생시인덱스 생성
시 정렬을 위해 tmpdir
에 설정된 디스크 임시 디렉터리 사용하는데, 이 공간
이 부족한 경우performance_schema
를 통해 진행 상황
모니터링
set global performance_schema=on; # 서버 재시작 필요
#Instrument 활성화
update performance_schema.setup_instruments
set enabled='YES', timed='YES'
where name like 'stage/innodb/alter%';
#Consumer 활성화
update performance_schema.setup_consumers
set enabled='YES'
where name like '%stages%';
스키마 변경 작업의 진행 상황
은 performance_schema.events_stages_current
테이블을 통해 확인
select event_name, work_completed, work_estimated
from performance_schema.events_stages_current;
온라인 DDL
은 단계
별로 event_name
칼럼 값이 달라져 여러개 보임
EVENT_NAME | WORK_COMPLETED | WORK_ESTIMATED |
---|---|---|
~~~(read PK and internal sort) | 9776 | 25281 |
work_estimated
는 예측치
work_completed
는 현재까지 완료된 정도
MySQL 서버는 스키마
와 데이터베이스
가 동격
의 개념이며, 하나의 인스턴스는 1개 이상의 데이터베이스
를 가질 수 있음
create database [if not exists] employees
character set utf8mb4 collate utf8mb4_general_ci;
show databases;
use employees;
#데이터베이스 생성 시 지정한 문자 집합 또는 콜레이션 변경
alter database employees character set=euckr;
drop database [if exists] employees;
InnoDB 스토리지 엔진의 시스템 테이블 스페이스(ibdata1)
만 제너럴 테이블스페이스
사용
=> 제너럴 테이블스페이스
: 여러 테이블의 데이터를 한꺼번에 저장
하는 테이블스페이스
제너럴 테이블스페이스 제약사항
파티션 테이블
은 제너럴 테이블스페이스 사용 불가복제 소스
와 레플리카 서버
가 동일 호스트
에서 실행되는 경우 add datafile 문장
사용 불가테이블 암호화
는 테이블스페이스 단위로 설정테이블 압축 가능 여부
는 테이블스페이스 블록 사이즈와 InnoDB 페이지 사이즈에 의해 결정삭제
해도 디스크 공간
이 운영체제로 반납 X제너럴 테이블스페이스 장점
파일 핸들러
최소화테이블스페이스 관리
에 필요한 메모리 공간
최소화=> 테이블 개수
가 많은
경우 유용
기본 설정은 자동으로 개별 테이블스페이스 사용
create [temporary] table t (
칼럼명 칼럼타입 [타입별 옵션] [NULL 여부] [기본값]
#ex. member_point int [not null] [default 0]
)engine=INNODB;
temporary
키워드 : 해당 데이터베이스 커넥션(세션)
에서만 사용 가능한 임시 테이블
생성
# 테이블의 create table 문장 표시
show create table 테이블명 \G
desc 테이블명;
desc
명령은 인덱스 칼럼 순서
나 외래키
, 테이블 자체의 속성을 보여주진 않음
alter table 명령
사용
=> 테이블 자체 속성 변경
및 인덱스 추가, 삭제
등
테이블 자체에 대한 속성 변경
=> 테이블의 문자 집합
이나 스토리지 엔진
, 파티션 구조
등 변경
alter table employees
convert to cahracter set utf8mb4 ~~;
#기본 문자 집합 및 콜레이션 변경
alter table employees engine=innodb,
algorithm=inplace, lock=none;
# 테이블의 데이터를 복사하는 작업 실행
# 테이블 리빌드 작업을 위해 사용하기도 함
# 테이블 리빌드 : 레코드 삭제가 발생하는 테이블에서 빈 공간 제거하는 역할
rename table
명령
rename table table1 to table2;
rename table db1.table1 to db2.table2;
# 다른 데이터베이스로 테이블 이동 가능
show table status like 테이블명 \G
information_schema
데이터베이스에는 데이터베이스
와 테이블
에 대한 메타 정보
를 모아서 메모리에 모아두고 참조
create table .. as select .. limit 0
은 인덱스
가 생성되지 않는다는 단점 존재
create table 테이블명 like 테이블명;
모든 칼럼
과 인덱스
가 같은 테이블 생성
drop table [if exists] table1;
칼럼 추가 작업
은 대부분 inplace 알고리즘
을 사용하는 온라인 DDL
로 처리 가능
테이블의 제일 마지막 칼럼
으로 추가하는 경우 instant 알고리즘
으로 즉시 추가
alter table 테이블명 add column 컬럼명 컬럼타입;
#테이블 중간에 칼럼 추가
alter table employees add column emp_telno varchar(20) after emp_no,
algorithm=inplace, lock=none;
칼럼 삭제
는 항상 테이블 리빌드
를 필요로 하므로 instant 알고리즘
사용 불가
=> inplace 알고리즘
으로만 칼럼 삭제 가능
#column 키워드 삭제 가능
alter table employees drop column emp_telno,
algorithm=inplace, lock=none;
#이름 변경
alter table salaries change to_date end_date date not null;
#타입 변경
alter table salaries modify salary varchar(20);
# 타입 변경 시 copy 알고리즘 필요
alter table 테이블명 add primary key (칼럼명), ...
alter table 테이블명 add unique index 인덱스명 (칼럼명)..
show index from 테이블명;
alter table 테이블명 rename index 인덱스명 to 바꿀인덱스명,
algorithm=inplace, lock=none;
alter table drop index
: 인덱스 삭제
인덱스 가시성
: MySQL 서버가 쿼리 실행을 할 때 해당 인덱스를 사용할 수 있게 할지 말지
를 결정
#특정 인덱스 사용 못하게 함
alter table employees alter index ix_firstname invisible;
alter table 테이블명 drop index 인덱스명
alter table 테이블명 drop primary key, algorithm=copy, lock=shared;
프라이머리 키 삭제
작업은 모든 세컨더리 인덱스
의 리프 노드
에 저장된 프라이머리 키 값을 삭제해야 하므로, 임시 테이블
로 레코드 복사
해서 테이블 재구축
온라인 DDL
이 가능하다면, 개별
로 실행하고 그렇지 않다면 모아서 실행
하는 것이 효율적
alter table 테이블명
add index ~~
add index ~~
algorithm=inplace, lock=none;
사용자 목록
이나 각 클라이언트 사용자가 어떤 쿼리
를 실행하고 있는지 확인
show processlist;
#커넥션 또는 쿼리 강제 종료
kill query id번호; #쿼리 종료
kill id번호; #커넥션 종료
트랜잭션 목록은 information_schema.innodb_trx 테이블
을 통해 확인
select * from infromation_schema.innodb_trx where trx_id=트랜잭션아이디 \G
#어떤 레코드 잠그고 있는지
select * from data_locks \G
MySQL 서버가 가지고 있는 여러 종류의 버퍼
나 캐시
MySQL 서버는 운영체제
의 파일 시스템 관련 기능(시스템 콜
)을 이용해 데이터 파일
을 읽어옴
=> 대부분 운영체제는 한 번 읽은 데이터를 운영체제가 관리하는 별도의 캐시 영역
에 보관
$ sync #캐시나 버퍼 내용을 디스크와 동기화
$ echo 3>/proc/sys/vm/drop_cahces # 캐시 초기화
MySQL 서버에서도 데이터 파일
의 내용을 페이지
단위로 캐시
하는 기능 제공
=> InnoDB의 캐시를 버퍼 풀
=> MyISAM의 캐시를 키 캐시
MySQL 서버가 기동 중인 장비에 웹 서버
나 다른 배치용 프로그램
이 실행된다면, 테스트하려는 쿼리의 성능이 영향을 받게 됨
테스트하려는 쿼리를 번갈아 가면서 6~7번 실행
한 후, 처음 한두번 결과를 버리고 나머지 결과의 평균값
을 기준으로 비교