本文目錄: 1.insert和replace插入數據 1.1 insert into values() 1.2 insert into set 1.3 insert into select_statement 1.4 create table select 1.5 處理鍵值重覆的問題 1.6 完整的 ...
本文目錄:
1.insert和replace插入數據
1.1 insert into values()
1.2 insert into set
1.3 insert into select_statement
1.4 create table select
1.5 處理鍵值重覆的問題
1.6 完整的insert語法
2.導出、導入數據
2.1 select into outfile導出數據
2.2 load data infile導入數據
2.3 mysqldump導出數據
2.4 mysqlimport導入數據
3.如何快速插入大量數據
1.insert和replace插入數據
先解釋insert。
insert插入數據有三種語法,簡潔版如下:
1.使用values()或value():insert into table_name values(),(),(),()...
2.使用set子句:insert into table_name set column_name=value,...
3.使用select子句:insert into table_name select_statement
第二種語法是MySQL/MariaDB對標準SQL insert語句的擴展。
1.1 insert into values()
給定如下表結構:
create or replace table t( id int primary key, sex char(3) default('nv'), name char(20) );
其中欄位'sex'含有預設值屬性,其預設值為'nan',name欄位允許NULL,這也是該欄位的預設值。
用insert插入幾行數據:
insert into t values(1,'nan','longshuai1'); # 插入一行數據 insert into t values(2,'nan','longshuai2'),(3,'nv','xiaofang1'); # 一次性插入兩行數據 insert into t values(4,DEFAULT,'xiaofang2'); # sex欄位使用預設值 insert into t(id,name) values(5,'xiaofang3'); # 指定插入的欄位 insert into t(id,sex,name) values(6,'nv','xiaofang4'); # 等價於insert into t values() insert into t(name,id) values('xiaofang5',7); # 指定插入的欄位,且改變欄位順序 insert into t value(8,'nan','longshuai3'); # 使用value,而非values
查看插入的數據:
MariaDB [test]> select * from t; +----+------+------------+ | id | sex | name | +----+------+------------+ | 1 | nan | longshuai1 | | 2 | nan | longshuai2 | | 3 | nv | xiaofang1 | | 4 | nv | xiaofang2 | | 5 | nv | xiaofang3 | | 6 | nv | xiaofang4 | | 7 | nv | xiaofang5 | | 8 | nan | longshuai3 | +----+------+------------+
註意,MySQL/MariaDB中預設變數autocommit=1,這意味著InnoDB表每次insert(以及其他的DML語句)都會自動提交事務,提交事務意味著會將臟數據刷到磁碟,這很影響插入效率。
insert into t values(),(),()一次插入多個值的效率比分多次執行insert效率要高得多。不僅不用頻繁地打開、關閉連接,還能將要插入的數據緩存起來,一定時間之後再一次性刷到磁碟。
關於如何快速插入數據到MariaDB/MySQL中,見後文:如何快速插入大量數據。
1.2 insert into set
insert into set語法是使用set子句為每個欄位設置值。所以,欄位的順序沒有任何要求。
例如:
insert into t set name='longshuai4',id=9,sex='nan';
1.3 insert into select_statement
insert into tbl_name select_statement語法是從其他表中檢索數據,並將檢索到的數據插入到表tbl_name中。要求tbl_name必須已經存在。
例如:
insert into t select 10,'nan','longshuai5'; insert into t select 11,'nan','longshuai6' from dual; insert into t select 12,'nv','xiaofang6' union select 13,'nan','longshuai7';
如果從其他表中檢索數據,則select檢索的欄位需要和tbl_name表的欄位對應。
create or replace table tx(user char(20),host char(20),password char(50)); insert into tx select user,host,password from mysql.user;
如果表tbl_name不存在,則insert into tbl_name select_statement語句會失敗。此時可以使用create table select的方式來創建表的同時插入數據。
1.4 create table select
創建表並插入數據兩種語法:
create table tbl_name select_statement create table tbl_name as select_statement
這些語句檢索數據,並按照檢索目標欄位新建一張表,表必須不能已經存在,除非使用or replace或者if not exists子句。
# 創建新表,並插入幾條數據 create or replace table t20 select user,host,password from mysql.user where user='root';
只創建表結構,不插入數據:
create table tbl_name1 like tbl_name2 # 創建完全相同的表結構 create table tbl_name select.col1,col2,col3.where 1=0; # where false。可以篩選部分欄位作為新表的結構
例如:
create table t10 like mysql.user; # 以mysql.user表為模板創建t10表 create table t11 select user,host,password from mysql.user where false; # 選出3個欄位創建新表 create table t12(col1 char(20),col2 char(20),col3 char(50)) as # 選出3個欄位,但自定義新表的欄位名稱 select user,host,password from mysql.user where 1=0;
需要註意:
create table [as] select_statement創建表的時候,只會創建幾個欄位並插入一些數據,不會複製模板表欄位的屬性,例如索引、預設值、auto_increment等。
create table like創建表的時候,新表和模板表的結構會完全相同,包括欄位的屬性(如default屬性、auto_increment屬性、索引等)。實際上,它是根據模板表的".frm"文件新建的。
1.5 處理鍵值重覆的問題
當表中存在唯一性索引(例如primary key,unique index)時,插入的記錄如果鍵值重覆,則插入操作會失敗。MariaDB中有三種方法解決重覆值衝突的問題:
- 使用ignore關鍵字忽略所有錯誤行,使insert操作繼續插入後面的數據。
- 使用insert ... on duplicate key update,將有重覆值的行update為新的值。
- 使用replace into語句替代insert into語句,將有重覆值的行替換為新行。
例如,創建帶有主鍵的表,並插入幾行數據。
create or replace table t(id int primary key,sex char(3),name char(20)); insert into t values (1,'nan','longshuai1'),(2,'nan','longshuai2'),(3,'nv','xiaofang1'), (4,'nv','xiaofang2'),(5,'nv','xiaofang3'),(6,'nv','xiaofang4'), (7,'nv','xiaofang5'),(8,'nan','longshuai3'),(9,'nan','longshuai4');
當插入id=5的新記錄,預設會報錯,如果是多值插入,所有的插入都會失敗。
insert into t values(5,'nv','xiaofang33'),(10,'nan','longshuai5'); ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY' select * from t where id=5 or id=10; +----+------+-----------+ | id | sex | name | +----+------+-----------+ | 5 | nv | xiaofang3 | +----+------+-----------+
以下是三種解決重覆值衝突的方法。
1.ignore關鍵字,忽略將要重覆的記錄。
完整語法見下文。此處只給使用示例。
insert ignore into t values(5,'nv','xiaofang33'),(10,'nan','longshuai5'); Query OK, 1 row affected, 1 warning (0.003 sec) Records: 2 Duplicates: 1 Warnings: 1
從返回中可以看到:1 row affected, 1 warning。說明只成功插入了一行記錄,id=5的重覆記錄被忽略直接跳過了。
show warnings; +---------+------+---------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------+ | Warning | 1062 | Duplicate entry '5' for key 'PRIMARY' | +---------+------+---------------------------------------+ select * from t where id=5 or id=10; +----+------+------------+ | id | sex | name | +----+------+------------+ | 5 | nv | xiaofang3 | | 10 | nan | longshuai5 | +----+------+------------+
2.ON DUPLICATE KEY UPDATE。
可以在INSERT語句後面加上on duplicate key update子句來修改重覆值記錄。
這有兩種情況:(1)插入的記錄沒有鍵值重覆衝突;(2)插入的記錄有鍵值重覆衝突。
對於第一種情況,INSERT語句中帶有on duplicate key update子句並沒有任何區別,它們都只是純粹地insert數據。
對於第二種情況,INSERT語句中帶有on duplicate key update子句會更新表中原有的記錄。
例如:
# 插入無鍵值重覆衝突的記錄 insert into t values(11,'nv','xiaofang4'),(12,'nan','longshuai6'); # 插入鍵值重覆衝突的記錄id=5,更新其name insert into t values(5,'nv','xiaofang33'),(13,'nv','xiaofang5') on duplicate key update name='xiaofang333'; Query OK, 3 rows affected (0.002 sec) Records: 2 Duplicates: 1 Warnings: 0
3行受到影響,其中1行重覆值。這裡的3行記錄中,有1行是插入id=13的記錄,另兩行是因為id=5重覆而受到的影響,涉及的操作是將舊id=5行update為新id=5的行。
select * from t where id=5 or id=13; +----+------+-------------+ | id | sex | name | +----+------+-------------+ | 5 | nv | xiaofang333 | | 13 | nv | xiaofang5 | +----+------+-------------+
可以在UPDATE子句中使用VALUES(col_name)函數從INSERT...ON DUPLICATE KEY UPDATE語句的INSERT部分引用列值。例如:
insert into t values(5,'nv','xiaofang33') on duplicate key update name=concat('xiaofang',values(id)); select * from t where id=5; +----+------+-----------+ | id | sex | name | +----+------+-----------+ | 5 | nv | xiaofang5 | +----+------+-----------+
VALUES()函數只在INSERT...ON DUPLICATE KEY UPDATE語句中有意義,其它時候會返回NULL。
INSERT...ON DUPLICATE KEY UPDATE語句的執行原理是:
(1).嘗試插入新行,判斷新行是否和表中已有記錄存在鍵值衝突。此時會觸發before insert觸發器。
(2).如果沒有衝突,就直接插入新行,此時會觸發after insert觸發器。
(3).如果有衝突,則更新舊行為新行。此時會先觸發before update觸發器,更新後觸發after update觸發器。
所以,當有重覆值衝突的時候,將觸發before insert、before update和after update觸發器。詳細內容見:on duplicate key update分析觸發器觸發原理。
3.replace into語句,更新重覆值所在的記錄行。
replace into和insert into語法完全一樣,可以完全替代insert into語句。它們之間只有存在鍵值重覆衝突的時候才有區別。
當沒有鍵值重覆衝突時,replace into和insert into完全等價。
當鍵值重覆衝突時,replace into語句會將表中產生衝突的記錄完全替換為新行。
replace into t values(5,'nv','xiaofang33'); select * from t where id=5; +----+------+------------+ | id | sex | name | +----+------+------------+ | 5 | nv | xiaofang33 | +----+------+------------+
replace into語句的執行原理是:
(1).嘗試插入新行,判斷新行是否和表中已有記錄存在鍵值衝突。此時會觸發before insert觸發器。
(2).如果沒有衝突,就直接插入新行,此時會觸發after insert觸發器。
(3).如果有衝突,則刪除舊行,並插入新行。此時會觸發before delete觸發器,刪除後觸發after delete觸發器,最後插入數據後觸發after insert觸發器。詳細內容見:replace into演算法分析。
1.6 完整的insert語法
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [PARTITION (partition_list)] [(col,...)] {VALUES | VALUE} ({expr | DEFAULT},...),(...),... [ ON DUPLICATE KEY UPDATE col=expr [, col=expr] ... ] 或: INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [PARTITION (partition_list)] SET col={expr | DEFAULT}, ... [ ON DUPLICATE KEY UPDATE col=expr [, col=expr] ... ] 或: INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [PARTITION (partition_list)] [(col,...)] SELECT ... [ ON DUPLICATE KEY UPDATE col=expr [, col=expr] ... ]
其中on duplicate key update子句是在出現重覆值衝突時使用的,前文已介紹。
IGNORE關鍵字的作用是忽略錯誤。當insert語句插入記錄時,如果某行出現錯誤(例如不滿足數據類型、重覆值衝突等等),則INSERT語句不會停止,而是繼續插入後面的記錄。前文已介紹通過ignore解決鍵值重覆衝突的問題。
DELAYED關鍵字設置INSERT的非同步插入。當使用該關鍵字時,服務端立即返回OK給客戶端,然後服務端將插入操作放進隊列,等待沒有其他進程訪問該表的時候才會將隊列中的數據插入到表中。在表被頻繁訪問的時候插入數據,使用DELAYED的效率比直接使用INSERT高,但如果沒有任何進程訪問表的時候,INSERT DELAYED比直接INSERT速度慢,因為MariaDB需要額外的資源來維護這個隊列。如果某一時刻有進程訪問該表,則多個客戶端同時使用INSERT DELAYED插入數據的時候,這些數據會全部放進隊列,效率比各客戶端分開插入要高,因為隊列中的數據是成批插入的。如果隊列中的數據還沒插入到表中,突然mysqld進程死掉了,則隊列中的數據會丟失,即沒有插入到表中。
LOW_PRIORITY和HIGH_PRIORITY關鍵字的意義在見(MariaDB/MySQL)MyISAM存儲引擎讀、寫操作的優先順序。
2.導出、導入數據
load data infile和select into outfile語句是配套的。select into outfile語句是將檢索出來的數據按格式導出到文件中,數據遷移跨資料庫系統時,該選項很有用,因為它可以指定分隔符。load data infile是將帶有格式的數據文件導入到表中。
導出、導入數據時需要指定格式(如不指定,則使用預設)。格式涉及幾個方面:欄位分隔符、行分隔符、引用符號、轉義符號。
還需註意一點,預設情況下(MySQL 5.6.34之後)這兩個語句無法執行成功,因為全局變數secure_file_priv的預設值為null,它表示禁用這兩種語句的導入導出。
所以應該將其設置為空(不指定任何值)或者指定一個目錄,將來該目錄中的所有文件都可以進行mysql file類的交互。當然,變數指定的目錄必須已經存在,且mysql系統用戶和組必須對該目錄有讀寫許可權。
mkdir /data chown -R mysql.mysql /data
這個變數是全局靜態變數,只能在mysqld實例未啟動的時候才能修改。所以將其寫入配置文件。
[mysqld] secure-file-priv=/data # 或者 # secure-file-priv=
查看變數。
select @@global.secure_file_priv; +---------------------------+ | @@global.secure_file_priv | +---------------------------+ | /data/ | +---------------------------+
再看這兩個語句的語法:
SELECT ... INTO OUTFILE 'file_name' [CHARACTER SET charset_name] [export_options] LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name' [REPLACE | IGNORE] INTO TABLE tbl_name [CHARACTER SET charset_name] [export_options] [IGNORE number {LINES|ROWS}] [(col_name_or_user_var,...)] [SET col_name = expr,...] export_options: [{FIELDS | COLUMNS} [TERMINATED BY 'string'] [[OPTIONALLY] ENCLOSED BY 'char'] [ESCAPED BY 'char'] ] [LINES [STARTING BY 'string'] [TERMINATED BY 'string'] ]
其中'char'表示只能使用一個字元,'string'表示可以指定多個字元。
fields terminated by 'string'指定欄位分隔符;enclosed by 'char'指定所有欄位都使用char符號包圍,如果指定了optionally則只用在字元串和日期數據類型等欄位上,預設未指定;escaped by 'char'指定轉義符。
lines starting by 'string'指定行開始符,如每行開始記錄前空一個製表符;lines terminated by 'string'為行分隔符。
要註意,在幾種情況下需要使用轉義符:數據中含有轉義符本身或者欄位分隔符。當指定了欄位引用符enclosed by時,如果數據中含有欄位引用符,則也需要轉義,若未指定enclosed by,則預設不使用欄位引用符,所以無需轉義。
以下為它們的預設值:
fileds terminated by '\t' enclosed by '' escaped by '\\' lines terminated by '\n' starting by ''
看上去語法還挺複雜的,使用示例來說明就很清晰易懂了。
給定如下表結構和數據。
create or replace table t(id int primary key,sex char(3),name char(20),ins_day date); insert into t values(1,'nan','longshuai1','2010-04-19'), (2,'nan','longshuai2','2011-04-19'), (3,'nv','xiaofang1','2012-04-19'), (4,'nv','xiaofang2','2013-04-19'), (5,'nv','xiaofang3','2014-04-19'), (6,'nv','xiaofang4','2015-04-19'), (7,'nv','tun\'er','2016-04-19'), (8,'nan','longshuai3','2017-04-19');
2.1 select into outfile導出數據
使用預設設置:
select * from t into outfile '/data/t_data.sql'; \! cat /data/t_data.sql 1 nan longshuai1 2010-04-19 2 nan longshuai2 2011-04-19 3 nv xiaofang1 2012-04-19 4 nv xiaofang2 2013-04-19 5 nv xiaofang3 2014-04-19 6 nv xiaofang4 2015-04-19 7 nv tun'er 2016-04-19 8 nan longshuai3 2017-04-19
指定欄位分隔符",",使用單引號包圍各欄位,每行前加上製表符。
select * from t into outfile '/data/t_data1.sql' fields terminated by ',' enclosed by '\'' lines starting by '\t' terminated by '\n'; \! cat /data/t_data1.sql '1','nan','longshuai1','2010-04-19' '2','nan','longshuai2','2011-04-19' '3','nv','xiaofang1','2012