# How to load csv files into MySQL tables (for Docker users) ## 요구사항 * MySQL 테이블 스키마에 맞는 대량의 데이터를 보유하고 있는 CSV 파일을 MySQL에 집어넣고자 한다. * Mac 환경에서 Docker를 기반으로 MySQL 8 이미지를 실행하고 있다. ## 파일 옮기기 * 로컬에서 작업하고 있는 CSV 파일을 도커 컨테이너로 옮긴다. * ```docker ps -a``` 명령어를 통해 현재 도커 컨테이너의 아이디를 검색한다. * 다음의 ```cp``` 명령어를 실행하여 도커 컨테이너로 csv 파일을 복사한다. ``` docker cp /home/(name)/(folder_name)/(file_name) (container_id):/(to_the_place_you_want_the_file_to_be) ``` ## 데이터 삽입하기 * 데이터가 들어가고자 하는 테이블을 만들어준다. ``` mysql> CREATE TABLE room ( -> id INT PRIMARY KEY AUTO_INCREMENT, -> name VARCHAR(150) NOT NULL, -> type INT NOT NULL, -> price INT NOT NULL, -> maximum_guest INT NOT NULL, -> bedroom INT NOT NULL, -> bed INT NOT NULL, -> bath INT NOT NULL, -> review_rating DOUBLE NOT NULL, -> review_count INT NOT NULL, -> location VARCHAR(45) NOT NULL, -> latitude DOUBLE NOT NULL, -> longitude DOUBLE NOT NULL, -> address VARCHAR(45) NOT NULL, -> host_id INT NOT NULL, -> CONSTRAINT room_has_host FOREIGN KEY (host_id) REFERENCES host (id) -> ); ``` * 다음의 명령어를 통해 CSV 파일의 데이터를 테이블로 집어넣는다. * 유의할 점은 CSV 파일에서 column 이름을 나타내는 맨 처음 row를 데이터 row로 인식한다는 것으로 이에 주의한다. * 다시 말해 스키마에서 지정했던 column 순서대로 CSV 파일이 나열되어 있어야만 하며, 그래야 column에 맞게 테이블을 삽입할 수 있다. ``` mysql> LOAD DATA LOCAL INFILE '/mock_room.csv' INTO TABLE room FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; Query OK, 50 rows affected (0.01 sec) Records: 50 Deleted: 0 Skipped: 0 Warnings: 0 ``` ## 오류 해결하기 ### 한글 깨짐 문제 해결하기 #### 원인 진단 * Docker에서 한글 자료가 담긴 CSV 파일을 로드했을 때, MySQL에서 한글 깨짐 문제가 발생할 수 있다. * MySQL이 담긴 도커 컨테이너를 실행할 때 다음의 명령어를 사용한다. 여기서 ```jypthemiracle-mysql```은 도커 컨테이너 이름이다. ``` docker exec -it jypthemiracle-mysql bash ``` * ```locale``` 명령어를 사용하여 현재 도커 컨테이너의 로케일 설정을 확인할 수 있다. * 로케일에 관련된 다양한 환경변수의 값을 확인할 수 있다. ```LANG, LANGUAGE, LC_ALL```은 설정되어 있지 않으며 이 로케일 설정 때문에 한글을 입력할 수 없음을 꼭 유의한다. ``` root@4d4524c460fc:/# locale LANG= LANGUAGE= LC_CTYPE="POSIX" LC_NUMERIC="POSIX" LC_TIME="POSIX" LC_COLLATE="POSIX" LC_MONETARY="POSIX" LC_MESSAGES="POSIX" LC_PAPER="POSIX" LC_NAME="POSIX" LC_ADDRESS="POSIX" LC_TELEPHONE="POSIX" LC_MEASUREMENT="POSIX" LC_IDENTIFICATION="POSIX" LC_ALL= ``` * ```locale -a```을 실행하여 현재 시스템에서 사용 가능한 로케일도 확인해본다. * ```ko_KR.UTF-8``` 과 같은 로케일이 보이지 않기 때문에, 별도로 설치를 해주어야 한다. ``` root@4d4524c460fc:/# locale -a C C.UTF-8 POSIX ``` #### 문제 해결하기 * 필자는 ```docker-compose.yml```을 활용하여 도커에 한글 설정을 해주었다. 파일은 다음과 같다. ``` version: "3" # 파일 규격 버전 services: # 이 항목 밑에 실행하려는 컨테이너 들을 정의 db: # 서비스 명 image: mysql:8.0.17 # 사용할 이미지 container_name: jypthemiracle-mysql # 컨테이너 이름 설정 ports: - "3306:3306" # 접근 포트 설정 (컨테이너 외부:컨테이너 내부) environment: # -e 옵션 MYSQL_ROOT_PASSWORD: "codesquad" # MYSQL 패스워드 설정 옵션 command: # 명령어 실행 - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci volumes: - /Users/jmlim/datadir:/var/lib/mysql # -v 옵션 (다렉토리 마운트 설정) ``` * 구체적인 도커 설정은 다음의 링크에서 확인한다. * http://jmlim.github.io/docker/2019/07/30/docker-mysql-setup/ * 도커를 실행할 때 LC_ALL이나 LANG 환경변수 값에 C.UTF-8을 넘겨주면 된다. * 로케일 관련 변수들이 모두 해당하는 값으로 지정된다. ``` docker run -it -e LC_ALL=C.UTF-8 jypthemiracle-mysql bash ``` * 만약 ```docker-compose.yml```을 활용하고 싶지 않다면, 다음과 같이 명령어로 도커를 실행할 수 있다. ``` docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password --name jypthemiracle-mysql -v /Users/Documents/docker/docker-mysql:/var/lib/mysql mysql:8.0.17 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci ``` * 모두 실행하였으면, 로케일을 확인하여 보자. * LANG과 LANGUAGE 값을 제외한 모든 값이 C.UTF-8로 덮여쓰여져 있음을 확인할 수 있고, 이 상태에서는 정상적으로 한글 입력이 가능하다. ``` root@77053c18f4af:/# locale LANG= LANGUAGE= LC_CTYPE="C.UTF-8" LC_NUMERIC="C.UTF-8" LC_TIME="C.UTF-8" LC_COLLATE="C.UTF-8" LC_MONETARY="C.UTF-8" LC_MESSAGES="C.UTF-8" LC_PAPER="C.UTF-8" LC_NAME="C.UTF-8" LC_ADDRESS="C.UTF-8" LC_TELEPHONE="C.UTF-8" LC_MEASUREMENT="C.UTF-8" LC_IDENTIFICATION="C.UTF-8" LC_ALL=C.UTF-8 ``` ### 지금 MySQL 버전에서는 명령어를 활용할 수 없다는 메시지 #### 문제 상황 * ```LOAD DATA``` 명령어를 활용하다가 다음과 같은 메시지를 만날 수 있다. ``` mysql> LOAD DATA LOCAL INFILE '/tmp/your.csv' INTO TABLE test.demo2 FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 LINES; ERROR 1148 (42000): The used command is not allowed with this MySQL version ``` #### 해결 방법 * 다음의 명령어를 통해 ```local_infile```이 0인지, 1인지 확인한다. ``` mysql> SHOW VARIABLES LIKE 'local_infile'; ``` * 만약 ```local_infile```이 0이라면, MySQL 콘솔에서 나간다. * 다음의 명령어를 실행하여 MySQL에 다시 접속한다. ``` mysql -u root -p --local-infile=1 ``` * 다음의 명령어를 통해 ```local_infile```이 1로 적절히 설정되었는 지 체크한다. ``` mysql> SHOW VARIABLES LIKE 'local_infile'; ``` * 명령을 실행하면 정상적으로 처리가 될 것이다. ``` mysql> LOAD DATA LOCAL INFILE ... INTO TABLE test.demo2 FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n'; Query OK, 300 rows affected (0.01 sec) Records: 300 Deleted: 0 Skipped: 0 Warnings: 0 ``` ## 기타 Tip * LOAD DATA 명령을 실행해준 다음, 실행에 Warning이 있다면 다음과 같이 메시지가 발생할 것이다. ``` mysql> LOAD DATA LOCAL INFILE '/root/xxxx.dat' INTO TABLE table_name; Query OK, 4613892 rows affected, 1 warning (30.47 sec) Records: 4613892 Deleted: 0 Skipped: 0 Warnings: 1 ``` * 이 때, ```show warnings``` 명령어를 실행해주면 어떤 부분에서 Warning이 나는 지 확인할 수 있다. * 단, LOAD DATA 명령어를 실행하고 바로 ```show warnings```를 하지 않으면 데이터가 날라간다. * 이후에 다른 명령어를 실행하고 show warnings를 하면 empty set으로 나타난다. * Column을 명시하는 별도의 row는 데이터를 담은 row로 인식하기 때문에 주의해야 한다. * column의 이름을 담은 row는 CSV 파일에서 제거하여 파일을 복사한다. * CSV 파일은 컴마 ```,``` 로 column을 구분한다. 각 필드에 컴마가 있어 CSV 파일이 정상적으로 파싱되지 않는지 확인해본다. * 필드 텍스트에 컴마가 없어야 한다. * 스키마에서 ID가 ```INT PRIMARY KEY AUTO_INCREMENT```로 설정되어 있다고 하더라도 다음을 주의해야 한다. * CSV 파일에는 id column이 있어 모든 row가 ID를 보유하고 있어야 한다. * 그렇지 않을 경우 별도 설정을 해주면 된다고 하는데 잘 모르겠다. * 아래의 링크를 참조하든지, 아니면 CSV 파일에 id column을 추가를 꼭 한다. * https://stackoverflow.com/questions/2716054/mysql-load-data-infile-with-auto-increment-primary-key * 도커 설정할 때 다음의 명령어를 활용하면 텍스트에 이모지도 지원하도록 할 수 있다. ``` --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci ``` ## References * https://www.44bits.io/ko/post/setup_linux_locale_on_ubuntu_and_debian_container * https://linuxize.com/post/how-to-remove-docker-images-containers-volumes-and-networks/ * https://stackoverflow.com/questions/18437689/error-1148-the-used-command-is-not-allowed-with-this-mysql-version * http://jmlim.github.io/docker/2019/07/30/docker-mysql-setup/ * https://www.edureka.co/community/10534/copying-files-from-host-to-docker-container