MySQL의 DB에 저장된 데이터가 대략 1분 간격으로 저장된 데이터가 있다고 할때에 이 데이터에서 10분 간격의 데이터를 추출하고 싶을때 쿼리를 어떻게 작성해야 할까?
이를 위해 필요한 기법과 개념은 
 -. UNIX_TIMESTAMP() 함수
 -. GROUP BY
 -. DIV
정도이다.

테이블명 : tb_test
r_Date : 2022-08-01 15:59:00과 같은 형태로 대략 1분 간격의 데이터가 저장되어 있다고 할때

SELECT * FROM tb_test GROUP BY UNIX_TIMESTAMP(r_Date) DIV 600 ORDER BY r_Date DESC

-. UNIX_TIMESTAMP(r_Date)에서 출력되는 결과는 1659337140이다. 이는 1970.1.1이후 경과한 초의 값을 반환한다.

MySQL UNIX_TIMESTAMP() returns a Unix timestamp in seconds since '1970-01-01 00:00:00' UTC as an unsigned integer if no arguments are passed with UNIX_TIMESTAMP().
When this function used with a date argument, it returns the value of the argument as an unsigned integer in seconds since '1970-01-01 00:00:00' UTC.
The argument may be a DATE, DATETIME,TIMESTAMP or a number in YYYYMMDD or YYMMDD.

-. UNIX_TIMESTAMP(r_Date) DIV 600
이것이 의미하는 것은 r_Date 칼럼에 있는 년-월-일 시:분:초의 값을 UTC 초 값으로 반환된 것을 600(10분에 대한 초)으로 나눈 값을 반환한다. 이때 소수점 이하는 절삭된 정수형 값을 반환한다. 

-. GROUP BY UNIX_TIMESTAMP(r_Date) DIV 600
이것의 의미는 1분간격의 년-월-일 시:분:초의 값을 DIV 600으로 나눈 후 소수점을 절삭하면 10분 간격의 시간 값은 동일한 값을 갖게 된다.  DIV 자체가 소수점을 절삭한 정수형을 반환한다.
사실은 2765561.x, 2765560.x, 2765559.x와 같이 소수점 이하에서 각각 다른 값들을 갖는데 소수점이 절삭된 정수형 값을 취하게 되면 아래와 같이 10분 간격의 값들은 동일한 값을 가지므로 GROUP BY로 묶을수 있는 조건이 된다.

2022-08-01 15:59:00;  2765561
2022-08-01 15:58:00;  2765561
2022-08-01 15:57:00;  2765561
2022-08-01 15:56:00;  2765561
2022-08-01 15:55:00;  2765561
2022-08-01 15:54:00;  2765561
2022-08-01 15:53:00;  2765561
2022-08-01 15:52:00;  2765561
2022-08-01 15:51:00;  2765561

2022-08-01 15:48:00;  2765560
2022-08-01 15:47:00;  2765560
2022-08-01 15:46:00;  2765560
2022-08-01 15:45:00;  2765560
2022-08-01 15:44:00;  2765560
2022-08-01 15:43:00;  2765560
2022-08-01 15:42:00;  2765560
2022-08-01 15:41:00;  2765560
2022-08-01 15:40:00;  2765560

2022-08-01 15:39:00;  2765559
2022-08-01 15:38:00;  2765559
2022-08-01 15:37:00;  2765559
2022-08-01 15:36:00;  2765559
2022-08-01 15:35:00;  2765559
2022-08-01 15:32:00;  2765559
2022-08-01 15:31:00;  2765559
2022-08-01 15:30:00;  2765559

따라서 GROUP BY로 묶으면 아래와 같이 10분 간격의 값을 추출할수가 있다. 만일 5분 간격으로 데이터를 추출하고자 한다면 DIV 300으로 하면 된다.

2022-08-01 15:51:00;  2765561
2022-08-01 15:40:00;  2765560
2022-08-01 15:30:00;  2765559

외부의 텍스트 파일로 작성된 SQL문을 이용하여 MySQL의 table 만들기


d:\mydir\member.sql이라는 이름으로 다음과 같은 SQL문이 작성되어 있다고 할때 SQL문이 작성된 외부의 파일로부터 MySQL의 테이블 생성하는 방법이다.


