13.2.9 SELECT 语法

以下内容均翻译自 MySQL 5.6 Reference Manual 来自一个四级都还没过的渣渣 通过阅读官方文档加深对一些sql注入的理解

13.2.9.1 SELECT ... INTO Syntax
13.2.9.2 JOIN Syntax
13.2.9.3 UNION Syntax

SELECT
    [ALL | DISTINCT | DISTINCTROW ]
      [HIGH_PRIORITY]
      [STRAIGHT_JOIN]
      [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
      [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
    select_expr [, select_expr ...]
    [FROM table_references
      [PARTITION partition_list]
    [WHERE where_condition]
    [GROUP BY {col_name | expr | position}
      [ASC | DESC], ... [WITH ROLLUP]]
    [HAVING where_condition]
    [ORDER BY {col_name | expr | position}
      [ASC | DESC], ...]
    [LIMIT {[offset,] row_count | row_count OFFSET offset}]
    [PROCEDURE procedure_name(argument_list)]
    [INTO OUTFILE 'file_name'
        [CHARACTER SET charset_name]
        export_options
      | INTO DUMPFILE 'file_name'
      | INTO var_name [, var_name]]
    [FOR UPDATE | LOCK IN SHARE MODE]]

SELECT用于从一个或多个表中取回行,可以包含UNION语句和子查询。参见第13.2.9.3节,UNION语法,和第13.2.10节,Subquery语法。

SELECT语句最常用的子句是:

  • 每个select_expr指示要检索的列。 必须至少有一个select_expr。

  • table_references指示要从中检索行的一个或多个表。 其语法在第13.2.9.2节“JOIN语法”中描 述。

  • SELECT支持使用partition关键字进行显式分区选择,在table_reference中的表名后面有一个分区或子分区列表(或两者都有)(请参见第13.2.9.2节“联接语法”)。在这种情况下,只从列出的分区中选择行,而忽略表中的任何其他分区。有关更多信息和示例,请参见第19.5节“分区选择”。

  • SELECT ... PARTITION from tables using storage engines such as MyISAM that perform table-level locks (and thus partition locks) lock only the partitions or subpartitions named by the PARTITION option.

    For more information, see Section 19.6.4, “Partitioning and Locking”.

  • WHERE子句(如果给定)指示要选择行必须满足的一个或多个条件。where_condition是一个表达式,对于要选择的每一行,其计算结果为true。如果没有WHERE子句,该语句将选择所有行。

    在WHERE表达式中,您可以使用MySQL支持的任何函数和运算符,聚合(摘要)函数除外。参见第9.5节“表达式”和第12章“函数和运算符”。

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

例如:

mysql> SELECT 1 + 1;
        -> 2

在没有引用表的情况下,允许将DUAL指定为虚拟表名:

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

DUAL纯粹是为了方便那些要求所有SELECT语句都应该包含FROM和其他子句的人。MySQL可能会忽略这些子句,如果没有引用表,MySQL并不依赖于FROM DUAL。

通常,使用的子句必须完全按照语法描述中显示的顺序给出。 例如,HAVING子句必须位于任何GROUP BY子句之后和任何ORDER BY子句之前。 例外情况是INTO子句可以如语法描述中所示出现,也可以紧跟在select_expr列表之后出现。 有关INTO的更多信息,请参见第13.2.9.1节“SELECT ... INTO语法”。

select_expr列表术语包含选择列表,该列表指示要检索哪些列。术语指定列或表达式,或者可以使用 * 来简写

  • 一个只包含一个非限定 * 的选择列表可以用作从所有表中选择所有列的简写
    SELECT * FROM t1 INNER JOIN t2 ...
    
  • tbl_name.* 可用作限定简写,从指定表中选择所有列:
    SELECT t1.*, t2.* FROM t1 INNER JOIN t2 ...
    
  • 与选择列表中的其他项一起使用非限定可能会产生解析错误。为了避免这个问题,请使用限定的tbl_name。引用
    SELECT AVG(score), t1.* FROM t1 ...
    

以下内容提供了有关其他SELECT子句的附加信息:

  • 可以使用 AS alias_nameselect expr提供别名。别名用作表达式的列名,可以在GROUP BYORDER BYHAVING子句中使用。例如

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

    当将select_expr与标识符混叠时,AS关键字是可选的。前面的例子可以写成这样

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

    但是,由于AS是可选的,如果忘记两个select expr表达式之间的逗号,就会出现一个微妙的问 题:MySQL将第二个表达式解释为别名。例如,在下面的语句中,columnb被视为别名

    SELECT columna columnb FROM mytable;
    

    因此,在指定列别名时,养成明确使用AS的习惯是一个很好的做法。

    不允许在WHERE子句中引用列别名,因为在执行WHERE子句时可能还没有确定列值。参见B.4.4.4节,列别名的问题。

  • FROM table_references子句指示从中检索行的一个或多个表。如果您指定了多个表,则表示您正在执行JOIN。有关JOIN语法的信息,请参见13.2.9.2节“JOIN语法”。对于指定的每个表,您可以选择指定别名。

    tbl_name [[AS] alias] [index_hint]
    

    index_hint的使用为优化器提供了关于如何在查询处理过程中选择索引的信息。有关指定这些提示的语法描述,请参见第8.9.3节“索引提示”。

    您可以使用SET max seek for key=value作为另一种强制MySQL选择键扫描而不是表扫描的方法。参见第5.1.7节,服务器系统变量。

  • 您可以将缺省数据库中的表称为tbl_name,或将db_name.tbl_name称为显式指定数据库。 您可以将列引用为col_nametbl_name.col_namedb_name.tbl_name.col_name。 除非引用不明确,否则无需为列引用指定tbl_namedb_name.tbl_name前缀。 有关需要更明确的列引用形式的歧义的示例,请参见第9.2.1节“标识符限定符”。

  • 引用表时可以使用 tbl_name AS alias_name 或者 tbl_name alias_name 给其指定别名,这些语句是等效的:

    SELECT t1.name, t2.salary FROM employee AS t1, info AS t2
      WHERE t1.name = t2.name;
    
    SELECT t1.name, t2.salary FROM employee t1, info t2
      WHERE t1.name = t2.name;
    
  • 选择输出的列可以使用列名、列别名或列位置在ORDER BY和GROUP BY子句中引用。列位置是整数,以1开始:

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

    要按相反顺序排序,请将DESC(降序)关键字添加到要排序的ORDER BY子句中的列名称后。 默认为升序; 这可以使用ASC关键字明确指定。

    如果ORDER BY出现在子查询中并且也应用于外部查询,则最外面的ORDER BY优先。例如,以下语句的结果按降序排列,而不是升序排列:

    (SELECT ... ORDER BY a) ORDER BY a DESC;
    

    不建议使用列位置,因为该语法已从SQL标准中删除。

  • MySQL扩展了GROUP BY子句,因此您还可以在子句中指定的列之后指定ASCDESC

    SELECT a, COUNT(b) FROM test_table GROUP BY a DESC;
    
  • 如果使用GROUP BY,输出行将根据GROUP BY列进行排序,就好像对相同的列都有一个ORDER BY一样。为了避免GROUP BY排序产生的开销,请添加ORDER BY NULL

    SELECT a, COUNT(b) FROM test_table GROUP BY a ORDER BY NULL;
    

    不赞成依赖隐式GROUP BY排序(即,在没有ASC或DESC指示符的情况下进行排序)。要生成给定的排序顺序,请为“GROUP BY”列使用显式的ASC或DESC指示符,或者提供ORDER BY子句。

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

  • MySQL扩展了GROUP BY的使用,允许选择GROUP BY子句中未提及的字段。 如果您没有从查询中获得预期的结果,请阅读第12.19节“聚合(GROUP BY)函数”中的GROUP BY说明。

  • GROUP BY允许WITH ROLLUP修饰符。 请参见第12.19.2节“GROUP BY Modifiers”。

  • HAVING子句几乎是最后应用的,就在项目发送到客户机之前,没有进行优化。(LIMITHAVING之后应用)

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

    如果HAVING子句引用了不明确的列,则会出现警告。在下面的语句中,col2不明确,因为它同时用作别名和列名:

    SELECT COUNT(col1) AS col2 FROM t GROUP BY col2 HAVING col2 = 2;
    

    优先考虑标准SQL行为,因此如果HAVING使用的列名同时出现在GROUP BY和输出列列表使用的别名中,则会优先选择GROUP BY列中的列名。

  • 不要对应该出现在WHERE子句中的项使用HAVING。例如,不要写下面的内容

    SELECT col_name FROM tbl_name HAVING col_name > 0;
    

    改为写这个:

    SELECT col_name FROM tbl_name WHERE col_name > 0;
    
  • HAVING子句可以引用聚合函数,而WHERE子句不能

    SELECT user, MAX(salary) FROM users
      GROUP BY user HAVING MAX(salary) > 10;
    

    (这在一些较老版本的MySQL中并不适用。)

  • MySQL允许重复的列名。 也就是说,可以有多个具有相同名称的select_expr。 这是标准SQL的扩展。 因为MySQL还允许GROUP BY和HAVING引用select_expr值,这可能会导致歧义:

    SELECT 12 AS a, a FROM t GROUP BY a;
    

    在该语句中,两列的名称都为a。为了确保分组时使用正确的列,请为每个select_expr使用不同的名称。

  • MySQL通过搜索select_expr值,然后在FROM子句中的表的列中搜索ORDER BY子句中的非限定列或别名引用。 对于GROUP BY或HAVING子句,它在搜索select_expr值之前搜索FROM子句。 (对于GROUP BY和have,这与mysql 5.0之前的行为不同,后者使用与ORDER BY相同的规则。)

  • LIMIT子句可用于约束SELECT语句返回的行数。LIMIT接受一个或两个数值参数,除了这些例外,它们都必须是非负整数常量

    • 在prepared statements中,LIMIT参数可以使用?占位符标记。
    • 在stored programs中,LIMIT参数可以使用integer-valued routine parameters或局部变量来指定。
      使用两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。第一行的偏移量是0(不是1)
    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
    

    换句话说,LIMIT row_count相当于LIMIT 0,row_count。

    对于prepared statements,可以使用占位符。下面的语句将从tbl表返回一行

    SET @a=1;
    PREPARE STMT FROM 'SELECT * FROM tbl LIMIT ?';
    EXECUTE STMT USING @a;
    

    以下语句将返回tbl表的第二至第六行:

    SET @skip=1; SET @numrows=5;
    PREPARE STMT FROM 'SELECT * FROM tbl LIMIT ?, ?';
    EXECUTE STMT USING @skip, @numrows;
    

    为了与PostgreSQL兼容,MySQL还支持LIMIT row_count OFFSET offset语法。

    果LIMIT出现在子查询中并且也应用于外部查询,则最外面的LIMIT优先。例如,以下语句产生两行,而不是一行:

    (SELECT ... LIMIT 1) LIMIT 2;
    
  • PROCEDURE子句指定应处理结果集中数据的过程。 有关示例,请参见第8.4.2.4节“使用PROCEDURE ANALYZE”,其中介绍了ANALYZE,该过程可用于获取可能有助于减小表大小的最佳列数据类型的建议。

    UNION语句中不允许使用PROCEDURE子句。

  • SELECT的SELECT ... INTO形式使查询结果能够写入文件或存储在变量中。 有关更多信息,请参见第13.2.9.1节“SELECT ... INTO语法”。

  • 如果对使用页锁或行锁的存储引擎使用FOR UPDATE,则查询检查的行将被写入锁定,直到当前事务结束。 使用LOCK IN SHARE MODE设置共享锁,允许其他事务读取已检查的行,但不允许更新或删除它们。 请参见第14.7.2.4节“锁定读取”。

    此外,您不能在诸如CREATE TABLE new_table SELECT ... FROM old_table ....之类的语句中使用FOR UPDATE作为SELECT的一部分。(如果您尝试这样做,语句将被拒绝,并显示错误 Can't update table 'old_table' while 'new_table' is being created.)这是MySQL 5.5及更早版本的行为更改,它允许CREATE TABLE ... SELECT语句在正在创建的表之外的表中进行更改。

在SELECT关键字之后,您可以使用许多影响语句操作的修饰符。 HIGH_PRIORITY(高优先级),STRAIGHT_JOIN(直连接)和以SQL_开头的修饰符是标准SQL的MySQL扩展。

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

  • 高优先级赋予SELECT比更新表的语句更高的优先级。您应该只将它用于非常快速且必须立即完成的查询。即使有更新语句等待表释放,在表被锁定读取时发出的SELECT HIGH_PRIORITY查询也会运行。这仅影响仅使用表级锁定的存储引擎(如MyISAM、MEMORY和MERGE)。

    HIGH_PRIORITY不能与属于UNION的SELECT语句一起使用。

  • STRAIGHT _ JOIN强制优化器按照表在FROM子句中列出的顺序来连接表。如果优化器以非最佳顺序连接表,您可以使用它来加速查询。还可以在table_references列表中使用STRAIGHT _ JOIN。参见第13.2.9.2节“连接语法”。

    STRAIGHT_JOIN不适用于优化程序视为const或system的任何表。 这样的表生成单行,在查询执行的优化阶段读取,并且在查询执行进行之前用适当的列值替换对其列的引用。 这些表将首先出现在EXPLAIN显示的查询计划中。 请参见第8.8.1节“使用EXPLAIN优化查询”。 此异常可能不适用于在外连接的NULL补充端(即LEFT JOIN的右侧表或RIGHT JOIN的左侧表)上使用的const或系统表。

  • SQL_BIG_REQUESTSQL_SMAll_REQUEST可以与GROUP BY或DISTINCT一起使用,分别告诉优化器结果集有许多行或很小。对于SQL_BIG_REQUEST,如果创建了基于磁盘的临时表,MySQL会直接使用它们,并且更喜欢使用临时表中在GROUP BY元素里的键进行排序。 对于SQL_SMALL_RESULT,MySQL使用内存中的临时表来存储生成的表而不是使用排序。 通常不需要这样做。

  • SQL_BUFFER_RESULT强制将结果放入临时表中。 这有助于MySQL尽早释放表锁,并在需要很长时间将结果集发送到客户端的情况下提供帮助。 此修饰符只能用于顶级SELECT语句,不能用于子查询或UNION之后。

  • SQL_CALC_FOUND_ROWS告诉MySQL计算结果集中将有多少行,忽略任何LIMIT子句。 然后可以使用SELECT FOUND_ROWS()检索行数。 请参见第12.15节“信息功能”。

  • SQL_CACHESQL_NO_CACHE修饰符会影响查询缓存中查询结果的缓存(请参见第8.10.3节“MySQL查询缓存”)。 SQL_CACHE告诉MySQL将结果存储在查询缓存中(如果它是可缓存的,并且query_cache_type系统变量的值是2或DEMAND)。 使用SQL_NO_CACHE,服务器不使用查询缓存。 它既不检查查询缓存,也不检查结果是否已缓存,也不缓存查询结果。 (由于解析器中的限制,空格字符必须位于SQL_NO_CACHE关键字之前和之后;非空格(如换行符)会导致服务器检查查询缓存以查看结果是否已缓存。)

    这两个修饰符是互斥的,如果同时指定它们则会发生错误。 此外,子查询(包括FROM子句中的子查询)和第一个SELECT以外的联合中的SELECT语句不允许使用这些修饰符。

    对于视图,如果SQL_NO_CACHE出现在查询中的任何SELECT中,则适用。 对于可高速缓存的查询,如果SQL_CACHE出现在查询引用的视图的第一个SELECT中,则应用SQL_CACHE。

使用存储引擎从分区表中进行选择,例如使用表级锁的MyISAM,只锁定那些包含与SELECT语句WHERE子句匹配的行的分区。(使用行级锁定的存储引擎(如InnoDB)不会出现这种情况。)有关更多信息,请参见第19.6.4节“分区和锁定”。

posted @ 2019-05-13 21:49  BxScope  阅读(213)  评论(0编辑  收藏  举报