11.5 表达式

本节列举了 MySQL 中表达式必须遵循的语法规则,并提供了关于表达式中可能出现的术语类型的额外信息。

以下语法规则定义了 MySQL 中的表达式语法。这里显示的语法基于 MySQL 源代码分布中的sql/sql_yacc.yy文件。关于某些表达式术语的额外信息,请参见表达式术语笔记

expr:
    expr OR expr
  | expr || expr
  | expr XOR expr
  | expr AND expr
  | expr && expr
  | NOT expr
  | ! expr
  | boolean_primary IS [NOT] {TRUE | FALSE | UNKNOWN}
  | boolean_primary

boolean_primary:
    boolean_primary IS [NOT] NULL
  | boolean_primary <=> predicate
  | boolean_primary comparison_operator predicate
  | boolean_primary comparison_operator {ALL | ANY} (subquery)
  | predicate

comparison_operator: = | >= | > | <= | < | <> | !=

predicate:
    bit_expr [NOT] IN (subquery)
  | bit_expr [NOT] IN (expr [, expr] ...)
  | bit_expr [NOT] BETWEEN bit_expr AND predicate
  | bit_expr SOUNDS LIKE bit_expr
  | bit_expr [NOT] LIKE simple_expr [ESCAPE simple_expr]
  | bit_expr [NOT] REGEXP bit_expr
  | bit_expr

bit_expr:
    bit_expr | bit_expr
  | bit_expr & bit_expr
  | bit_expr << bit_expr
  | bit_expr >> bit_expr
  | bit_expr + bit_expr
  | bit_expr - bit_expr
  | bit_expr * bit_expr
  | bit_expr / bit_expr
  | bit_expr DIV bit_expr
  | bit_expr MOD bit_expr
  | bit_expr % bit_expr
  | bit_expr ^ bit_expr
  | bit_expr + interval_expr
  | bit_expr - interval_expr
  | simple_expr

simple_expr:
    literal
  | identifier
  | function_call
  | simple_expr COLLATE collation_name
  | param_marker
  | variable
  | simple_expr || simple_expr
  | + simple_expr
  | - simple_expr
  | ~ simple_expr
  | ! simple_expr
  | BINARY simple_expr
  | (expr [, expr] ...)
  | ROW (expr, expr [, expr] ...)
  | (subquery)
  | EXISTS (subquery)
  | {identifier expr}
  | match_expr
  | case_expr
  | interval_expr

关于操作符优先级,见第14.4.1节,“操作符优先级”。某些操作符的优先级和含义取决于 SQL 模式:

  • 默认情况下,|| 是逻辑OR 运算符。启用PIPES_AS_CONCAT|| 是字符串连接,优先级在^ 和单目运算符之间。

  • 默认情况下,! 的优先级高于 NOT。启用HIGH_NOT_PRECEDENCE!NOT 优先级相同。

请参见第7.1.11节,“服务器SQL模式”

请参见第11.1节,“字面值”

请参见第11.2节,“模式对象名称”

变量可以是用户变量、系统变量或存储程序局部变量或参数:

param_marker 是在预准备语句中的占位符,例如?。见第15.5.1节,“PREPARE语句”

( subquery) 表示返回单个值的子查询,即标量子查询。见第15.2.15.1节,“标量子查询作为操作数”

{identifier expr} 是 ODBC 逃逸语法,用于ODBC 兼容性。值为 expr。语法中的{} 大括号应该写作文字;它们在其他语法描述中不作为语法元语法使用。

match_expr 表示MATCH 表达式。见第14.9节,“全文搜索函数”

case_expr 表示一个CASE表达式。见第14.5节,“控制流函数”

interval_expr 表示一个时间间隔。见时间间隔

interval_expr 在表达式中表示一个时间间隔。间隔具有以下语法:

INTERVAL expr unit

expr 表示数量。unit 表示解释数量的单位;它是一个指定符号,如 HOURDAYWEEK。关键字 INTERVALunit 指定符号不区分大小写。

以下表格显示了每个 unit 值的 expr 参数的预期形式。

表11.2 时间间隔表达式和单位参数

unit Value Expected expr Format
MICROSECOND MICROSECONDS
SECOND SECONDS
MINUTE MINUTES
HOUR HOURS
DAY DAYS
WEEK WEEKS
MONTH MONTHS
QUARTER QUARTERS
YEAR YEARS
SECOND_MICROSECOND 'SECONDS.MICROSECONDS'
MINUTE_MICROSECOND 'MINUTES:SECONDS.MICROSECONDS'
MINUTE_SECOND 'MINUTES:SECONDS'
HOUR_MICROSECOND 'HOURS:MINUTES:SECONDS.MICROSECONDS'
HOUR_SECOND 'HOURS:MINUTES:SECONDS'
HOUR_MINUTE 'HOURS:MINUTES'
DAY_MICROSECOND 'DAYS HOURS:MINUTES:SECONDS.MICROSECONDS'
DAY_SECOND 'DAYS HOURS:MINUTES:SECONDS'
DAY_MINUTE 'DAYS HOURS:MINUTES'
DAY_HOUR 'DAYS HOURS'
YEAR_MONTH 'YEARS-MONTHS'

MySQL 允许在 expr 格式中使用任何标点符号,表格中显示的都是建议的分隔符。

时间间隔用于某些函数,如DATE_ADD()DATE_SUB()