create table tbl_member (

userid varchar(50) not null,

userpw varchar(50) not null,

username varchar(50) not null,

email varchar(100),

regdate timestamp default now(),

updatedate timestamp default now(),

primary key(userid)

);


먼저 mysql에 로그인한다. 만일 tbl_mamber라는 테이블을 생성할 데이터베이스가 book이라고 한다면

C:\>mysql -uroot -p


mysql>use book;

Database changed


mysql>source d:/mydir/member.sql

Query OK, 0 rows affected (0.03 sec)


여기서 중요한 것은 윈도우즈에서 경로 표시때 사용되는 \(backslash)를 사용하는 것이 아니라 /(forwardslash)를 사용한다는 점이다.

이상을 tbl_member라는 book이라는 데이터베이스에 tbl_member라는 테이블을 생성했다.


mysql> show tables;

+--------------------+

| Tables_in_book     |

+--------------------+

| tbl_member         |

+--------------------+

1 row in set (0.00 sec)


mysql> desc tbl_member;

+------------+--------------+------+-----+-------------------+-------+

| Field      | Type         | Null | Key | Default           | Extra |

+------------+--------------+------+-----+-------------------+-------+

| userid     | varchar(50)  | NO   | PRI | NULL              |       |

| userpw     | varchar(50)  | NO   |     | NULL              |       |

| username   | varchar(50)  | NO   |     | NULL              |       |

| email      | varchar(100) | YES  |     | NULL              |       |

| regdate    | timestamp    | NO   |     | CURRENT_TIMESTAMP |       |

| updatedate | timestamp    | NO   |     | CURRENT_TIMESTAMP |       |

+------------+--------------+------+-----+-------------------+-------+

6 rows in set (0.01 sec)




원격 IP로 MySQL(MariaDB)에 접속이 되지 않을 때의 해법


※ Ubuntu 16.04 MariaDb 10.0의 상황과 원격 IP의 주소가 111.222.33.44라고 가정한다.


Java에서 다음과 같이 원격의 MySQL에 접속을 시도할때


String url = "jdbc:mysql://111.222.33.44:3306/나의DB명";


Connection refused 에러가 발생하거나 telnet을 이용해서 MySQL이 설치된 IP의 3306포트로 접속시 연결이 되지 않을 때의 해법이다.

네트워크의 연결 상태에따라 다양한 이유가 있을 수 있다. 예를들어 공유기 아래 서버가 물려있을 때 공유기에서 3306에 대한 포트 포워딩이 되어있지 않다면 아래의 방법이 통할수 없을 것이다.

다양한 원인들이 있으나 본 포스트는 3306 포트에 listening이 외부 IP로 설정되지 않고 127.0.0.1로 설정되어 있을 경우에 대한 해법이다.


telnet을 이용해서 해당 IP로 3306포트로 접속이 되는지부터 확인해보자.

일단 Windows에서는 기본적으로 telnet을 사용할수 없는 상태로 설정되어 있다. 다음과 같이 하여 telnet을 사용할수 있도록 변경한다.


제어판 - 프로그램 제거 또는 변경 - Windows 기능 켜기/끄기 - 텔넷 클라이언트 체크


MS-DOS 창에서 telent으로 다음과 같이 먼저 확인해 본다.


telnet 111.222.33.44 3306

연결 대상 111.222.33.44...호스트에 연결할 수 없습니다. 포트 3306: 연결하지 못했습니다.


SQLyog에서 접속시도해 보면 다음과 같은 에러가 발생한다.


오류 번호 2003 Can't connect to MySQL server on '111.222.33.44'


이럴 경우 MySQL이 외부 IP의 3306포트로 접속이 가능하지 않은 상태이다. 

아래와 같이 자기 자신의 IP(여기서는 111.222.33.44)로 3306 포트로 접속시 접속을 시도했을 때 접속이 되지 않는다면 외부 IP에서 3306포트로 접속이 가능하지 않은 상태라는 뜻이다.


물론 아래의 접속으로 접속이 되기 위해서는 사전에 아래 명령어에 의해서 root가 외부 IP로 접속할수 있는 권한 설정이 mysql 데이터베이스에 설정 되어 있어야 하는 걸 전제로 한다.


MariaDB [(none)]> grant all privileges on *.* to 'root'@'%' identified by '비번';

