13.2.4.1. INSERT ... SELECT语法
13.2.4.2. INSERT DELAYED语法
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    VALUES ({expr | DEFAULT},...),(...),...
    [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

或:

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name
    SET col_name={expr | DEFAULT}, ...
    [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

或:

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    SELECT ...
    [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

INSERT用于向一个已有的表中插入新行。INSERT...VALUESINSERT...SET形式的语句根据明确指定的值插入行。INSERT...SELECT形式的语句插入从其它表中选出的行。在13.2.4.1节,“INSERT ... SELECT语法”中对INSERT...SELECT进行了进一步的讨论。

行应被插入到tbl_name表中。可以按以下方法指定列。本语句向这些列提供值。

·         列名称清单或SET子句明确的指示了列。

·         如果您不为INSERT...VALUESINSERT...SELECT指定列的清单,则表中每列的值必须在VALUES清单中提供,或由SELECT提供。如果您不知道表中各列的顺序,则使用DESCRIBE tbl_name查询。

列值可以采用多种方法给定:

·         如果不是在严格模式下运行,则所有没有明确给定值的列都被设置为默认值(明确的或隐含的)。例如,如果您指定了一个列清单,但此清单没有对表中所有的列进行命名,则未命名的各列被设置为默认值。默认值的赋值在13.1.5节,“CREATE TABLE语法”中进行了说明。也可参见1.8.6.2节,“对无效数据的约束”

有时候,您需要对所有没有默认值的列明确地指定值。如果您希望,在没有明确指定值时,INSERT语句可以生成错误信息,则您应该使用STRICT模式。请参见5.3.2节,“SQL服务器模式”

·         使用关键词DEFAULT,明确地把列设置为默认值。这样,编写向所有列赋值的INSERT语句时可以更容易,因为使用DEFAULT可以避免编写出不完整的、未包含全部列值的VALUES清单。如果不使用DEFUALT,您必须编写一个列名称清单,与VALUES清单中的每个值对应。

您还可以使用DEFAULT(col_name)作为一种更通用的形式,在表达式中使用,用于生成一个列的默认值。

·         如果列清单和VALUES清单均为空清单,则INSERT会创建一个行,每个列都被设置为默认值:

·                mysql> INSERT INTO tbl_name () VALUES();

STRICT模式中,如果有一列没有默认值,则会出现错误。或者,MySQL会对所有没有明确定义默认值的列使用隐含的默认值。

·         您可以指定一个表达式expr来提供一个列值。如果表达式的类型与列值不匹配,这样做会造成类型转化。并且,给定值的转化会导致不同的插入值,插入何值由列类型而定。例如,向一个INT, FLOAT, DECIMAL(10,6)YEAR列插入字符串'1999.0e-2',插入值分别是199919.992119.9921001999。存储在INTYEAR列中的值为1999的原因是,在从字符串到整数的转化中,只把字符串的前面部分看作有效的整数或年份。对于浮点列和固定点列,在从字符串到浮点的转化中,把整个字符串均看作有效的浮点值。

表达式expr可以引用在值清单中已设置的所有列。例如,您可以这么操作,因为用于col2的值引用了col1,而col1已经被赋值:

mysql> INSERT INTO tbl_name (col1,col2) VALUES(15,col1*2);

但是以下语句不合法,因为用于col1的值引用了col2,而col2col1之后被赋值:

mysql> INSERT INTO tbl_name (col1,col2) VALUES(col2*2,15);

有一种例外情况,那就是含有AUTO_INCREMENT值的列。因为AUTO_INCREMENT值在其它值赋值之后被生成,所以任何在赋值时对AUTO_INCREMENT列的引用都会返回0

INSERT语句支持下列修改符:

·         如果您使用DELAYED关键字,则服务器会把待插入的行放到一个缓冲器中,而发送INSERT DELAYED语句的客户端会继续运行。如果表正在被使用,则服务器会保留这些行。当表空闲时,服务器开始插入行,并定期检查是否有新的读取请求。如果有新的读取请求,则被延迟的行被延缓执行,直到表再次空闲时为止。请参见13.2.4.2节,“INSERT DELAYED语法”

·         如果您使用LOW_PRIORITY关键词,则INSERT的执行被延迟,直到没有其它客户端从表中读取为止。当原有客户端正在读取时,有些客户端刚开始读取。这些客户端也被包括在内。此时,INSERT LOW_PRIORITY语句等候。因此,在读取量很大的情况下,发出INSERT LOW_PRIORITY语句的客户端有可能需要等待很长一段时间(甚至是永远等待下去)。(这与INSERT DELAYED形成对比,INSERT DELAYED立刻让客户端继续执行。请参见13.2.4.2节,“INSERT DELAYED语法”。)注意LOW_PRIORITY通常不应用于MyISAM表,因为这么做会取消同时进行的插入。请参见15.1节,“MyISAM存储引擎”

·         如果您指定了HIGH_PRIORITY,同时服务器采用--low-priority-updates选项启动,则HIGH_PRIORITY将覆盖--low-priority-updates选项。这么做还会导致同时进行的插入被取消。

·         使用mysql_affected_rows() C API函数,可以获得用于INSERT的受影响行的值。请参见25.2.3.1节,“mysql_affected_rows()”

·         如果您在一个INSERT语句中使用IGNORE关键词,在执行语句时出现的错误被当作警告处理。例如,没有使用IGNORE时,如果一个行复制了原有的UNIQUE索引或PRIMARY KEY值,会导致出现重复关键字错误,语句执行失败。使用IGNORE时,该行仍然未被插入,但是不会出现错误。IGNORE未被指定时,如果数据转化引发错误,则会使语句执行失败。使用IGNORE后,无效数据被调整到最接近的值,并被插入;此时,生成警告,但是语句执行不会失败。您可以使用mysql_info() C API函数测定有多少行被插入到表中。

如果您指定了ON DUPLICATE KEY UPDATE,并且插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值,则执行旧行UPDATE。例如,如果列a被定义为UNIQUE,并且包含值1,则以下两个语句具有相同的效果:

mysql> INSERT INTO table (a,b,c) VALUES (1,2,3)
    -> ON DUPLICATE KEY UPDATE c=c+1;
 
mysql> UPDATE table SET c=c+1 WHERE a=1;

如果行作为新记录被插入,则受影响行的值为1;如果原有的记录被更新,则受影响行的值为2

注释:如果列b也是唯一列,则INSERT与此UPDATE语句相当:

mysql> UPDATE table SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;

如果a=1 OR b=2与多个行向匹配,则只有一个行被更新。通常,您应该尽量避免对带有多个唯一关键字的表使用ON DUPLICATE KEY子句。

您可以在UPDATE子句中使用VALUES(col_name)函数从INSERT...UPDATE语句的INSERT部分引用列值。换句话说,如果没有发生重复关键字冲突,则UPDATE子句中的VALUES(col_name)可以引用被插入的col_name的值。本函数特别适用于多行插入。VALUES()函数只在INSERT...UPDATE语句中有意义,其它时候会返回NULL

示例:

mysql> INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
    -> ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

本语句与以下两个语句作用相同:

mysql> INSERT INTO table (a,b,c) VALUES (1,2,3)
    -> ON DUPLICATE KEY UPDATE c=3;
mysql> INSERT INTO table (a,b,c) VALUES (4,5,6)
    -> ON DUPLICATE KEY UPDATE c=9;

当您使用ON DUPLICATE KEY UPDATE时,DELAYED选项被忽略。

您可以使用SQL LAST_INSERT_ID()函数查找用于AUTO_INCREMENT列的值。从C API的内部,使用mysql_insert_id()函数。不过,您应该注意,两个函数的作用并不总是相同的。在12.9.3节,“信息函数”25.2.3.36节,“mysql_insert_id()”中进一步讨论了与AUTO_INCREMENT列有关的INSERT语句的作用。

如果您使用INSERT...VALUES语句时采用了多个值清单或INSERT...SELECT,则该语句按以下格式返回一个信息字符串:

Records: 100 Duplicates: 0 Warnings: 0

记录指示了经过语句处理的行的数目。(因为重复数目可以不是零,所以该数目不一定是实际被插入的行的数目。)重复数目指的是不能被插入的行的数目,因为这些行会复制部分原有的唯一索引值。警告指的是插入有错误或有问题的列值的次数。在以下情况下会出现警告:

·         向一个已定义为NOT NULL的列中插入NULL。对于一个多行INSERT语句或INSERT INTO...SELECT语句,根据列数据的类型,列被设置为隐含的默认值。对于数字类型,默认值为0;对于字符串类型,默认值为空字符串('');对于日期和时间类型,默认值为“zero”值。对INSERT INTO...SELECT语句的处理方法与对多行插入的处理方法一样,因为服务器不能检测来自SELECT的结果,不能判断是否返回单一行。(对于单一行INSERT,当NULL被插入一个NOT NULL列时,不会出现警告,而是出现错误,并且语句运行失败。)

·         数字列的值被设置在列的值范围之外。此值被修改为未最接近的值范围端点。

·         向一个数字列赋予一个例如'10.34 a'的值。尾部的非数字文本被删节,其余的数字部分被插入,如果字符串值没有前导的数字部分,则该列被设置为0

·         向一个字符串列(CHAR, VARCHAR, TEXTBLOB)中插入的字符串超过了列的最大长度。此值被删节到列的最大长度。

·         向日期或时间列中插入的值对于该列的类型是不合法的。根据列的类型,该列被设置到相应的零值。

如果您正在使用C API,则可以通过调用mysql_info()函数获取信息字符串。请参见25.2.3.34节,“mysql_info()”

13.2.4.1. INSERT ... SELECT语法

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    SELECT ...
    [ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

使用INSERT...SELECT,您可以快速地从一个或多个表中向一个表中插入多个行。

示例:

INSERT INTO tbl_temp2 (fld_id)
    SELECT tbl_temp1.fld_order_id
    FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;

使用INSERT...SELECT语句时会出现以下情况:

·         明确地指定IGNORE,用于忽略会导致重复关键字错误的记录。

·         不要同时使用DELAYEDINSERT...SELECT

·         INSERT语句的目标表会显示在查询的SELECT部分的FROM子句中。(在有些旧版本的MySQL中不会出现这种情况。)

·         AUTO_INCREMENT列照常运行。

·         为了确保二进制日志可以被用于再次创建原表,MySQL不允许在INSERT...SELECT运行期间同时进行插入操作。

·         目前,您不能在向一个表插入的同时,又在一个子查询中从同一个表中选择。

ON DUPLICATE KEY UPDATE的值部分中,只要您不使用SELECT部分中的GROUP BY,您就可以引用在其它表中的列。有一个副作用是,您必须使值部分中的非唯一列的名称符合要求。

您可以使用REPLACE替代INSERT,来覆盖旧行。对于包含唯一关键字值,并复制了旧行的新行,在进行处理时,REPLACE可以作为INSERT IGNORE的同类子句:新行被用于替换旧行,而不是被丢弃。

13.2.4.2. INSERT DELAYED语法

INSERT DELAYED ...

用于INSERT语句的DELAYED选项是MySQL相对于标准SQL的扩展。如果您的客户端不能等待INSERT完成,则这个选项是非常有用的。当您使用MySQL进行日志编写时,这是非常常见的问题。您也可以定期运行SELECTUPDATE语句,这些语句花费的时间较长。

当一个客户端使用INSERT DELAYED时,会立刻从服务器处得到一个确定。并且行被排入队列,当表没有被其它线程使用时,此行被插入。

使用INSERT DELAYED的另一个重要的好处是,来自许多客户端的插入被集中在一起,并被编写入一个块。这比执行许多独立的插入要快很多。

使用DELAYED时有一些限制:

·         INSERT DELAYED仅适用于MyISAM, MEMORYARCHIVE表。对于MyISAM表,如果在数据文件的中间没有空闲的块,则支持同时采用SELECTINSERT语句。在这些情况下,基本不需要对MyISAM使用INSERT DELAYED。请参见15.1节,“MyISAM存储引擎”, 15.4节,“MEMORY (HEAP)存储引擎”15.8节,“ARCHIVE存储引擎”

·         INSERT DELAYED应该仅用于指定值清单的INSERT语句。服务器忽略用于INSERT DELAYED...SELECT语句的DELAYED

·         服务器忽略用于INSERT DELAYED...ON DUPLICATE UPDATE语句的DELAYED

·         因为在行被插入前,语句立刻返回,所以您不能使用LAST_INSERT_ID()来获取AUTO_INCREMENT值。AUTO_INCREMENT值可能由语句生成。

·         对于SELECT语句,DELAYED行不可见,直到这些行确实被插入了为止。

·         DELAYED在从属复制服务器中被忽略了,因为DELAYED不会在从属服务器中产生与主服务器不一样的数据。

注意,目前在队列中的各行只保存在存储器中,直到它们被插入到表中为止。这意味着,如果您强行中止了mysqld(例如,使用kill -9)或者如果mysqld意外停止,则所有没有被写入磁盘的行都会丢失。

以下详细描述了当您对INSERTREPLACE使用DELAYED选项时会发生什么情况。在这些描述中,“线程”指的是已接受了一个INSERT DELAYED语句的线程,“管理程序”指的是为某个特定的表处理所有INSERT DELAYED语句的线程。

·         当一个线程对一个表执行DELAYED语句时,会创建出一个管理程序线程(如果原来不存在),对用于本表的所有DELAYED语句进行处理。

·         线程会检查是否管理程序以前已获取了DELAYED锁定;如果没有获取,则告知管理程序线程进行此项操作。即使其它线程对表有READWRITE锁定,也可以获得DELAYED锁定。但是管理程序会等待所有的ALTER TABLE锁定或FLUSH TABLE锁定,以确保表的结构是最新的。

·         线程执行INSERT语句,但不是把行写入表中,而是把最终行的拷贝放入一个由管理程序线程管理的队列中。线程会提示出现语法错误,这些错误会被报告到客户端中。

·         因为在插入操作之前,INSERT返回已经完成,所以客户端不能从服务器处获取重复记录的数目,也不能获取生成的行的AUTO_INCREMENT值。(如果您使用C API,则出于同样的原因,mysql_info()函数不会返回任何有意义的东西。)

·         当行被插入表中时,二进制日志被管理程序线程更新。在多行插入情况下,当第一行被插入时,二进制日志被更新。

·         每次delayed_insert_limit行被编写时,管理程序会检查是否有SELECT语句仍然未执行。如果有,则会在继续运行前,让这些语句先执行。

·         当管理程序的队列中没有多余的行时,表被解锁。如果在delayed_insert_timeout时间内,没有接收到新的INSERT DELAYED语句,则管理程序中止。

·         如果在某个特定的管理程序队列中,有超过delayed_queue_size的行未被执行,则申请INSERT DELAYED的线程会等待,直到队列中出现空间为止。这么做可以确保mysqld不会把所有的存储器都用于被延迟的存储队列。

·         管理程序线程会显示在MySQL进程清单中,其命令列中包含delayed_insert。如果您执行一个FLUSH TABLES语句或使用KILL thread_id进行删除,则会删除此线程。不过,在退出前,线程会首先把所有排入队列的行存储到表中。在这期间,该线程不会从其它线程处接受任何新的INSERT语句。如果您在此之后执行一个INSERT DELAYED语句,则会创建出一个新的管理程序线程。

注意,如果有一个INSERT DELAYED管理程序正在运行,则这意味着INSERT DELAYED语句比常规的INSERT语句具有更高的优先权。其它更新语句必须等待,直到INSERT DELAYED语句队列都运行完毕,或者管理程序线程被中止(使用KILL thread_id),或者执行了一个FLUSH TABLES时为止。

·         以下状态变量提供了有关INSERT DELAYED语句的信息:

状态变量

意义

Delayed_insert_threads

管理程序线程的数目

Delayed_writes

使用INSERT DELAYED写入的行的数目

Not_flushed_delayed_rows

等待被写入的行的数目

·         您可以通过发送一个SHOW STATUS语句,或者执行一个mysqladmin extended-status命令,来阅览这些变量。

注意,当没有使用表时,INSERT DELAYED比常规的INSERT要慢。对于服务器来说,为每个含有延迟行的表操纵一个独立的线程,也是一个额外的系统开销。这意味着只有当您确认您需要时,才应使用INSERT DELAYED