NULL 은 INDEX 만들어지지 않는다. NULL 이 많은 항목은 INDEX 부적합하다.
INDEX란, 데이터의 고유성을 확인하거나 검색 성능을 향상시키기 위해 사용하는 객체이다.
특정 칼럼에 INDEX를 생성하면, 해당 칼럼의 데이터들을 정렬하여 별도의 공간에 데이터의 물리적 주소와 함께 저장한다.
INDEX의 가장 중요한 기능은 데이터의 조회 속도를 빠르게 하는 것이다.
속도 향상을 위해 index를 설정한다. INDEX 를 걸었지만, SELECT 문이 제대로 인덱스를 타고 있는지 알아볼 필요가 있다. MySQL 에서 쿼리의 실행 계획을 확인하려면 explain 명령을 사용하면 된다.
explain 명령은 SELECT 문 앞에 explain SELECT 를 붙이기만 하면 된다.
const : 매치되는 행이 하나만 존재
하나의 행이기 때문에 각 컬럼값은 나머지 연산에서 상수로 간주되며, 처음 한번만 읽어들이면 되기 때문에 엄청 빠르다.
possible_keys : 쿼리를 튜닝하는데 도움이 안되므로 possible_keys 칼럼은 그냥 무시해도 된다.
key : 사용된 인덱스를 나타내고, 쿼리를 튜닝할 때 key 칼럼에 의도했던 인덱스가 표시되는지
확인하는 것이 중요하다.
실행계획의 type 이 ALL 이면 key 칼럼은 NULL로 표시된다.
실행계획의 type 이 index_merge 가 아닌 경우에는 반드시 테이블당 하나의 인덱스만 이용할 수 있다.
key_len : 사용된 가장 긴 key 부분을 나타. 실제 업무는 다중 칼럼으로 만들어진 인덱스가 더 많다.
쿼리를 처리하기 위해 다중 칼럼으로 구성된 인덱스에서 몇개의 칼럼이 사용되었는지 알려준다.
ref : 접근 방법이 ref 방식이면 참조 조건으로 어떤 값이 제공되었는지 보여준다.
range : 인덱스를 하나의 값이 아니라 범위로 검색하는 경우를 의미하는데, 주로 <, >, <=, >=, IS NULL, BETWEEN, IN, LIKE 등의 연산자를 이용해 인덱스를 검색할 때 사용된다.
모든 쿼리가 이 접근 방법만 사용해도 어느 정도의 성능을 보장된다고 볼 수 있다.
- ref 컬럼은 이 타입의 조인에서 NULL 임
LIKE 검색을 할 경우 FULL SCAN 하는지 확인이 가능하다.
ALL : Full TABLE Scan 된다.
대부분의 경우에 아주 느린 성능을 보인다.가장 마지막에 선택되는 가장 비효율적인 방법이다. 빠른 응답을 사용자에게 보내줘야 하는 웹서비스와 같은 환경에서는 적합하지 않다. LIKE 검색시 앞에 %를 붙이지 않으면 INDEX 를 탄다는 걸 알 수 있다.
이제 인덱스를 제거하고 다시 explain 해보자.
mysql > ALTERTABLE`bbs_file`DROPINDEX`mp3file`;
WHERE 조건에 사용되는 칼럼 mp3file 을 INDEX를 삭제하고 나서 LIKE 검색을 했더니 ALL(Full Scan)으로 변경되었다.
WHERE 조건에 사용되는 칼럼에 INDEX 가 설정되어 있어야만 검색속도가 빠르게 될 수 있다는 걸 확인했다.
mysql > ALTER TABLE `bbs_file` ADD UNIQUE (`mp3file`); -- mp3file 칼럼에 인덱스 추가
이제 두개의 테이블을 조인해서 원하는 결과를 조회하는 걸 해보자.
explain select d.uid, d.no, f.rand from bbs_data d, bbs_file f where d.no = f.no; explain select d.uid, d.no, f.rand from bbs_file f, bbs_data d where f.no = d.no;
조인하는 테이블 순서는 상관이 없다는 걸 테스트해서 확인했다.
처음에는 ALL(Full Scan) 하고 다음에는 eq_ref 가 나왔다.
eq_ref : 조인수행을 위해 각 테이블에서 하나씩의 행만이 읽혀지는 형태.
조인(INNER JOIN) 에서 두번째 이후에 읽는 테이블(bbs_file)에서 반드시 1건만 존재한다는 보장이 있어야 사용할 수 있는 접근 방법이다.
- const 타입이외에 가장 훌륭한 조인타입이다.
- 조인연산에 PRIMARY KEY 나 UNIQUE index 인덱스가 사용되는 경우.
- 인덱스된 컬럼이 = 연산에 사용되는 경우.
- 비교되는 값은 상수이거나 이전조인결과의 컬럼값일 수 있다.
explain SELECT uid, no, (select classname from bbs_category where uid=bbs_data.category1) as cate1, (select classname from bbs_category where uid=bbs_data.category2) as cate2 from bbs_data;
mysql > ALTER DATABASE `DBname` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; mysql > ALTER DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
명령어를 통해서 MySQL 기본 인코딩을 변경하는 걸 처리했다.
Database 를 생성할 때 기본 인코딩 값을 UTF-8 로 설정하는 방법
mysql > CREATE DATABASE `DBname` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
기본적으로 처리하기 위해서는 MySQL 환경설정변수 파일(my.cnf)을 수정해줘야 한다.
홈페이지 버전업을 하면서 기존 테이블에 필드를 추가해둔 것이 있어서 추가된 필드만 별도로 MySQL 상에서 미리 작업하면 편리할 거 같아서 테스트해보고 기록해둔다.
// 칼럼 추가
ALTER TABLE `테이블명` ADD `추가할 칼럼명` 칼럼 타입 AFTER `현재 칼럼명`; ALTER TABLE `rb_bskrbbs_data` ADD `hidden1` TINYINT( 4 ) NOT NULL DEFAULT '0' AFTER `hidden`; ALTER TABLE `rb_bskrbbs_data` ADD `category1` VARCHAR( 100 ) NULL DEFAULT NULL AFTER `category`;
한번에 여러개 칼럼 추가할 때 ALTER TABLE `rb_bskrbbs_data` ADD `category1` VARCHAR( 100 ) NULL DEFAULT NULL AFTER `category` , ADD `category2` VARCHAR( 100 ) NULL DEFAULT NULL AFTER `category1` , ADD `category3` VARCHAR( 100 ) NULL DEFAULT NULL AFTER `category2`;
// 칼럼 삭제
ALTER TABLE `테이블명` DROP `칼럼명`;
ALTER TABLE `rb_bskrbbs_data` DROP `category1`;
한번에 여러개 칼럼 삭제할 때
ALTER TABLE `rb_bskrbbs_data` DROP `category1`, DROP `category2`, DROP `category3`;
// 컬럼명 변경 및 타입 변경 ALTER TABLE `테이블명` change `컬럼명` `변경할컬럼명` varchar(20);
ALTER TABLE `rb_bskrbbs_data` CHANGE `category1` `category1` VARCHAR( 80 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL; ALTER TABLE `rb_bskrbbs_data` CHANGE `category1` `category4` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL; ALTER TABLE `rb_bskrbbs_data` CHANGE `category4` `category1` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL;
// 컬럼 타입 수정 ALTER TABLE `테이블명` modify `컬럼명` varchar(14);
ALTER TABLE `rb_bskrbbs_data` CHANGE `category1` `category1` CHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL; ALTER TABLE `rb_bskrbbs_data` CHANGE `category1` `category1` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL;
ALTER TABLE `rb_bskrbbs_data` modify `category1` VARCHAR( 120 ); ALTER TABLE `rb_bskrbbs_data` modify `category1` CHAR( 100 ); ALTER TABLE `rb_bskrbbs_data` modify `category1` VARCHAR( 100 );
SELECT 필드명, count(*) as 변수명 FROM 테이블명 GROUP BY 필드명 HAVING 변수명 > n;
또는
SELECT 필드명, count(*) FROM 테이블명 GROUP BY 필드명 HAVING count(*) > n;
// 중복된 값을 찾고자 하는 필드명을 지정한다.
// 가령 mp3rand 라고 지정하면 GROUP BY mp3rand
칼럼에 INDEX 를 걸기 전과 건 후의 속도 차이를 보면 엄청난 차이가 발생한 걸 알 수 있다.
2. 중복된 데이터 추출
SELECT column1, column2, column3, ...
FROM tableA
WHERE column1 IN (
SELECT column1
FROM tableA
GROUP BY column1
HAVING COUNT(*) > 1
);
로 하면 중복된 게시물을 보여준다.
select a.uid, a.no, a.eng from data a, (select eng from data group by eng having count(*) > 1) b where a.eng = b.eng; select uid, no, eng from data where eng IN (select eng from data group by eng having count(*) > 1) order by eng;
이 두개는 동일한 결과를 화면에 뿌려준다.
하지만 Query 속도는 JOIN 으로 가져온 첫번째 방법이 훨씬 더 빠르다.
동명이인 자료 추출
select m.* from member m, (select userNM from member group by userNM having count(*) > 1) b where m.userNM=b.userNM;
그런데 가져올 기존 테이블명을 손대지 않고 처리를 하려면 별칭으로 이름을 변경해주면 중복이 발생하지 않기 때문에 자료를 조회하는데 문제가 생기지 않는다.
select * from member , (select userNM as userNM1 from member group by userNM having count(*) > 1) b where member.userNM=b.userNM1;
3. 중복된 것 모두 찾기
SELECT 필드명, count(*) FROM 테이블명 GROUP BY 필드명
영문, 한글자까지 문자의 정확한 길이를 알려면 CHAR_LENGTH() 함수를 사용한다.
SELECT eng, CHAR_LENGTH(eng) FROM data WHERE uid =번호;
테이블 칼럼에 중복되는 자료가 존재하면 안되기 때문에 테이블 설계시에 중복발생이 되지 않도록 인덱스를 걸어두었다.
이미 등록한 자료를 다시 등록을 시도해봤다.
load data local infile '/home/httpd/test/test.csv' into table `rand_mp3file` fields terminated by ';' -- 칼럼을 구분할 구분자를 지정 lines terminated by '\r\n' -- Row(행)을 구분해줄 구분자를 지정 ignore 1 lines -- 등록할 파일의 첫줄을 무시하고 등록하라 (`relatedno` , `mp3file` , `mp3rand` );
총 5개의 레코드인데 전부 Skipped 된 걸로 나온다.
즉 등록된 자료는 하나도 없는 것으로 나온다.
UNIQUE INDEX 를 추가하고 업데이트 방식을 해보고자 한다.
중복된 자료가 있을 경우와 중복되지 않은 자료가 있을 경우 어떤 현상이 생기는지 궁금해졌다.
mysql > show index from 테이블명;
첫번째 데이터 등록은 잘 등록되었는데, 추가 자료를 등록할 때는 어떻게 등록해야 UNIQUE 하게 자료가 잘 등록될까?