MariaDB [(none)]> flush privileges;


만일 위와 같이 root가 외부 IP로의 자유로운 접속이 허락되어 있지 않으면 아래의 명령으로 접속이 안되는 건 당연한 일이다.

다음과 같이 MySQL에서는 권한 설정이 아래와 같이 정상적으로 되어 있다고 가정해 보자.


MariaDB [(none)]> use mysql;

Reading table information for completion of table and column names

You can turn off this feature to get a quicker startup with -A


Database changed

MariaDB [mysql]> select host, user, password from user;

+-----------+------+-------------------------------------------+

| host      | user | password                                  |

+-----------+------+-------------------------------------------+

| localhost | root | *8EA3CE4E6710007B6FD72BC385D48F3D985FD258 |

| %         | joe  | *8EA3CE4E6710007B6FD72BC385D48F3D985FD258 |

| %         | root | *8EA3CE4E6710007B6FD72BC385D48F3D985FD258 |

+-----------+------+-------------------------------------------+

3 rows in set (0.00 sec)


MariaDB [mysql]> select host, user, db from db;

+------+------+-------+

| host | user | db    |

+------+------+-------+

| %    | joe  | mysql |

+------+------+-------+

1 row in set (0.00 sec)


일단 여기서는 root에 대한 외부 IP로의 접속 권한이 설정되어 있다는 걸 전제로 했을 때 아래와 같이 접속이 안된다면 현재 MySQL(MariaDB)는 외부 IP로의 접속을 위한 설정에 문제가 있다는 것이다.


root@xxx:/home/joe# mysql -h 111.222.33.44 -P 3306 -uroot -p

Enter password: 


이 경우 다음과 같은 에러가 발생한다.


ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.0.107' (111 "Connection refused")


이상과 같이 정상적으로 외부 IP로의 접속이 허용되어 있음에도 접속 불능사태가 발생한다면 다음 명령으로 3306 포트에 대해 listening 상태를 확인해 보자.


# netstat -ntlp | grep 3306

을 하면 


tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      1235/mysqld


이렇게 나오면 localhost에서는 접속이 되지만 원격 IP에서는 접속이 안된다.


이 문제에 대한 해법은 아래 파일에서

/etc/mysql/mariadb.conf.d/50-server.cnf

  

bind-address            = 127.0.0.1 를 주석처리하고

bind-address   = 0.0.0.0 을 추가한다. 다음과 같이


#bind-address            = 127.0.0.1 

bind-address   = 0.0.0.0


변경된 사항을 적용하기 위해서 

# service mysql restart

를 하면된다고 검색해 보면 나와있는데 이 방법으로는 netstat으로 확인해보면 여전히 3306에 대한 listening을 127.0.0.1로되어 있다.

현재 가장 확실한 방법은 서버를 재부팅하면 변경된 설정 값이 제대로 적용이되고 외부 IP로 접속이 정상적으로 된다.

재부팅 없이 되는 방법을 아직은 모르겠다....;;;



DOS 창에서 MySQL에 root 계정으로 비번 입력 없이 막바로 접속이 가능할 경우 비번 셋팅하기


MySQL을 Windows에 설치하는 방법 중 일반적인 install 방법이 아닌 압축(.zip) 형태의 파일을 다운 받아 압축을 해제한 후 MySQL을 등록하는 경우 root계정에 비번이 없는 상태로 등록이 되어 비번 입력 없이 MySQL에 접속이 된다. 이럴경우 다음과 같이 MySQL에 접속한 후 


c:\mysql -uroot -p

Enter password:


여기서 비번 입력 없이 엔터를 치면 막바로 다음과 같이 MySQL에 접속이 된다.


Welcome to the MySQL monitor.  Commands end with ; or \g.

Your MySQL connection id is 7

Server version: 5.6.40 MySQL Community Server (GPL)


Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.


Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.


Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.


이럴 경우 root 계정에 비밀번호를 셋팅하는 방법이다.


mysql> use mysql;

mysql> select host, user, password from user;

+-----------+------+----------+

| host      | user | password |

+-----------+------+----------+

| localhost | root |          |

| 127.0.0.1 | root |          |

| ::1       | root |          |

| localhost |      |          |

+-----------+------+----------+

