【MySQL】浅谈MySQL的LOAD DATA

mysql_001.jpg

前言

好久没碰MySQL了,这次碰巧在研究superset的时候需要将一份csv格式的数据文件导入到数据库中。正好借此机会可以重温下MySQL。

数据来源
网盘密码 : g5xa

开发环境

  • Mac OS 10.13
  • MySQL 8.0

准备工作

在开始之前需要对源数据做一次清洗:

  • 去除”,“等影响数据导入的符号
  • 去除第一行索引值,因为它不是数据
  • 如果需要咋数据库中加上id,那么为了对应在源数据中也要加上,excel中加上id还是很方便的。
  • 把数据转为utf-8格式的csv文件

在这之前先简单阅读下官方文档:MySQL Documentation

可以很方便的找到LOAD DATA的表达式:

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}] ...]

Get Started

我们知道导入数据首先得有表,所以在开始导入之前,我们先建个表。这个简单:

mysql> create table da_inv( 
id int not null auto_increment primary key, 
city varchar(20) not null, 
companyFullName varchar(255) not null, companyId varchar(100) not null, companyLabelList varchar(255) not null default '', 
companyShortName varchar(100) not null default '', 
companySize varchar(100) not null, businessZones varchar(255) not null, 
firstType varchar(100) not null, 
secondType varchar(100) not null, 
education varchar(100) not null, 
industruField varchar(100) not null,
positionId varchar(100) not null, positionAdvantage varchar(255) not null, positionName varchar(255) not null, positonLabels varchar(255) not null, 
salary varchar(100) not null, 
workYear varchar(100) not null, 
topSalary varchar(100) not null, 
bottomSalary varchar(100) not null, averageSalary varchar(100) not null);

ok...
那么问题来了,表我们建好了,数据也准备好了 。

  • 数据文件放哪里?

带着这两个问题继续查看文档:

If LOCAL is not specified, the file must be located on the server host and is read directly by the server.

我们先照着上面这句的方式,把文件放在MySQL Server中:

mysql> select @@datadir;
+-----------------------+
| @@datadir             |
+-----------------------+
| /usr/local/var/mysql/ |
+-----------------------+
1 row in set (0.01 sec)

ok.. 把数据放到对应的数据库目录下

cd /usr/local/var/mysql/inv_data
➜  inv_data  mkdir data

为了区分,我在这里新建了个data的文件夹,把数据放到这里了。

  • 切换到对应的数据库
mysql> use inv_data;
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
mysql>
  • 开始导入
load data infile 'inv_data/data/DataAnalyst.csv' into table da_inv fields terminated by ',' lines terminated by '\r\n';
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

报了个错误,emmm
MySQL还加了什么限制吗?看下文档:

mysql> select @@secure_file_priv;
+--------------------+
| @@secure_file_priv |
+--------------------+
| NULL |
+--------------------+
1 row in set (0.00 sec)
secure_file_priv may be set as follows: 
*   If empty, the variable has no effect. This is not a secure setting.

*   If set to the name of a directory, the server limits import and export operations to work only with files in that directory. The directory must exist; the server will not create it.

*   If set to `NULL`, the server disables import and export operations.

文档中解释到由于secure_file_privNULL,无法进行导入导出操作。我们需要把它改为指定路径。

通过修改my.cnf文件,找到mysqld设置secure-file-priv/usr/local/var/mysql/inv_data

mdfind -name "my.cnf"
/private/etc/my.cnf

找到mysqld,添加secure-file-priv = /usr/local/var/mysql/inv_data

[mysqld]
# skip-grant-tables
character-set-server=utf8
init_connect='SET NAMES utf8
port = 3306
socket = /tmp/mysql.sock
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
character-set-server=utf8
init_connect='SET NAMES utf8'
secure-file-priv = /usr/local/var/mysql/inv_data

保存并重启mysql

mysql.server restart
Shutting down MySQL
..... SUCCESS!
Starting MySQL
.... SUCCESS!


mysql> load data infile 'inv_data/data/DataAnalyst.csv' into table da_inv fields terminated by ',' lines terminated by '\r\n';

Query OK, 6876 rows affected (0.80 sec)
Records: 6876  Deleted: 0  Skipped: 0  Warnings: 0

搞定!

对于LOAD DATA的工作原理和一些思考

  • 这次是把文件放在MySQL Server中的,如果把文件放在本地该如何处理?

先读文档:LOCAL INFILE Request

可以看到下面这张图表:


inline_umlgraph_49.png

基本上可以这么解读:

  • 由我们的客户端(命令行)发送COM_QUERY来执行SQL语句到服务端(MySQL server)
  • 服务端接收请求后通过MySQL交互协议将文件名发送给客户端去读取文件
  • 客户端以文件流的形式将内容和一个空的数据包发送给服务端,空数据包代表传输结束。

问题来了

  • 为什么简单的发送文件,它需要客户端发送给服务端,然后服务端再把文件名返回给客户端之后才开始向服务端传送文件?多出来的两个步骤是为了什么?

客户端如果如果想要发送文件必须先知道这个文件的完整路径,但是在本例中我们只告诉了客户端inv_data/data/DataAnalyst.csv这样不完整的路径。所以需要服务端再返回给客户端该文件的详细地址才能把文件发送过去。就好比快递员接受的你的订单,但是你给他的地址并不齐全这个时候快递员就会打电话过来问你要具体的地址然后才能把快递寄到你家里。

MySQL Server的原理解释清楚了,回到一开始的问题:

如何发送本地文件到MySQL Server

If LOCAL is specified, the file is read by the client program on the client host and sent to the server. The file can be given as a full path name to specify its exact location. If given as a relative path name, the name is interpreted relative to the directory in which the client program was started.

load data local infile '/Users/weijiezhu/Desktop/DataAnalyst.txt' into table da_inv fields terminated by ',' lines terminated by '\r\n';
ERROR 1148 (42000): The used command is not allowed with this MySQL version

发现并不能顺利使用,反而告诉我这条命令被禁掉了。
可以看这篇文档 Security Issues with LOAD DATA LOCAL

For the mysql client, local data loading is disabled by default. To disable or enable it explicitly, use the --local-infile=0 or --local-infile[=1] option.

这里按照官方文档设置为1没用,不知道什么原因。改为ON就没问题了

SET GLOBAL local_infile = 'ON';
load data local infile '/Users/weijiezhu/Desktop/DataAnalyst.txt' into table da_inv fields terminated by ',' lines terminated by '\r\n';
Query OK, 0 rows affected, 6876 warnings (0.25 sec)
Records: 6876  Deleted: 0  Skipped: 6876  Warnings: 6876

ok 搞定了!

导入本地文件的总结

  • --local-infile参数决定了我们能否通过客户端来使用LOAD DATA LOCAL INFILE的关键,如果在设置为1是无效,那么可以强制设置为ON也是个好办法。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容