MySQL十部曲之六:数据操作语句(DML)

前言

本文翻译自官方文档。

语法约定

方括号[]表示可选词或语句,例如,在下面的语句中,IF EXISTS是可选的:

DROP DTABLE [IF EXISTS] tab1_name

当一个语法元素由多个备选项组成时,这些备选项由竖条|分隔。当可以从一组选项中选择一个成员时,备选项列在方括号内:

TRIM([[BOTH | LEADING | TRAILING] [remstr] FROM] str)

当必须从一组选项中选择一个成员时,备选项在大括号{}中列出:

{DESCRIBE | DESC} tbl_name [col_name | wild]

省略号表示省略语句的某一部分,通常是为了提供更复杂语法的更短版本。例如,SELECT…INTO OUTFILESELECT语句形式的简写。省略号还可以表示语句的前一个语法元素可以重复。在下面的示例中,可以给出多个reset_option值,每个值在第一个值之后以逗号开头:

RESET reset_option [,reset_option] ...

DELETE

DELETE是一条DML语句,用于从表中删除行,并返回删除的行数。

DELETE FROM tbl_name [[AS] tbl_alias]
    [WHERE where_condition]
    [ORDER BY ...]
    [LIMIT row_count]
  • WHERE:可选WHERE子句中的条件确定要删除哪些行。如果没有WHERE子句,则删除所有行。
  • ORDER BY:如果指定了ORDER BY子句,则按指定的顺序删除行。
  • LIMIT:LIMIT子句对可以删除的行数设置了限制。

DELETE语句注意事项如下:

  • 不能在子查询中从表中删除并从同一表中进行选择。
  • 如果删除包含AUTO_INCREMENT列最大值的行,则该值不会在MyISAM或InnoDB表中重用。
  • 如果在autocommit模式下使用DELETE FROM tbl_name删除表中的所有行,则除InnoDB和MyISAM外的所有存储引擎都将重新开始该顺序。

INSERT

INSERT语句将新行插入到现有表中。

INSERT 
    [INTO] tbl_name
    { VALUES (value_list) [, (value_list)] ... }

INSERT 
    [INTO] tbl_name
    SET assignment_list
	
INSERT 
    [INTO] tbl_name
    { SELECT ... 
      | TABLE table_name 
      | VALUES row_constructor_list
    }
	
value:
    {expr | DEFAULT}

value_list:
    value [, value] ...

row_constructor_list:
    ROW(value_list)[, ROW(value_list)][, ...]

assignment:
    col_name = 
          value
        | [tbl_name.]col_name

assignment_list:
    assignment [, assignment] ...

插入行的列和值可以通过以下规则对应:

  • tbl_name后面提供用括号括起来的以逗号分隔的列名列表。在这种情况下,每个指定列的值必须被提供。
  • 如果没有指定列名列表,所有列的值都必须按顺序被提供。
  • SET子句通过名称显式地指示列,以及为每个列分配的值。

列值可以通过几种方式给出:

  • 如果未启用严格SQL模式,则未显式给定值的任何列都将被设置为其默认值。如果启用严格SQL模式,如果INSERT语句没有为没有默认值的每个列指定显式值,则会生成错误。
  • 如果列名列表和值列表都是空的,INSERT将创建一行,每一列都设置为默认值:
INSERT INTO tbl_name () VALUES();
  • 使用关键字DEFAULT显式地将列设置为默认值。在表达式中,您可以使用DEFAULT(col_name)为列col_name生成默认值。
  • 如果表达式expr数据类型与列数据类型不匹配,MySQL将根据列类型进行类型转换。
  • 表达式expr可以引用先前在值列表中设置的任何列。例如,你可以这样做,因为col2的值引用了col1,它之前已经被赋值:
INSERT INTO tbl_name (col1,col2) VALUES(15,col1*2);
  • 引用包含AUTO_INCREMENT值的列会出现异常。因为AUTO_INCREMENT值是在其他值赋值之后生成的,所以对赋值中的AUTO_INCREMENT列的任何引用都会返回0。

插入语句会有以下返回值:

Records: N1 Duplicates: N2 Warnings: N3
  • Records:表示语句处理的行数。(这不一定是实际插入的行数)
  • Duplicate:表示无法插入的行数,因为它们会重复某些现有的唯一索引值。
  • Warnings:表示以某种方式出现问题的插入列值的尝试次数。

使用INSERT ... SELECT,可以从SELECT语句的结果中快速插入许多行到表中,SELECT语句可以从一个或多个表中进行选择。例如:

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

从MySQL 8.0.19开始,你可以用TABLE语句代替SELECT语句,如下所示:

INSERT INTO ta TABLE tb;

TABLE tb相当于SELECT * FROM tb。当将源表中的所有列插入目标表时,它非常有用,并且不需要使用WHERE进行过滤。此外,可以使用ORDER BY对来自TABLE的行按一个或多个列排序,并且可以使用LIMIT子句限制插入的行数。

SELECT

SELECT用于从一个或多个表中选择的行。

SELECT
    [ALL | DISTINCT]
    select_expr [, select_expr] ...
    [into_option]
    [FROM table_references
    [WHERE where_condition]
    [GROUP BY {col_name | expr}, ... [WITH ROLLUP]]
    [HAVING where_condition]
    [WINDOW window_name AS (window_spec) [, window_name AS (window_spec)] ...]
    [ORDER BY {col_name | expr} [ASC | DESC], ... [WITH ROLLUP]]
    [LIMIT {[offset,] row_count | row_count}]
    [into_option]
    [FOR {UPDATE | SHARE}
        [OF tbl_name [, tbl_name] ...]
        [NOWAIT | SKIP LOCKED]]
    [into_option]

into_option: {
    INTO OUTFILE 'file_name'
        [CHARACTER SET charset_name]
        export_options
  | INTO DUMPFILE 'file_name'
  | INTO var_name [, var_name] ...
}

SELECT还可以用于检索不引用任何表而计算出来的行。

mysql> SELECT 1 + 1;
        -> 2

在不引用表的情况下,允许指定DUAL作为虚拟表名。DUAL纯粹是为了方便那些要求所有SELECT语句都应该有FROM和其他可能的子句的人:

mysql> SELECT 1 + 1 FROM DUAL;
        -> 2

查询列表

select_expr子句包含一个查询列表,该列表指示要检索哪些列。该子句可以指定一个列或表达式,或者可以使用*简写:

  • 只包含一个不限定的*的查询列表可以用来从所有表中选择所有列:
  • tbl_name.*可以作为限定的简写来选择指定表中的所有列。
  • 如果一个表有不可见的列,*tbl_name.*将不会包含它们。

可以使用alias_nameselect_expr指定别名。别名用作表达式的列名,可以在GROUP BYORDER BYHAVING子句中使用。例如:

SELECT CONCAT(last_name,', ',first_name) AS full_name
  FROM mytable ORDER BY full_name;

SELECT 选项

ALLDISTINCT修饰符指定是否应该返回重复的行。ALL(默认值)指定应该返回所有匹配的行,包括重复的行。DISTINCT指定从结果集中删除重复行。同时指定两个修饰符是错误的。

STRAIGHT_JOIN强制优化器按照表在FROM子句中列出的顺序连接表。如果优化器以非最优顺序连接表,则可以使用它来加快查询速度。

子句

一般来说,所使用的子句必须完全按照语法描述中显示的顺序排列。

FROM

FROM table_references子句指示要从中检索行的表。如果指定了多个表,则执行连接查询。对于指定的每个表,您可以选择指定一个别名。

tbl_name [[AS] alias] [index_hint]

index_hint提供了有关如何在查询处理期间选择索引的信息。

可以将默认数据库中的表引用为tbl_namedb_name.tbl_name显式指定数据库。可以将列引用为col_nametbl_name.col_namedb_name.tbl_name.col_name。通常不需要这么做,除非该引用具有二义性。

WHERE

如果给定WHERE子句,则指示行必须满足的一个或多个条件才能被选中。where_condition是一个表达式,对于要选择的每一行,其计算结果为true。如果没有WHERE子句,语句将选择所有行。在WHERE表达式中,您可以使用MySQL支持的任何函数和操作符,除了聚合函数。

ORDER BY

ORDER BY用于排序要输出的列,可以在ORDER BY子句中使用列名和列别名:

SELECT college, region, seed FROM tournament
  ORDER BY region, seed;

SELECT college, region AS r, seed AS s FROM tournament
  ORDER BY r, s;

ORDER BY默认为升序,这可以使用ASC关键字显式指定。要按反向顺序排序,请将DESC关键字添加到要排序的ORDER BY子句中的列名的后边。

当您使用ORDER BYSELECT中的列进行排序时,服务器仅使用max_sort_length系统变量指示的初始字节数对值进行排序。

GROUP BY

GROUP BY子句用于将查询结果分组,SQL-92及更早版本的查询不允许在查询列表、HAVINGORDER BY内引用未在GROUP BY子句中使用的非聚合列。SQL:1999及以后版本在特定情况下允许使用非聚合列。

MySQL实现了功能依赖检测。如果启用了ONLY_FULL_GROUP_BY SQL模式(默认),MySQL会遵循SQL-92标准。

当启用了SQL的ONLY_FULL_GROUP_BY模式时,MySQL也允许没有在GROUP BY子句中命名的非聚合列,只要该列被限制为单个值,如下例所示:

mysql> CREATE TABLE mytable (
    ->    id INT UNSIGNED NOT NULL PRIMARY KEY,
    ->    a VARCHAR(10),
    ->    b INT
    -> );

mysql> INSERT INTO mytable
    -> VALUES (1, 'abc', 1000),
    ->        (2, 'abc', 2000),
    ->        (3, 'def', 4000);

mysql> SET SESSION sql_mode = sys.list_add(@@session.sql_mode, 'ONLY_FULL_GROUP_BY');

mysql> SELECT a, SUM(b) FROM mytable WHERE a = 'abc';
+------+--------+
| a    | SUM(b) |
+------+--------+
| abc  |   3000 |
+------+--------+

当使用ONLY_FULL_GROUP_BY时,也可能在SELECT列表中有多个非聚合列。在这种情况下,每个这样的列必须在WHERE子句中被限制为单个值,并且所有这样的限制条件必须通过逻辑与连接,如下所示:

mysql> DROP TABLE IF EXISTS mytable;

mysql> CREATE TABLE mytable (
    ->    id INT UNSIGNED NOT NULL PRIMARY KEY,
    ->    a VARCHAR(10),
    ->    b VARCHAR(10),
    ->    c INT
    -> );

mysql> INSERT INTO mytable
    -> VALUES (1, 'abc', 'qrs', 1000),
    ->        (2, 'abc', 'tuv', 2000),
    ->        (3, 'def', 'qrs', 4000),
    ->        (4, 'def', 'tuv', 8000),
    ->        (5, 'abc', 'qrs', 16000),
    ->        (6, 'def', 'tuv', 32000);

mysql> SELECT @@session.sql_mode;
+---------------------------------------------------------------+
| @@session.sql_mode                                            |
+---------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+---------------------------------------------------------------+

mysql> SELECT a, b, SUM(c) FROM mytable
    ->     WHERE a = 'abc' AND b = 'qrs';
+------+------+--------+
| a    | b    | SUM(c) |
+------+------+--------+
| abc  | qrs  |  17000 |
+------+------+--------+

如果禁用了ONLY_FULL_GROUP_BY,MySQL不在遵循SQL-92标准。在这种情况下,服务器可以从每个组中自由选择任何值,所以除非它们相同,否则所选择的值是不确定的,这可能不是你想要的。

HAVING

HAVING子句和WHERE子句一样,指定了选择条件。WHERE子句为查询列表中的列指定条件,但不能引用聚合函数。HAVING子句指定组的条件,组通常由GROUP BY子句指定。查询结果只包含满足HAVING条件的组。如果没有GROUP BY,所有行隐式地形成一个组。

SQL标准要求HAVING必须只引用GROUP BY子句中的列或聚合函数中使用的列。然而,MySQL支持对这种行为的扩展,并允许HAVING引用SELECT列表中的列和外部子查询中的列。

WINDOW

WINDOW子句定义了可由窗口函数引用的命名窗口。

LIMIT

LIMIT子句可用于约束SELECT语句返回的行数。LIMIT接受一个或两个数字参数,它们必须都是非负整数常量,但有以下例外:

使用两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。初始行的偏移量为0:

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15

要检索从某个偏移量到结果集末尾的所有行,可以为第二个参数使用较大的数字。这个语句检索从第96行到最后一行的所有行:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

通过一个参数,该值指定从结果集的开头返回的行数:

SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

FOR

如果对使用页锁或行锁的存储引擎使用FOR UPDATE,则查询检查的行将被写锁,直到当前事务结束。

FOR SHARE设置共享锁,允许其他事务读取检查的行,但不能更新或删除它们。

NOWAIT导致立即执行FOR UPDATEFOR SHARE查询,如果由于另一个事务持有的锁而无法获得行锁,则返回错误。

SKIP LOCKED导致立即执行FOR UPDATEFOR SHARE查询,排除结果集中被另一个事务锁定的行。

NOWAITSKIP LOCKED选项对于基于语句的复制是不安全的。

OF tbl_name适用于对命名表的UPDATESHARE查询。例如:

SELECT * FROM t1, t2 FOR SHARE OF t1 FOR UPDATE OF t2;

当省略OF tbl_name时,查询块所引用的所有表都被锁定。因此,将没有OF tbl_name的锁定子句与另一个锁定子句结合使用将返回错误。在多个锁子句中指定同一个表将返回错误。如果在SELECT语句中指定了别名作为表名,则锁子句只能使用该别名。如果SELECT语句没有显式指定别名,则锁定子句可能只指定实际的表名。

SELECT … INTO

SELECT…INTO形式可以将查询结果存储在变量或写入文件中。

  • SELECT ... INTO var_list :选择列值并将其存储到变量中。
  • SELECT ... INTO OUTFILE:将选定的行写入文件。可以指定列和行终止符以产生特定的输出格式。
  • SELECT ... INTO DUMPFILE:将单行写入文件而不进行任何格式化。

更多详情请看官方文档

连接查询

MySQL支持以下JOIN语法,用于SELECT语句和多表DELETEUPDATE语句的table_references部分:

table_references:
    table_reference[, table_reference] ...

table_reference: {
    table_factor
  | joined_table
}

table_factor: {
  [LATERAL] table_subquery [AS] alias [(col_list)]
  | ( table_references )
}

joined_table: {
	table_reference CROSS JOIN table_factor 
  | table_reference INNER JOIN  table_factor [join_specification]
  | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_specification
  | table_reference NATURAL [INNER | {LEFT|RIGHT} [OUTER]] JOIN table_factor
}

join_specification: {
    ON search_condition
  | USING (join_column_list)
}

join_column_list:
    column_name [, column_name] ...

index_hint_list:
    index_hint [, index_hint] ...

index_hint: {
    USE {INDEX|KEY}
      [FOR {JOIN|ORDER BY|GROUP BY}] ([index_list])
  | {IGNORE|FORCE} {INDEX|KEY}
      [FOR {JOIN|ORDER BY|GROUP BY}] (index_list)
}

index_list:
    index_name [, index_name] ...

下面的列表描述了编写JOIN时需要考虑的一般因素:

  • table_references表名可以使用别名tbl_name AS alias_name
  • table_subquery也称为FROM子句中的派生表或子查询。这样的子查询必须包含一个别名来为子查询结果提供一个表名,并且可以选择在括号中包含一个表列名列表。下面是一个简单的例子:
SELECT * FROM (SELECT 1, 2, 3) AS t1;
  • 在单个连接中可以引用的表的最大数量是61。这包括通过将FROM子句中的派生表和视图合并到外部查询块中来处理的连接。

请添加图片描述

CROSS JOIN和INNER JOIN

在标准SQL中,CROSS JOININNER JOIN的区别在于CROSS JOIN不能带有连接条件,而CROSS JOIN可以带有连接条件,除此之外,它们都会在指定的表之间产生笛卡尔积。但在MySQL中,CROSS JOININNOR JOINJOIN都是等价的,此外,,和它们在语义上也是等价的:

SELECT * FROM t1 LEFT JOIN (t2, t3, t4)
                 ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)

SELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4)
                 ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)

但逗号操作符的优先级是最低的,因此连接表达式t1, t2 JOIN t3被解释为(t1, (t2 JOIN t3)),而不是((t1, t2) JOIN t3)。这会影响使用ON子句的语句,因为该子句只能引用连接操作数中的列,并且优先级会影响对这些操作数的解释。

CREATE TABLE t1 (i1 INT, j1 INT);
CREATE TABLE t2 (i2 INT, j2 INT);
CREATE TABLE t3 (i3 INT, j3 INT);
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t2 VALUES(1, 1);
INSERT INTO t3 VALUES(1, 1);
SELECT * FROM t1, t2 JOIN t3 ON (t1.i1 = t3.i3);

JOIN优先于逗号操作符,因此ON子句的操作数是t2和t3,所以该语句是错误的。

同样的优先级解释也适用于将逗号操作符与INNER JOINCROSS JOINLEFT JOINRIGHT JOIN混合在一起的语句,所有这些语句的优先级都高于逗号操作符。

因此在编写语句时应该使用括号控制好优先级或避免使用逗号运算符。

ON和USING

ON一起使用的search_condition是可以在WHERE子句中使用的任何形式的条件表达式。通常,ON子句用于指定如何连接表的条件,而WHERE子句限制在结果集中包含哪些行。