4 rows in set (0.00 sec)


이상과 같이 mysql 데이터베이스의 root 계정에 비번이 설정되어 있지 않음을 확인할 수 있다.

이제 root 계정에 비밀번호를 다음과 같이 셋팅한다.


mysql> update user set password=password('xxxxxxxx') where user='root';

Query OK, 3 rows affected (0.03 sec)

mysql>flush privileges;

Query OK, 0 rows affected (0.00 sec)


이상과 같이 하면 이제 MySQL에 root 계정으로 접속시 비번을 입력해야 접속이 가능하게 된다.



MySQL의 외부 IP 접속 허용하기


Java에서 MySQL을 접속할 때 localhost로 접속하거나 127.0.0.1로 접속할 때는 정상적으로 잘 되던 것이 


String url = "jdbc:mysql://192.168.x.x:3306/mysql";


장비의 실제 IP주소나 도메인 이름으로 접속할때 다음과 같은 에러가 발생할 때 


javax.servlet.ServletException: java.sql.SQLException: null,  message from server: "Host 'xxxx' is not allowed to connect to this MySQL server"


이 경우는 MySQL을 외부에서 접속할 수 있도록 권한을 허용해 주어야 한다.


mysql -uroot -p

Enter password:


로 접속해서 다음 과정으로 외부 IP에서 접속할 수 있도록 권한을 허용해 주어야 한다.


mysql> use mysql;

Database changed

mysql>


다음 명령을 실행해 보면 MySQL에 접속할 수 있는 user들의 계정과 접속이 가능한 host가 다음과 같이 나타날 것이다.


mysql> select host, user from user;

+-----------+------+

| host         | user |

+-----------+------+

| 127.0.0.1   | root |

| ::1           | root |

| localhost   |      |

| localhost   | root |

+-----------+------+

4 rows in set (0.00 sec)


위에서 보는 바와 같이 접속이 가능한 host가 localhost와 127.0.0.1에 대해서만 root id로 접속이 가능하다.  따라서 다음코드는 정상적으로 접속이 가능하다.

String url = "jdbc:mysql://localhost:3306/mysql";

String url = "jdbc:mysql://127.0.0.1:3306/mysql";


그러나 만일 MySQL이 설치된 컴퓨터의 IP가 만일 192.168.122.19라고 한다면 다음과 같은 코드는 "Host 'xxxx' is not allowed to connect to this MySQL server"과 같은 에러가 발생한다.


String url = "jdbc:mysql://192.168.122.19:3306/mysql";


다음 명령어로 외부 접속 권한을 설정한다.

mysql> GRANT ALL PRIVILEGES ON DB명.* TO 사용자계정@'IP주소' identified by 'password';

예) mysql> grant all privileges on mysql.* to 'root'@'%' identified by 'xxxxx';


위에서 IP주소 부분을 %로 지정하면 모든 외부의 IP를 모두 허용한다는 뜻이다.


mysql> flush privileges;

Query OK, 0 rows affected (0.00 sec)


이후부터는 외부 IP에서의 접속이 정상적으로 실행된다.


mysql> select host, user from user;

+-----------+------+

| host        | user |

+-----------+------+

| %           | root |

| 127.0.0.1  | root |

| ::1           | root |

| localhost  |      |

| localhost  | root |

+-----------+------+

5 rows in set (0.00 sec)


위에서 보는 바와 같이 host가 %로 모든 IP에서의 접속이 가능하도록 설정되어 있음을 확인할수 있다.



MySQL table에 데이터를 insert하다보면  

Data truncation: Data too long for column '에러가_발생한_필드' at row 1

과 같은 에러를 만나는 경우가 있다.


에러 메시지 자체만 놓고 보면 특정 필드(column)의 선언된 크기보다 insert 될 데이터의 크기가 더 크기 때문으로 보인다.

그런 경우도 있으나 MySQL의 환경 설정상의 원인으로 인한 경우도 있다.

MySQL의 mode가 STRICT인 경우에 위와 같은 에러가 빈번히 발생할수 있다.


MySQL의 설정 파일인 my.cnf에 sql_mode의 값이 STRICT_TRANS_TABLES일 경우 필드에 선언된 대로의 값이 주어지지 않으면 위와같은 에러를 발생시키고 쿼리문을 중단한다.