mysql> SELECT DATE_ADD('2018-05-01',INTERVAL 1 DAY);
        -> '2018-05-02'
mysql> SELECT DATE_SUB('2018-05-01',INTERVAL 1 YEAR);
        -> '2017-05-01'
mysql> SELECT DATE_ADD('2020-12-31 23:59:59',
    ->                 INTERVAL 1 SECOND);
        -> '2021-01-01 00:00:00'
mysql> SELECT DATE_ADD('2018-12-31 23:59:59',
    ->                 INTERVAL 1 DAY);
        -> '2019-01-01 23:59:59'
mysql> SELECT DATE_ADD('2100-12-31 23:59:59',
    ->                 INTERVAL '1:1' MINUTE_SECOND);
        -> '2101-01-01 00:01:00'
mysql> SELECT DATE_SUB('2025-01-01 00:00:00',
    ->                 INTERVAL '1 1:1:1' DAY_SECOND);
        -> '2024-12-30 22:58:59'
mysql> SELECT DATE_ADD('1900-01-01 00:00:00',
    ->                 INTERVAL '-1 10' DAY_HOUR);
        -> '1899-12-30 14:00:00'
mysql> SELECT DATE_SUB('1998-01-02', INTERVAL 31 DAY);
        -> '1997-12-02'
mysql> SELECT DATE_ADD('1992-12-31 23:59:59.000002',
    ->            INTERVAL '1.999999' SECOND_MICROSECOND);
        -> '1993-01-01 00:00:01.000001'

使用 INTERVAL 一同进行时间计算的表达式,可以使用+ 或者 - 运算符:

date + INTERVAL expr unit
date - INTERVAL expr unit

INTERVAL expr unit 可以出现在+ 运算符的两侧,如果另一边的表达式是日期或datetime值。对于 - 运算符,INTERVAL expr unit 只能出现在右侧,因为从间隔值减去日期或datetime值没有意义。

mysql> SELECT '2018-12-31 23:59:59' + INTERVAL 1 SECOND;
        -> '2019-01-01 00:00:00'
mysql> SELECT INTERVAL 1 DAY + '2018-12-31';
        -> '2019-01-01'
mysql> SELECT '2025-01-01' - INTERVAL 1 SECOND;
        -> '2024-12-31 23:59:59'

函数EXTRACT() 使用与DATE_ADD()DATE_SUB()相同的unit指定符,但从日期中提取部分,而不是执行日期算术:

mysql> SELECT EXTRACT(YEAR FROM '2019-07-02');
        -> 2019
mysql> SELECT EXTRACT(YEAR_MONTH FROM '2019-07-02 01:02:03');
        -> 201907

时间间隔可以在CREATE EVENT语句中使用:

CREATE EVENT myevent
    ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
    DO
      UPDATE myschema.mytable SET mycol = mycol + 1;

如果您指定的间隔值太短(不包括unit关键字预期的所有间隔部分),MySQL假设您省略了左侧的间隔值。例如,如果您指定的unitDAY_SECOND,那么expr的值预期有天、小时、分钟和秒部分。如果您指定的值像'1:10',MySQL假设天和小时部分缺失,值表示分钟和秒。换言之,'1:10' DAY_SECOND被解释为等同于'1:10' MINUTE_SECOND。这与MySQL解释TIME值的方式相似,表示已过时间,而不是一天中的时间。

expr 被视为字符串,因此如果使用 INTERVAL 指定非字符串值,需要小心。例如,以间隔指定符号 HOUR_MINUTE,'6/4' 将被视为 6 小时,四分钟,而 6/4 则评估为 1.5000 并被视为 1 小时,5000 分钟:

mysql> SELECT '6/4', 6/4;
        -> 1.5000
mysql> SELECT DATE_ADD('2019-01-01', INTERVAL '6/4' HOUR_MINUTE);
        -> '2019-01-01 06:04:00'
mysql> SELECT DATE_ADD('2019-01-01', INTERVAL 6/4 HOUR_MINUTE);
        -> '2019-01-04 12:20:00'

为了确保间隔值的解释与期望相符,可以使用 CAST() 操作。要将 6/4 视为 1 小时,5 分钟,可以将其转换为一个DECIMAL 值,以单个小数位:

mysql> SELECT CAST(6/4 AS DECIMAL(3,1));
        -> 1.5
mysql> SELECT DATE_ADD('1970-01-01 12:00:00',
    ->                 INTERVAL CAST(6/4 AS DECIMAL(3,1)) HOUR_MINUTE);
        -> '1970-01-01 13:05:00'

如果你对日期值添加或减去包含时间部分的值,结果将自动转换为 datetime 值:

mysql> SELECT DATE_ADD('2023-01-01', INTERVAL 1 DAY);
        -> '2023-01-02'
mysql> SELECT DATE_ADD('2023-01-01', INTERVAL 1 HOUR);
        -> '2023-01-01 01:00:00'

如果你添加 MONTHYEAR_MONTHYEAR,并且结果日期的天数大于新月的最大天数,天数将被调整到新月的最大天数:

mysql> SELECT DATE_ADD('2019-01-30', INTERVAL 1 MONTH);
        -> '2019-02-28'

日期算术操作要求完整日期,不支持不完整日期,如 '2016-07-00' 或严重错误日期:

mysql> SELECT DATE_ADD('2016-07-00', INTERVAL 1 DAY);
        -> NULL
mysql> SELECT '2005-03-32' + INTERVAL 1 MONTH;
        -> NULL