USING(join_column_list)子句命名一个列列表,这些列必须同时存在于两个表中。如果表a和表b都包含c1、c2、c3列,下面的连接将比较两个表中对应的列:

a LEFT JOIN b USING (c1, c2, c3)

USING子句可以重写为比较对应列的ON子句。然而,尽管USINGON是相似的,但它们并不完全相同。考虑以下两个查询:

a LEFT JOIN b USING (c1, c2, c3)
a LEFT JOIN b ON a.c1 = b.c1 AND a.c2 = b.c2 AND a.c3 = b.c3

在确定哪些行满足连接条件方面,两个连接在语义上是相同的。

关于SELECT *展开显示哪些列,这两个连接在语义上并不相同。USING连接选择相应列的合并值,而ON连接选择所有表中的所有列。对于USING连接,SELECT *选择这些值:

COALESCE(a.c1, b.c1), COALESCE(a.c2, b.c2), COALESCE(a.c3, b.c3)

对于ON连接,SELECT *选择以下值:

a.c1, a.c2, a.c3, b.c1, b.c2, b.c3

使用内连接时,COALESCE(a.c1, b.c1)与a.c1或b.c1相同,因为两列具有相同的值。对于外部连接,两个列中的一个可以为NULL。该列将从结果中省略。

OUTER JOIN