현재의 sql_mode가 어떤 내용인지를 확인할려면 my.cnf를 열어서 확인할수도 있고 아래와 같이 MySQL에 접속한 상태에서 


# mysql -umysql아이디 -p로 mysql에 접속한 상태에서 select @@global.sql_mode 명령어로 현재의 sql-mode를 확인할수 있다.


mysql> select @@global.sql_mode;

+----------------------------------+

| @@global.sql_mode              |

+----------------------------------+

| NO_ENGINE_SUBSTITUTION    |

+----------------------------------+


sql_mode가 STRICT일 경우는 insert 작업을 까다롭게 관리해서 데이터의 무결성을 위해서는 좋으나 사실은 번거로운 상황이다.

STRICT모드를 변경할려면 my.cnf의 sql_mode의 내용을 변경해 주면 된다.

CentOS의 경우 


# vi /etc/my.cnf 파일을 열어서


# Recommended in standard MySQL setup

sql_mode=NO_ENGINE_SUBSTITUTION, STRICT_TRANS_TABLES


sql_mode에서 STRICT_TRANS_TABLES를 제거하거나 아니면 sql_mode 자체를 주석처리해서 사용하지 않도록 하면된다.


저장 후 mysql 데몬을 재 시작해 준다.


# service mysqld restart




MySQL 테이블의 특정 필드에 한글을 insert하다보면 아래와 같은 에러가 발생하는 경우를 만날수 있다.

ERROR 1366 (HY000): Incorrect string value: ...


kkk라는 테이블이 아래와 같은 형식으로 생성되어 있다고 가정하면,


mysql> show create table kkk;

+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------+

| Table | Create Table                                                                                                                                             |

+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------+

