You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* SQL이 어떤 SQL syntax를 지원하고 있는지 그리고 데이터의 validation을 검사하는 수준과 방법은 어떤것들인지를 나타내는것
* 대표적인 설정값(P.378)
* STRICT_ALL_TALBES … etc
대,소문자의 구분
* 테이블이 디스크의 디렉토리나 파일로 매핑되기 때문에 운영체제에 따라 대소문자의 구분여부가 결정된다.
* 윈도우에 설치되면 대소문자를 구분하지 않지만 유닉스 계열의 운영체제에서는 대소문자를 구분한다.
리터럴(literal) 표기법
* Boolean
* BOOL 이나 BOOLEAN 같은 타입이 있지만 사실은 TINYINT 타입에 대한 동이어일 뿐이다. ( 실제로 테이블을 BOOL로 생성후에 칼럼 타입을 조회해보면 TINYINT 임)
* MySQL에선 FALSE가 0을 의미 TRUE는 1만을 의미한다. (ex> P.385)
MySQL 연산자
* MySQL은 `<=>` 와 같은 null-safe한 연산자를 제공함 (P.385)
* `<>`는 not-equal
* `||` (or 연산자) 는 SQL Mode에 따라 concat의 기능으로 사용될 수 있음. `||` 와 `&&` 의 사용은 자제하는게 가독성에 도움이 될것이다.
* LIKE 연산자는 `%, _` 와같은 와일드 카드 문자가 검색어의 뒤쪽에 있다면 인덱스 레인지 스캔을 사용 할 수있지만 검색어의 앞쪽에 있으면 인덱스 레인지 스캔을 사용하지 못한다. ( 인덱스의 left-most 특성)
* BETWEEN 과 IN
* BETWEEN은 크다, 작다 비교연산을 묶어둔것, IN은 여러개의 동등비교(=) 연산을 묶어둔것
* BETWEEN은 선형으로 인덱스를 검색해야함, IN은 동등비교를 여러번 수행하는 것과 같은 효과 (P.394)
* IN 연산자의 비교값이 상수가 아닌경우는 상당히 느려질 수 있음. (P.397)
* NOT-IN은 부정형 비교이어서 처리 범위를 줄이는 조건으로는 사용 할 수 없기때문에 인덱스 풀 스캔을 한다.
내장 함수
* NOW, SYSDATE
* NOW는 쿼리가 실행되는 시점에 값을 할당 받아서 쿼리의 모든 부분에 같은 값을 사용하게 되지만 SYSDATE는 함수가 호출되는 시점에 값을 받아온다.
* 이때문에 SYSDATE가 사용된 구문은 slave에서 안정적으로 복제가 되지 못함.
* SYSDATE는 꼭 써야할 상황이 아니라면 피하는게 ㅎㅎ;
* GROUP_CONCAT
* 지정한 칼럼들의 값을 연결하기위해 제한적인 메모리 버퍼 공간을 사용한다
* 기본값이 1KB 이기때문에 group_concat을 자주 사용한다면 이 설정값을 늘려주는게 좋음.
* INET_ATON, INET_NATON
* COUNT
* count의 인자 `*`은 레코드 자체를 의미한다. 모든 칼럼을 읽는 형태로 처리하지는 않는다.
* count 만을 가져오는 쿼리라면 orderby 나 left join같은 의미없는것들은 지우도록 하자.
* Count(column1) 이라고 사용하면 column1이 null이 아닌 건수를 가져오므로 nullable한 컬럼에 대해서는 주의하자
SELECT
처리 순서 (P.420)OR연산의 경우 읽어와야할 레코드가 훨 늘어나기때문에
인덱스를 사용하기 위한 기본 규칙 (p.422)
* 인덱스된 칼럼의 값을 변환하지 않고 그대로 사용해야 한다.
* 인덱스는 칼럼의 값을 아무런 변환없이 저장함.
* WHERE 절에 사용되는 비교대상의 값은 데이터 타입이 일치해야한다.
* 데이터 타입이 다르면 데이터를 옵티마이저가 좀 더 처리하기 효율적인 타입으로 형변환을 하기 때문에 인덱스 레인지 스캔을 사용하지 못하고 인덱스 풀 스캔을 사용하게 된다.
WHERE의 INDEX 사용 (p.424)
* 각 조건들이 명시된 순서보다는 인덱스가 걸린 칼럼에대한 조건이 있는지 없는지가 중요.
* OR 연산자를 사용을 조심해야한다.
* OR연산에 사용된 인자들이 인덱스가 없는 인자가 포함되어있다면. 풀 테이블 스캔을 할 수 밖에 없고
* 각 인자들의 인덱스가 존재 한다고해도, index_merge방식으로 실행을 하게된다. 이는 제대로된 인덱스하나를 레인지 스캔하는것 보다는 느리기때문에
GROUP BY의 INDEX 사용(P.426)
* group by 절에 명시된 칼럼이 인덱스 칼럼의 순서와 위치가 같아야한다.
* 인덱스를 구성하는 칼럼중에서 뒷쪽에 있는 칼럼은 group by절에 명시되어있지 않아도 인덱스를 사용할 수 있지만 앞쪽에 있는 칼럼이 group by절에 명시 되지않으면 인덱스를 사용 할 수 없다.
* group by절에 명시된 칼럼이 하나라도 인덱스에 없으면 인덱스를 이용하지 못한다.
ORDER BY의 INDEX 사용(P.426)
* group by와 비슷 하지만 오름차순및 내림차순 옵션이 인덱스와 같거나 또는 정반대의 경우에만 사용가능
* Mysql의 인덱스는 모두 오름차순 정렬이어서 order by절의 모든 칼럼이 오름차순이나 내림 차순일때만 가능.
WHERE 조건과 ORDER BY(OR GROUP BY) 절의 인덱스 사용 (p.428)
* 일반적으로 where과 group by 혹은 order by는 같이 사용하게됨.
* where 절의 비교조건에서 사용하는 칼럼과 정렬 대상칼럼이 모두 하나의 인덱스에 연속해서 포함 되어있을 경우에 where 과 order by절이 같은 인덱스를 사용할 수 있음
* where 절만 인덱스 사용이 가능한 경우 별도의 정렬 과정 (filesort)를 거쳐서 정렬을 수행 where절의 조건에 일치하는 레코드가 많지 않을때 효율적인 방식
* order_by절만 인덱스를 이용하는 경우, order_by절의 순서대로 인덱스를 읽으면서 레코드 한건씩을 where의 조건에 일치하는지 비교함. 아주많은 레코드를 조회해서 정렬할때 이런식으로 튜닝 하기도함.
* where절에서 equal 조건으로 비교된 칼럼과 order_by 절에 명시된 칼럼의 순서가 인덱스 칼럼의 왼쪽부터 일치해야한다. (p.430)
* where 과 group by의 조합도 같은 기준이 적용된다.
Group by 절과 order by 절의 인덱스 사용
* 인덱스를 사용하려면 group by절에 명시된 칼럼과 order by에 명시된 칼럼의 순서와 내용이 모두 같아야 하낟.
* MySQL의 group by는 칼럼에 대한 정렬까지 함께 수행하는것이 기본 작동방식이어서 group by와 order by 칼럼의 내용과 순서가 같다면 order by를 생략해도 무방함.
Where절의 비교조건 사용시 주의 사항(p.433)
* NULL 비교
* MySQL에서는 NULL값이 포함된 레코드도 인덱스로 관리됨.
* SQL 표준에서 NULL은 비교 될 수 없는 값임. 그래서 한쪽이라도 NULL 이면 결과도 NULL임.
* NULL비교를 할때는 가급적 IS NULL 연산자를 사용하자
* 문자열 숫자 비교
* 문자열이나 숫자 칼럼을 비교할때는 반드시 그 타입에 맞추어 상수를 사용할것
날짜 비교 (p.435)
* DATE나 DATETIME의 값과 문자열을 비교할때는 문자열 값을 자동으로 DATETIME타입의 값으로 변환을 해서 비굘르 수행함. 문자열을 명시적으로 변환해줄 필요는 없다.
* 칼럼을 변형해 비교하지 말고 상수를 변경하는 형태로 조건을 사용하는것이 좋음
* TIME_STAMP의 경우는 비교값으로 사용되는 상수 리터럴을 비교 대상 칼럼의 타입에 맞게 변환해서 사용하는것이 좋음.
DISTINCT (p.438)
* group by와 비슷하게 처리되나 distinct는 정렬을 보장하지 않음.
* SELECT DISTINCT …는 SELECT 되는 레코드를 유니크하게 처리하는것이지, 칼럼을 유니크하게 처리하는것이 아니다.
* 칼럼만 유니크하게 조회하는 방법은 없음.
* 집함 함수와 사용된 distinct는 임시테이블을 만들어 낸다. 인덱스된 칼럼에 대한 distinct는 인덱스를 활용해 처리할 수있음.
LIMIT
* LIMIT에서 필요한 레코드 건수만 준비되면 쿼리를 종료 시킨다.
* 정렬이나 grouping 또는 distinct가 없는 쿼리에서 limit을 사용하면 상당히 빨리 끝날 수 있다.
JOIN
* 드리븐 테이블에서는 인덱스 탐색 작업과 스캔작업을 드라이빙 테이블에서 읽은 레코드 수만큼 반복함. 그래서 옵티마이져는 항상 드리븐 테이블을 최적으로 읽을 수 있게 플랜을 수립함
* 예시 : `SELECt * from employees e, dept_emp de where e.emp_no=de.emp_no`
* 두칼럼 모두 각각 인덱스가 있는 경우
* 어떤 테이블을 드라이빙 테이블로 선택하든 드리븐 테이블의 검색작업을 빠르게 처리 할 수 있음, 옵티마이져가 통계 정보를 조회하여 드라이빙 테이블을 선택함.
* employees.emp_no 에만 인덱스가 있는 경우
* dept_emp가 드리븐 테이블이 될경우 employees 건수 만큼 dept_emp을 풀스캔 해야하므로 옵티마이져는 항상 dept_emp 을 드라이빙 테이블로 선택한다. 반대의 경우에도 같음
* 두칼럼 모두 인덱스가 없는 경우
* 어느 테이블을 드라이빙으로 선택해도 풀스캔이 발생하므로, 레코드 건수가 적은 테이블을 드리븐으로 선택한다. 드리븐 테이블을 읽을때 조인 버퍼를 사용함.
* 조인칼럼간의 데이터 타입이 일치하지 않으면 인덱스를 효율적으로 사용 할 수 없다. (p.445)
* CHAR - VARCHAR, INT-BIGINT-SMALLINT, DATE-DATETIME 의 경우에는 타입 불일치 문제가 생기지 않음
* 같은 CHAR 타입이라도 charset 혹은 collation이 다른경우 타입 불일치 문제가 생김 ( mysql5.0 이상 에서는 charset은 같지만 collation이 다른 경우 바교를 하면 에러를 뿜음)
* Signed - Unsigned는 타입 불일치 문제가 생긴다.
* Mysql에서는 조인되는 칼럼에 대한 조건은 전부 on절에 표시 해야한다 그렇지 않으면 InnerJoin같은 방법으로 처리함(p.448)
* count 쿼리를 날릴때 드라이빙 테이블과 드리븐테이블의 관계가 1:1, M:1인 경우, 드리븐 테이블에 조인 조건 이외의 별도 조건이 필요 없는 경우에는 outerJoin이 불필요하다. (p.449)
* ANTI-JOIN을 사용할때에는 조건절에 반드시 NOT NULL인 칼럼을 사용해야한다. 1:1 이거나 M:1일때만 사용 하는것이 좋다, 1:M이라면 NOT_EXIST를 활용한 subquery가 더 효율적
* INNER JOIN과 OUTER JOIN은 실제 가져와야할 쿼리 레코드가 같다면 별 성능차이는 없음.
* MYSQL에서 FULL-OUTER-JOIN은 지원하지 않아서 UNION이나 UNION_ALL을 활용해 구현해야한다. 일반적으로 UNION은 중복제거가 필요하기 때문에 각 쿼리에서 중복을 미리 제거하고 UNION_ALL을 사용한다면 더 빠르다.
* UNION, UNION_ALL 은 모두 임시테이블을 사용하므로 성능이 걱정된다면 mutex 테이블을 사용하자(p.452)
* Mysql 은 ANSI 표준인 INNER-JOIN, LEFT-JOIN 구문을 기본문법으로 제공, 혼용하거나 조건의 순서에 신경써야함(p.454)
JOIN과 FOREIGN KEY
* FORIGN KEY는 조인과는 관계가 없다. 주 목적은 데이터의 무결성을 보장하기 위함임.
Delayed Join (p.455) ~
* JOIN이 실행되기 이전에 GROUP BY 혹은 ORDER BY를 처리하는 방식 주로 LIMIT과 함께 사용된 쿼리에서 효과를 얻는다
* OUTER JOIN의 경우 드라이빙 테이블과 드리븐 테이블이 1:1 혹은 M:1 관계여야 사용가능
* INNER JOIN의 경우 드라이빙 테이블과 드리븐 테이블은 1:1 혹은 M:1 관계임과 동시에 드라이빙 테이블에 있는 모두 드리븐 테이블에 존재 해야한다.
* GROUP_BY나 ORDER_BY 처리가 필요한 레코드의 전체크기를 줄이는 역할도 함.
조인 버퍼 사용으로 인한 정렬의 흐트러짐
* Mysql 에서는 nested-loop 방식의 조인만 지원함. 특성상 드라이빙 테이블의 순서로 결과가 정렬이 된다.
* 그러나 조인 버퍼가 사용되면 드라이빙 테이블과 드리븐 테이블을 읽은 순서와는 무관하게 정렬이 흐트러짐
Group By
* Group Key가 아닌 칼럼은 일반적으로 집합 함수를 감싸서 이용하는것이 일반적 감싸지 않은 값들은 어떤 값을 가져올지 예측할 수 없다(제일 큰값, 중간값, 작은값)
* Group by는 정렬까지 보장하므로 정렬작업때문에 느려지는데 order by null을 활용하면 정렬을 하지 않는다.
* group by절에 순서를 명시하는 문법도 지원함. group by title desc, from_date asc
* GROUP BY WITH ROLLUP
ORDER BY
* ORDER BY를 사용하지 않으면?
* 인덱스를 사용한 SELECT인 경우에는 인덱스의 정렬 순서대로 정렬됨
* MyISAM의 경우 테이블에 저장된 순서(insert된 순서가 아님, 중간에 삭제된값이있으면 삭제된 공간에 채워넣기에)
* InnoDB의 경우 PK의 순서대로
* 임시테이블을 거쳐 처리되면 순서를 알기 어렵다
* 정렬 순서는 칼럼별로 명시가능
* ORDER BY 2 같은 문법도 존재, 이는 두번째 칼럼 기준으로 정렬 한다는 의미
* ORDER BY RAND()는 인덱스를 이용 할 수 없다.
* 여러 칼럼을 조합하여서 정렬할때는 정렬 순서가 오름차순과 내림차순이 혼용되면 인덱스를 이용할 수 없음.
* NULL은 항상 최소값으로 간주하고 정렬함. DESC면 맨 처음 ASC 면 맨 마지막
* ORDER BY절에 인덱스에 명시된 칼럼의 값을 변형하여 정렬을 수행하면 인덱스를 활용한 정렬이 불가능해짐
* MySQL의 인덱스키는 756바이트를 넘을 수 없음
Sub Query
* From 절에 사용되는 서브쿼리나 where 절의 IN구문은 그다지 효율적으로 처리 되지 못함. (5.6 부터 개선됨..)
* 외부쿼리에서 정의된 칼럼을 참조하는 여부에 따라 Correlated subquery , self-contained subquery로 나뉨
* LIMIT절 과 LOAD DATA INFILE의 파일명에서는 사용할 수 없음
* IN연산자와 함께 사용할때는 효율적으로 처리 되지못함, ORDER BY 와 LIMIT을 동시에 사용할 수도 없다
* FROM 절에 사용하는 서브쿼리는 Correlated subquery형태로 사용할 수 없다.
* SELECT 절에 사용된 Sub Query
* 서브쿼리가 적절히 인덱스를 사용할 수 있으면 크게 비효율적으로 처리되지는 않는다
* 항상 칼럼과 레코드가 하나인 결과를 반환해야한다.(p.477)
* (Scalar subquery는 레코드의 칼럼이 각각 하나인 결과를 만들어내는 서브쿼리, scalar subquery 보다 레코드 건수가 많거나 칼럼수가 많은 결과를 만들어내는 subquery 는 row subquery 라고함.)
* 일반적으로 join으로 처리할때가 더 빠름(처리해야할 건수가 많을 수록), join으로 처리할건 join으로 하자
* Where 절에 사용된 Sub Query(p.480)
* self-contained subquery가 where문 안에 있을때는 서브쿼리를 매번 실행시키는 비효율 적인 방법으로 실행됨(p.479)(mysql 5.5 에선 최적화 됨)
* In subquery의 형태는 EXISTS 패턴으로 변형시켜 실행한다.
* NOT-IN절과 함께 사용되는경우, 서브쿼리의 결과가 NULL인지 아닌지에 따라 Mysql이 NOT EXIST로 최적화를 할지 안할지를 판단하는 작업을 수행한다.
* From 절에 사용된 Sub Query
* from 절에 사용되는 sub query는 항상 임시테이블을 사용하므로 제대로 최적화 되지 못하고 비효율적일때가 많다
* 용량이 클 경우 임시 테이블을 디스크에 만듬 더욱 병목을 만들 수 있다.
* join으로 풀자..
집합 연산
* 집합 연산도 모두 임시테이블이 필요한 작업임. 레코드 건수가 많다면 디스크를 사용하기때문에 문제가 될수도
* 집합 연산은 크게 세가지
* Union - 두개의 집합을 하나로 묶는 역할을 함 두 집합에서 중복되는 레코드를 제거할지 말지에 따라 UNION DISTICT와 UNION ALL로 나뉜다.
* INTERSECT는 두 집합의 교집합을 반환
* MINUS는 첫번째 집합에서 두번째 집합을 뺀 차집합의 결과를 반환
* mysql에서는 union만 지원한다.
* UNION
* Union뒤에 아무것도 명시하지 않으면 기본적으로 DISTINCT가 적용됨, UNION ALL은 중복제거 과정을 거치지 않고 반환.
* 중복제거를 위해 UNION(DISTINCT)는 집합의 모든 칼럼을 사용해 UNIQUE인덱스를 사용하는 방식으로 동작함. 성능적으로 큰 차이.. 조심하자
* 교집합을 제거하고 UNION ALL 로 바꾸는것이 좋다, 그게 어렵다면 어플리케이션 단에서 독립적으로 실행시키고 합치는것이 좋다
* INTERSECT
* Inner join 과 같다.
* MINUS
* NOT EXIST 혹은 ANTI-JOIN의 형태로 변환
LOCK IN SHARE MODE , FOR UPDATE
* InnoDB에서는 SELECT 할때 아무런 잠금을 걸지 않는데, 혹여 잠금이 필요할때 사용할 수 있음
* 두 가지다 AUTO-INCREMENT 가 비활성화 된 상태 혹은 BEGIN 이나 START Transaction으로 트랜젝션이 시작된 경우 에만 잠금이 유지된다.
* LOCK IN SHARE MODE는 레코드에 대해 shared lock을 설정하고 다른 세션에서 해당 레코드를 변경하지 못하게 한다. 다른 세션에서 잠금된 레코드를 읽는건 가능
* FOR UPDATE는 Exclusive lock( 베타잠금)을 설정하고 다른 세션에서는 write 뿐만아니라 select도 못하게 한다.
* COMMIT 혹은 ROLLBACK 으로 해제 할 수 있음.
* 잠금경합을 발생시키고 데드락을 유발시킬수도 있으니 주의하자