如果在LEFT JOINONUSING部分中没有与右表匹配的行,则将所有列都设置为NULL的行用于右表。你可以使用这个事实来查找在另一个表中没有对应的行:

SELECT left_tbl.*
  FROM left_tbl LEFT JOIN right_tbl ON left_tbl.id = right_tbl.id
  WHERE right_tbl.id IS NULL;

RIGHT JOIN的工作原理类似于LEFT JOIN。为了保持代码跨数据库的可移植性,建议使用LEFT JOIN而不是RIGHT JOIN

NATURE JOIN

自然连接的冗余列不会出现,等价于使用USING的连接。MySQL根据标准SQL进行列合并和列排序,产生如下显示顺序:

  • 首先,按照第一个表中出现的顺序合并两个连接表的公共列
  • 第二,如果列在第一个表中是唯一的,按它们在该表中出现的顺序排列
  • 第三,如果列在第二个表中是唯一的,按它们在第二个表中出现的顺序排列

对于内连接,合并列定义的结果是:包含相同值列的某一列;对于外部连接,合并列定义的结果是:

  • 如果两列的值不同,则均包含
  • 如果两列的值相同,则包含某一列
  • 如果两列中有一列始终为NULL,则合并列包含非NULL列的值。

子查询

子查询是另一个语句中的SELECT语句。下面是子查询的一个例子:

SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);

在这个例子中,SELECT * FROM t1…是外部查询,(SELECT column1 FROM t2)是子查询。我们说子查询嵌套在外部查询中,实际上可以在其他子查询中嵌套更深的子查询。子查询必须始终出现在括号内。