| kkk   | CREATE TABLE `kkk` (

  `bdid` bigint(11) unsigned NOT NULL,

  `name` varchar(80) NOT NULL,

  PRIMARY KEY (`bdid`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 |

+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------+

1 row in set (0.00 sec)


위와 같이 kkk라는 테이블이 있을 경우 DEFAULT CHARSET=latin1으로 되어 있다.

이때 한글을 name 필드에 insert하게 되면 아래와 같은 에러가 발생한다.


mysql> insert into kkk (bdid, name) values (1, '한글입력');

ERROR 1366 (HY000): Incorrect string value: '\xED\x95\x9C\xEA\xB8\x80...' for column 'name' at row 1


이 문제 해결을 위해서는 default character set을 utf8로 변경해 주면 깨끗이 해결된다.

kkk라는 테이블을 생성하는 DDL 문에서 아예 utf8로 설정해서 테이블을 생성해 주면 된다.

다음과 같이


CREATE TABLE `kkk` (

  `bdid` bigint(11) unsigned NOT NULL,

  `name` varchar(80) NOT NULL,

  PRIMARY KEY (`bdid`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;



MySQL 필드의 내용이 한글 일 경우 Java 소스코드 상 SELECT문의 비교문에서 인식이 안되는 문제가 있다.


mysql> select * from board;

+------+-----------------+

| bdid | name            |

+------+-----------------+

|    1 | 공지사항        |

|    2 | QandA           |

|    3 | 자유게시판      |

|    4 | menu            |

+------+-----------------+


board라는 테이블의 내용이 위와 같다고 할 경우


select bdid from board where name = '자유게시판';

과 같은 구문에서 bdid의 값 3이 추출되지 않는다.


만일 select bdid from board where name = 'QandA';

와 같이 한글이 아닌 영문일 경우는 정상적으로 bdid의 값 2를 추출할 수 있다.


이 문제는 MySQL의 환경설정 중 character set상에서의 문제이다.


mysql> show variables like '%chara%';

+--------------------------+----------------------------+

| Variable_name            | Value                      |

+--------------------------+----------------------------+

| character_set_client     | utf8                       |

| character_set_connection | utf8                       |

| character_set_database   | latin1                     |

| character_set_filesystem | binary                     |

| character_set_results    | utf8                       |

| character_set_server     | utf8                       |

| character_set_system     | utf8                       |

| character_sets_dir       | /usr/share/mysql/charsets/ |

+--------------------------+----------------------------+


비록 위와 같이 MySQL variagle들이 utf8로 설정되어 있다하더라도 

/etc/my.cnf라는 설정 파일을 아래와 같이 charater set을 모두 설정해 주어야 한글이 정상적으로 select문에서 작동하게 된다.


[client]

default-character-set = utf8


[mysql]

default-character-set = utf8


[mysqld]

#

# Remove leading # and set to the amount of RAM for the most important data

# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.

# innodb_buffer_pool_size = 128M

#

# Remove leading # to turn on a very important data integrity option: logging

# changes to the binary log between backups.

# log_bin

#

# Remove leading # to set options mainly useful for reporting servers.

# The server defaults are faster for transactions and fast SELECTs.

# Adjust sizes as needed, experiment to find the optimal values.

# join_buffer_size = 128M

# sort_buffer_size = 2M

# read_rnd_buffer_size = 2M

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock


character-set-server = utf8

collation-server = utf8_general_ci

init_connect=SET collation_connection = utf8_general_ci

init_connect=set NAMES utf8


character-set-client-handshake = FALSE

skip-character-set-client-handshake

wait_timeout=31536000


[mysqldump]

default-character-set = utf8


# Disabling symbolic-links is recommended to prevent assorted security risks

#symbolic-links=0


# Recommended in standard MySQL setup

#sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES


[mysqld_safe]

log-error=/var/log/mysqld.log

pid-file=/var/run/mysqld/mysqld.pid


위와 같이 설정 후 MySQL 데몬을 재가동해 준다.

# service mysqld restart




Database의 DB table이 생성되어 있을 때 이들 table 생성시 사용했던 DDL(Data Definition Language)를 보고 싶을 경우가 있다.

한마디로 이미 생성되어 있는 DB table들의 DDL을 뽑아내는 방법에 대한 것이다.


명령어 구문은(MySQL에 접속한 상태에서)


mysql> show create table 테이블명;


mysql> show create table kkk;

+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------+

| Table | Create Table                                                                                                                                             |

+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------+

| kkk   | CREATE TABLE `kkk` (

  `bdid` bigint(11) unsigned NOT NULL,

  `name` varchar(80) NOT NULL,

  PRIMARY KEY (`bdid`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 |

+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------+

1 row in set (0.00 sec)


kkk라는 테이블을 생성한 DDL 구문이다.


여기서 ENGINE의 종류는 InnoDB이다. MySQL의 경우 2가지 엔진 타입이 있는 InnoDB와 MyISAM이 있다.

InnoDB의 경우는 빈번한 쓰기, 수정, 삭제가 발생할 경우 유리하고

MyISAM은 InnoDB에 비해 상대적으로 성능이 높은 편인데 주로 읽기 위주의 처리에 높은 성능을 발휘한다.

반면에 MyISAM은 빈번한 쓰기, 수정, 삭제가 발생하는 경우라면 오히려 InnoDB보다 성능이 못하다고 한다.

정답은 상황에 맞게...


여기서 또 한가 주목할 부분은 DEFAULT CHARSET=latin1을 되어 있다.

이럴 경우 한글을 insert할 때 에러가 발생한다.

이 문제에 대한 해법은 다음 링크 참조.


default charset과 한글 입력 문제




mysql의 table을 생성할 때 로컬의 특정 파일이 담고 있는 내용을 table의 값으로 저장해야하는 경우가 있다.

이때 로컬의 특정 파일은 dump 파일 일수도 있고, 일반적인 txt 파일 일수도 있다.

test.txt에 ,로 구분되어 다음과 같은 내용이 담겨 있다고 하면

홍길동,남,010-1234-4567,YES

장길동,여,011-0123-1111,NO

...

이 내용을 mysql의 특정 table의 내용으로 저장하는 작업을 할수도 있다.


혹은 로컬의 특정 파일의 내용이 다음과 같이 |로 필드의 값이 구분되어 있다고 할 경우

12      |       74109    |

30      |       29        |

36      |       184914  |

37      |       42        |

46      |       39        |

67      |       32033   |

76      |       155892  |

77      |       74311   |

79      |       74313   |

80      |       155892 |

90      |       34102   |

91      |       34103   |

.....


이럴 경우 mysqlimport 명령어를 이용하면 된다.


mysqlimport  -uroot  -p  --local  --fields-terminated-by="|"  yyy  merged.dmp


여기서 -uroot는 mysql 사용자 계정의 id가 root라는 뜻이고 -p옵션은 mysql 사용자 계정의 비번을 입력하겠다는 것이다. 

이 경우는 비번을 입력하지 않으면 위의 명령어를 실행할 때 비번을 묻게 된다.


--local 옵션을 주지 않으면 로컬 파일인 merged.dmp를 읽지 못하고 다음과 같은 에러를 발생 시킨다.


mysqlimport: Error: 13, Can't get stat of '/var/lib/mysql/yyy/merged.dmp' (Errcode: 2 - No such file or directory), when using table: merged


이 에러는 DB 명으로 입력한 yyy를 디렉토리 경로로 인식하는 문제다.


그 다음 옵션인 --fields-terminated-by="|"는 table저장할 데이터가 들어 있는 merged.dmp의 내용이 위에서 보는바와 같이 |를 기준으로 필드의 값이 구분되어 있다.

따라서 필드 구분자를 지정해 줘야 한다. 만일 ,로 구분되어 있다면 --fields-terminated-by=","와 같이 하면된다.


그다음 옵션인 yyy는 Database 이름이다. yyy라는 데이터베이스 안에 merged라는 table에 merged.dmp가 가지고 있는 내용을 table의 각 레코드와 필드에 값을 저장하게 된다.

이때 만일 다음과 같은 에러가 발생할 것이다.


mysqlimport: Error: 1290, The MySQL server is running with the --secure-file-priv option so it cannot execute this statement, when using table: merged


이 에러는 보안 상의 이유로 특정 디렉토리에 있는 파일에 대해서만 위의 명령어를 실행할수 있도록 설정되어 있다는 뜻이고 그 특정 디렉토리가 어느 곳인지를 알려면

mysql -u사용자id -p사용자비번

으로 들어가서 


mysql>show variables like 'secure%';


를 실행하면


+------------------+--------------------------+

| Variable_name    | Value             |

+------------------+--------------------------+

| secure_auth      | ON                  |

| secure_file_priv  | /aaa/bbb/ccc/     |

+------------------+---------------------------+


와 같은 정보가 나올 것이다. 이것은 /aaa/bbb/ccc/라는 디렉토리에서만 mysqlimport로 특정 파일의 내용을 mysql db의 table에 값을 저장할수 있다는 뜻이다.

만일 secure-file-priv 자체를 off 시키고 싶다면


우분투의 경우 /etc/mysql/mysql.cnf 파일 안의 맨 끝에 

secure-file-priv=""를 한줄 추가한 후


# service mysql restart


를 실행해서 mysql을 재실행해 주면 mysql.cnf의 설정대로 적용이 되어 secure-file-priv를 off하게 된다.

보안을 위해 이 작업 후에는 역시 mysql.cnf의 

secure-file-priv="/aaa/bbb/ccc/"로 변경후 역시 mysql을 restart시키는 것이 신상에 좋을 것이다.


혹은 mysqlimport와 동일한 기능을 다음과 같이도 가능하다.

mysql에 접속해서(mysql -u사용자id -p)


mysql> LOAD DATA INFILE '/var/lib/mysql-files/merged.dmp' INTO TABLE merged FIELDS TERMINATED BY '\t|\t' LINES TERMINATED BY '\t|\n' (old_tax_id, new_tax_id);


여기서 /var/lib/mysql-files/merged.dmp는 mysql db의 table에 저장할 데이터가 들어 있는 로컬의 (텍스트)파일,

FIELDS TERMINATED BY '\t|\t'는 /var/lib/mysql-files/merged.dmp 파일의 필드 구분자가 '\t|\t'라는 뜻이고

LINES TERMINATED BY '\t|\n'는 /var/lib/mysql-files/merged.dmp 파일의 row(레코드) 구분자가 '\t|\n'라는 뜻이고

(old_tax_id, new_tax_id)는 /var/lib/mysql-files/merged.dmp 파일의 내용을 읽어서 저장할 merged라는 table의 필드명이다.


대용량 데이터를 처리해본 결과 mysqlimport에 비해서 LOAD DATA INFILE의 방식이 훨씬 안정적으로 동작했다.





+ Recent posts