子查询可以返回标量(单个值)、单行、单列或表。这些子查询称为标量子查询、列子查询、行子查询和表子查询。返回特定类型结果的子查询通常只能在某些上下文中使用。

标量子查询

在最简单的形式中,子查询是返回单个值的标量子查询。标量子查询是一个简单的操作数,您几乎可以在任何允许单列值或文字合法的地方使用它。例如:

CREATE TABLE t1 (s1 INT, s2 CHAR(5) NOT NULL);
INSERT INTO t1 VALUES(100, 'abcde');
SELECT (SELECT s2 FROM t1);

这个SELECT中的子查询返回单个值('abcde'),其数据类型为CHAR,长度为5,字符集和排序等于CREATE TABLE时的默认值。如果子查询结果为空,则结果为NULL。对于刚才显示的子查询,如果t1为空,结果将为NULL,即使s2不是NULL

使用子查询进行比较

通常使用以下形式使用子查询进行比较:

non_subquery_operand comparison_operator (subquery)

其中comparison_operator是这些操作符之一:

=  >  <  >=  <=  <>  !=  <=>

MySQL也允许这种结构:

non_subquery_operand LIKE (subquery)

带有ANY、IN或SOME的子查询

operand comparison_operator ANY (subquery)
operand IN (subquery)
operand comparison_operator SOME (subquery)

其中comparison_operator是这些操作符之一:

=  >  <  >=  <=  <>  != 

ANY关键字必须跟在比较运算符后面,表示“如果子查询返回的列中的任何一个值的比较为TRUE,则返回TRUE”。例如:

SELECT s1 FROM t1 WHERE s1 > ANY (SELECT s1 FROM t2);

当与子查询一起使用时,单词IN= ANY的别名。因此,这两个语句是相同的:

SELECT s1 FROM t1 WHERE s1 = ANY (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 IN    (SELECT s1 FROM t2);

单词SOMEANY的别名。

带有ALL的子查询

operand comparison_operator ALL (subquery)

单词ALL必须跟在比较运算符后面,表示“如果子查询返回的列中的所有值的比较都为TRUE,则返回TRUE”。例如:

SELECT s1 FROM t1 WHERE s1 > ALL (SELECT s1 FROM t2);

NOT IN<> ALL的别名。

行子查询

标量子查询或列子查询返回单个值或一列值。行子查询是返回单行的子查询变体,因此可以返回多个列值。行子查询比较的合法操作符是:

=  >  <  >=  <=  <>  !=  <=>

例如:

SELECT * FROM t1
  WHERE (col1,col2) = (SELECT col3, col4 FROM t2 WHERE id = 10);
SELECT * FROM t1
  WHERE ROW(col1,col2) = (SELECT col3, col4 FROM t2 WHERE id = 10);

带有EXISTS或NOT EXISTS的子查询

如果子查询返回任何行,则EXISTS子查询为TRUE, NOT EXISTS子查询为FALSE。例如:

SELECT column1 FROM t1 WHERE EXISTS (SELECT * FROM t2);

关联子查询

关联子查询是包含对出现在外部查询中的表的引用的子查询。例如:

SELECT * FROM t1
  WHERE column1 = ANY (SELECT column1 FROM t2
                       WHERE t2.column2 = t1.column2);

派生表

派生表是在查询FROM子句范围内生成表的表达式。例如,SELECT语句FROM子句中的子查询是派生表:

SELECT ... FROM (subquery) [AS] tbl_name ...

[AS] tbl_name子句是强制性的,因为FROM子句中的每个表都必须有一个名称。派生表中的任何列都必须具有唯一的名称。或者,tbl_name后面可以跟一个带括号的派生表列的名称列表:

SELECT ... FROM (subquery) [AS] tbl_name (col_list) ...

横向派生表

派生表通常不能在同一个FROM子句中引用前面表的列。从MySQL 8.0.14开始,派生表可以定义为横向派生表,以指定允许这样的引用。

SELECT
  salesperson.name,
  max_sale.amount,
  max_sale.customer_name
FROM
  salesperson,
  LATERAL
  (SELECT amount, customer_name
    FROM all_sales
    WHERE all_sales.salesperson_id = salesperson.id
    ORDER BY amount DESC LIMIT 1)
  AS max_sale;

子查询的限制

  • 通常,不能在修改表的同时在子查询中从同一表中进行选择。
  • 仅部分支持行比较操作
  • MySQL不支持某些子查询LIMIT

集操作(UNION、INTERSECT和EXCEPT)

SQL集操作将多个查询块的结果合并为单个结果。查询块是返回结果集的任何SQL语句,例如SELECT。SQL标准定义了以下三种集合操作:

  • UNION:将来自两个查询块的所有结果合并为单个结果,省略任何重复项。
  • INTERSECT:只合并两个查询块的结果相同的行,省略任何重复的行。
  • EXCEPT:对于两个查询块A和B,返回A中不存在于B中的所有结果,省略任何重复的结果。

这些集合操作符都支持一个ALL修饰符,该修饰符会导致结果中包含重复项。

所有三个集操作符还支持DISTINCT关键字,该关键字抑制结果中的重复项。由于这是集操作符的默认行为,因此通常不需要显式指定DISTINCT

你应该知道,在UNIONEXCEPT之前,INTERSECT会被求值。这意味着,例如,TABLE x UNION TABLE y INTERSECT TABLE z总是被计算为TABLE x UNION (TABLE y INTERSECT TABLE z)

集操作结果的列名取自第一个查询块的列名。

mysql> CREATE TABLE t1 (x INT, y INT);
Query OK, 0 rows affected (0.04 sec)

mysql> INSERT INTO t1 VALUES ROW(4,-2), ROW(5,9);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> CREATE TABLE t2 (a INT, b INT);
Query OK, 0 rows affected (0.04 sec)

mysql> INSERT INTO t2 VALUES ROW(1,2), ROW(3,4);
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> TABLE t1 UNION TABLE t2;
+------+------+
| x    | y    |
+------+------+
|    4 |   -2 |
|    5 |    9 |
|    1 |    2 |
|    3 |    4 |
+------+------+
4 rows in set (0.00 sec)

mysql> TABLE t2 UNION TABLE t1;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    3 |    4 |
|    4 |   -2 |
|    5 |    9 |
+------+------+
4 rows in set (0.00 sec)

要将ORDER BYLIMIT子句应用于作为联合、交集或其他集合操作的一部分的单个查询块,请将查询块括起来,将子句放在括号内,如下所示:

(SELECT a FROM t1 WHERE a=10 AND b=1 ORDER BY a LIMIT 10)
UNION
(SELECT a FROM t2 WHERE a=11 AND b=2 ORDER BY a LIMIT 10);

(TABLE t1 ORDER BY x LIMIT 10) 
INTERSECT 
(TABLE t2 ORDER BY a LIMIT 10);

要使用ORDER BY或LIMIT子句对集合操作的整个结果进行排序或限制,请将ORDER BY或LIMIT放在最后一条语句之后:

SELECT a FROM t1
EXCEPT
SELECT a FROM t2 WHERE a=11 AND b=2
ORDER BY a LIMIT 10;

TABLE t1
UNION 
TABLE t2
ORDER BY a LIMIT 10;

如果要排序的列有别名,ORDER BY子句必须引用别名,而不是列名。

UPDATE

UPDATE是一个DML语句,用于修改表中的行,返回实际更改的行数

UPDATE table_reference
    SET assignment_list
    [WHERE where_condition]
    [ORDER BY ...]
    [LIMIT row_count]

value:
    {expr | DEFAULT}

assignment:
    col_name = value

assignment_list:
    assignment [, assignment] ...

UPDATE语句用新值更新已命名表中现有行的列。SET子句指出要修改哪些列,以及应该给它们指定哪些值。每个值可以通过表达式给出,也可以通过关键字DEFAULT显式地设置列的默认值。如果给出了WHERE子句,它指定了标识要更新哪些行的条件。如果没有WHERE子句,则更新所有行。如果指定了ORDER BY子句,则按照指定的顺序更新行。LIMIT子句限制了可以更新的行数。

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亻乍屯页女子白勺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值