MySQL8-中文参考-七-

104 阅读28分钟

MySQL8 中文参考(七)

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

3.6 准备升级安装

原文:dev.mysql.com/doc/refman/8.0/en/upgrade-prerequisites.html

在升级到最新的 MySQL 8.0 版本之前,请通过执行以下描述的初步检查来确保您当前的 MySQL 5.7 或 MySQL 8.0 服务器实例的升级准备就绪。否则,升级过程可能会失败。

提示

考虑使用 MySQL Shell 升级检查工具,它使您能够验证 MySQL 服务器实例是否准备好升级。您可以选择一个目标 MySQL Server 版本,从 MySQL Server 8.0.11 到与当前 MySQL Shell 版本号匹配的 MySQL Server 版本号。升级检查工具执行与指定目标版本相关的自动检查,并建议您进行进一步的手动检查。升级检查工具适用于 MySQL 5.7、8.0 和 8.3 的所有 GA 版本。MySQL Shell 的安装说明可以在这里找到。

初步检查:

  1. 不能存在以下问题:

    • 不能有使用过时数据类型或函数的表。

      如果表中包含旧的时间列(TIMEDATETIMETIMESTAMP列不支持分数秒精度)的旧格式(在 5.6.4 之前),则不支持直接升级到 MySQL 8.0。如果您的表仍然使用旧的时间列格式,请在尝试直接升级到 MySQL 8.0 之前使用REPAIR TABLE进行升级。有关更多信息,请参阅服务器更改,在 MySQL 5.7 参考手册中。

    • 不能有孤立的.frm文件。

    • 触发器不能有缺失或空的定义者,也不能有无效的创建上下文(由SHOW TRIGGERS显示的character_set_clientcollation_connectionDatabase Collation属性或INFORMATION_SCHEMA TRIGGERS表)。任何此类触发器必须被导出和恢复以修复问题。

    要检查这些问题,请执行以下命令:

    mysqlcheck -u root -p --all-databases --check-upgrade
    

    如果mysqlcheck报告任何错误,请纠正这些问题。

  2. 不能有使用不具有本机分区支持的存储引擎的分区表。要识别这样的表,请执行以下查询:

    SELECT TABLE_SCHEMA, TABLE_NAME
    FROM INFORMATION_SCHEMA.TABLES
    WHERE ENGINE NOT IN ('innodb', 'ndbcluster')
    AND CREATE_OPTIONS LIKE '%partitioned%';
    

    查询报告的任何表必须被更改为使用InnoDB或变为非分区表。要将表存储引擎更改为InnoDB,请执行此语句:

    ALTER TABLE *table_name* ENGINE = INNODB;
    

    有关将MyISAM表转换为InnoDB的信息,请参见 Section 17.6.1.5,“从 MyISAM 转换表到 InnoDB”。

    要使分区表变为非分区表,请执行此语句:

    ALTER TABLE *table_name* REMOVE PARTITIONING;
    
  3. 在 MySQL 8.0 中可能保留了以前未保留的一些关键字。请参见 Section 11.3,“关键字和保留字”。这可能导致以前用作标识符的单词变得非法。要修复受影响的语句,请使用标识符引用。请参见 Section 11.2,“模式对象名称”。

  4. MySQL 5.7 mysql系统数据库中不能有与 MySQL 8.0 数据字典使用的表同名的表。要识别具有这些名称的表,请执行此查询:

    SELECT TABLE_SCHEMA, TABLE_NAME
    FROM INFORMATION_SCHEMA.TABLES
    WHERE LOWER(TABLE_SCHEMA) = 'mysql'
    and LOWER(TABLE_NAME) IN
    (
    'catalogs',
    'character_sets',
    'check_constraints',
    'collations',
    'column_statistics',
    'column_type_elements',
    'columns',
    'dd_properties',
    'events',
    'foreign_key_column_usage',
    'foreign_keys',
    'index_column_usage',
    'index_partitions',
    'index_stats',
    'indexes',
    'parameter_type_elements',
    'parameters',
    'resource_groups',
    'routines',
    'schemata',
    'st_spatial_reference_systems',
    'table_partition_values',
    'table_partitions',
    'table_stats',
    'tables',
    'tablespace_files',
    'tablespaces',
    'triggers',
    'view_routine_usage',
    'view_table_usage'
    );
    

    查询报告的任何表必须被删除或重命名(使用RENAME TABLE)。这可能还需要对使用受影响表的应用程序进行更改。

  5. 不能有外键约束名超过 64 个字符的表。使用此查询标识约束名过长的表:

    SELECT TABLE_SCHEMA, TABLE_NAME
    FROM INFORMATION_SCHEMA.TABLES
    WHERE TABLE_NAME IN
      (SELECT LEFT(SUBSTR(ID,INSTR(ID,'/')+1),
                   INSTR(SUBSTR(ID,INSTR(ID,'/')+1),'_ibfk_')-1)
       FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN
       WHERE CHAR_LENGTH(SUBSTR(ID,INSTR(ID,'/')+1))>64);
    

    对于约束名超过 64 个字符的表,请删除约束并使用不超过 64 个字符的约束名重新添加它(使用ALTER TABLE)。

  6. 不能定义由sql_mode系统变量定义的过时 SQL 模式。尝试使用过时的 SQL 模式会阻止 MySQL 8.0 启动。应该修改使用过时 SQL 模式的应用程序以避免它们。有关 MySQL 8.0 中删除的 SQL 模式的信息,请参见服务器更改。

  7. 不能有显式定义列名超过 64 个字符的视图(MySQL 5.7 允许具有长达 255 个字符的列名的视图)。为避免升级错误,应在升级之前修改此类视图。目前,识别列名超过 64 个字符的视图的唯一方法是使用SHOW CREATE VIEW检查视图定义。您还可以通过查询信息模式VIEWS表来检查视图定义。

  8. 在个别ENUMSET列元素中,字符长度超过 255 个字符或 1020 个字节的表格或存储过程是不允许的。在 MySQL 8.0 之前,ENUMSET列元素的最大组合长度为 64K。在 MySQL 8.0 中,单个ENUMSET列元素的最大字符长度为 255 个字符,最大字节长度为 1020 个字节(1020 字节限制支持多字节字符集)。在升级到 MySQL 8.0 之前,修改任何超出新限制的ENUMSET列元素。如果不这样做,升级将因错误而失败。

  9. 在升级到 MySQL 8.0.13 或更高版本之前,不能有驻留在共享InnoDB表空间中的表分区,其中包括系统表空间和通用表空间。通过查询INFORMATION_SCHEMA来识别共享表空间中的表分区:

    如果从 MySQL 5.7 升级,请运行此查询:

    SELECT DISTINCT NAME, SPACE, SPACE_TYPE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES
      WHERE NAME LIKE '%#P#%' AND SPACE_TYPE NOT LIKE 'Single';
    

    如果从较早的 MySQL 8.0 版本升级,请运行此查询:

    SELECT DISTINCT NAME, SPACE, SPACE_TYPE FROM INFORMATION_SCHEMA.INNODB_TABLES
      WHERE NAME LIKE '%#P#%' AND SPACE_TYPE NOT LIKE 'Single';
    

    使用ALTER TABLE ... REORGANIZE PARTITION将表分区从共享表空间移动到每个表的表空间:

    ALTER TABLE *table_name* REORGANIZE PARTITION *partition_name*
      INTO (*partition_definition* TABLESPACE=innodb_file_per_table);
    
  10. 不能有来自 MySQL 8.0.12 或更低版本的查询和存储程序定义使用ASCDESC修饰符用于GROUP BY子句。否则,升级到 MySQL 8.0.13 或更高版本可能会失败,复制到 MySQL 8.0.13 或更高版本的复制服务器也可能失败。有关更多详细信息,请参见 SQL Changes。

  11. 您的 MySQL 5.7 安装不得使用 MySQL 8.0 不支持的功能。这里的任何更改都是特定于安装的,但以下示例说明了要查找的内容:

    在 MySQL 8.0 中已删除了一些服务器启动选项和系统变量。请参阅 MySQL 8.0 中删除的功能,以及第 1.4 节,“MySQL 8.0 中添加、弃用或删除的服务器和状态变量和选项”。如果您使用其中任何内容,升级需要进行配置更改。

    例如:由于数据字典提供有关数据库对象的信息,服务器不再检查数据目录中的目录名称以查找数据库。因此,--ignore-db-dir选项是多余的并已被移除。为了处理这个问题,在升级到 MySQL 8.0 之前,从启动配置中删除任何--ignore-db-dir的实例。此外,在升级到 MySQL 8.0 之前,删除或移动命名数据目录的子目录。(或者,让 8.0 服务器将这些目录添加到数据字典作为数据库,然后使用DROP DATABASE删除每个数据库。)

  12. 如果您打算在升级时将lower_case_table_names设置为 1,请确保在升级之前模式和表名称均为小写。否则,可能会由于模式或表名称大小写不匹配而导致失败。您可以使用以下查询检查包含大写字符的模式和表名称:

    mysql> select TABLE_NAME, if(sha(TABLE_NAME) !=sha(lower(TABLE_NAME)),'Yes','No') as UpperCase from information_schema.tables;
    

    截至 MySQL 8.0.19 版本,如果lower_case_table_names=1,升级过程将检查表和模式名称,确保所有字符均为小写。如果发现表或模式名称包含大写字符,则升级过程将因错误而失败。

    注意

    不建议在升级时更改lower_case_table_names设置。

如果由于上述任何问题导致 MySQL 8.0 升级失败,则服务器将撤销对数据目录的所有更改。在这种情况下,删除所有重做日志文件并在现有数据目录上重新启动 MySQL 5.7 服务器以解决错误。重做日志文件(ib_logfile*)默认位于 MySQL 数据目录中。在错误修复后,在尝试再次升级之前执行慢关闭(通过设置innodb_fast_shutdown=0)。

3.7 在 Unix/Linux 上升级 MySQL 二进制或基于包的安装

原文:dev.mysql.com/doc/refman/8.0/en/upgrade-binary-package.html

本节描述了如何在 Unix/Linux 上升级 MySQL 二进制和基于包的安装。介绍了原地和逻辑升级方法。

  • 原地升级

  • 逻辑升级

  • MySQL Cluster 升级

原地升级

原地升级涉及关闭旧的 MySQL 服务器,用新的 MySQL 二进制文件或包替换旧的文件,重新启动现有数据目录上的 MySQL,并升级需要升级的现有安装的任何剩余部分。有关可能需要升级的详细信息,请参阅 Section 3.4, “MySQL 升级过程升级的内容”。

注意

如果您正在升级最初通过安装多个 RPM 包生成的安装程序,请升级所有包,而不仅仅是一些。例如,如果您之前安装了服务器和客户端 RPM 包,请不要仅升级服务器 RPM 包。

对于某些 Linux 平台,从 RPM 或 Debian 包安装 MySQL 包括 systemd 支持以管理 MySQL 服务器的启动和关闭。在这些平台上,mysqld_safe未安装。在这种情况下,使用 systemd 代替以下说明中使用的方法进行服务器的启动和关闭。请参阅 Section 2.5.9, “使用 systemd 管理 MySQL 服务器”。

对于 MySQL Cluster 安装的升级,请参阅 MySQL Cluster 升级。

���行原地升级:

  1. 查看 Section 3.1, “开始之前”中的信息。

  2. 通过完成 Section 3.6, “准备升级安装”中的初步检查,确保您的安装准备好升级。

  3. 如果您在 InnoDB 中使用 XA 事务,请在升级之前运行XA RECOVER以检查未提交的 XA 事务。如果返回结果,请通过发出XA COMMITXA ROLLBACK语句提交或回滚 XA 事务。

  4. 如果您从 MySQL 5.7.11 或更早版本升级到 MySQL 8.0,并且存在加密的 InnoDB 表空间,请通过执行此语句旋转密钥环主密钥:

    ALTER INSTANCE ROTATE INNODB MASTER KEY;
    
  5. 如果您通常将 MySQL 服务器配置为将innodb_fast_shutdown设置为2(冷关闭),请通过执行以下任一语句之一配置它执行快速或慢速关闭:

    SET GLOBAL innodb_fast_shutdown = 1; -- fast shutdown
    SET GLOBAL innodb_fast_shutdown = 0; -- slow shutdown
    

    使用快速或慢速关闭,InnoDB会将其撤销日志和数据文件保留在一种状态,以便在不同版本之间的文件格式差异情况下进行处理。

  6. 关闭旧的 MySQL 服务器。例如:

    mysqladmin -u root -p shutdown
    
  7. 升级 MySQL 二进制文件或软件包。如果升级二进制安装,解压新的 MySQL 二进制分发包。参见获取和解压分发包。对于基于软件包的安装,请安装新软件包。

  8. 启动 MySQL 8.0 服务器,使用现有的数据目录。例如:

    mysqld_safe --user=mysql --datadir=*/path/to/existing-datadir* &
    

    如果存在加密的InnoDB表空间,请使用--early-plugin-load选项加载密钥环插件。

    当您启动 MySQL 8.0 服务器时,它会自动检测数据字典表是否存在。如果不存在,服务器将在数据目录中创建这些表,填充元数据,然后继续正常的启动序列。在此过程中,服务器会升级所有数据库对象的元数据,包括数据库、表空间、系统和用户表、视图以及存储程序(存储过程和函数、触发器和事件调度器事件)。服务器还会删除以前用于存储元数据的文件。例如,在从 MySQL 5.7 升级到 MySQL 8.0 后,您可能会注意到表不再具有.frm文件。

    如果此步骤失败,服务器将还原对数据目录的所有更改。在这种情况下,您应删除所有重做日志文件,在相同数据目录上启动您的 MySQL 5.7 服务器,并修复任何错误的原因。然后执行另一个慢速关闭 5.7 服务器,并启动 MySQL 8.0 服务器再次尝试。

  9. 在前一步骤中,服务器根据需要升级数据字典。现在需要执行任何剩余的升级操作:

    • 从 MySQL 8.0.16 开始,服务器会在前一步骤中执行这些操作,对mysql系统数据库在 MySQL 5.7 和 MySQL 8.0 之间所需的任何更改进行处理,以便您可以利用新的权限或功能。它还会为 MySQL 8.0 更新性能模式、INFORMATION_SCHEMAsys数据库,并检查所有用户数据库是否与当前版本的 MySQL 不兼容。

    • 在 MySQL 8.0.16 之前,服务器仅在前一步骤中升级数据字典。成功启动 MySQL 8.0 服务器后,执行mysql_upgrade来执行剩余的升级任务:

      mysql_upgrade -u root -p
      

      然后关闭并重新启动 MySQL 服务器,以确保对系统表所做的任何更改生效。例如:

      mysqladmin -u root -p shutdown
      mysqld_safe --user=mysql --datadir=*/path/to/existing-datadir* &
      

      第一次启动 MySQL 8.0 服务器(在之前的步骤中),您可能会在错误日志中看到有关未升级表的消息。如果mysql_upgrade已成功运行,则第二次启动服务器时不应出现此类消息。

注意

升级过程不会升级时区表的内容。有关升级说明,请参见第 7.1.15 节,“MySQL 服务器时区支持”。

如果升级过程使用mysql_upgrade(即在 MySQL 8.0.16 之前),该过程也不会升级帮助表的内容。在这种情况下的升级说明,请参见第 7.1.17 节,“服务器端帮助支持”。

逻辑升级

逻辑升级涉及使用备份或导出工具(如mysqldumpmysqlpump)从旧的 MySQL 实例中导出 SQL,安装新的 MySQL 服务器,并将 SQL 应用于新的 MySQL 实例。有关可能需要升级的详细信息,请参见第 3.4 节,“MySQL 升级过程升级了什么”。

注意

对于某些 Linux 平台,从 RPM 或 Debian 软件包安装 MySQL 包括 systemd 支持以管理 MySQL 服务器的启动和关闭。在这些平台上,不安装mysqld_safe。在这种情况下,请使用 systemd 来启动和关闭服务器,而不是以下说明中使用的方法。请参见第 2.5.9 节,“使用 systemd 管理 MySQL 服务器”。

警告

将从先前的 MySQL 版本中提取的 SQL 应用于新的 MySQL 版本可能会由于新功能和能力的不兼容性而导致错误。因此,从先前的 MySQL 版本中提取的 SQL 可能需要修改以实现逻辑升级。

在升级到最新的 MySQL 8.0 版本之前,执行第 3.6 节,“准备升级安装”中描述的步骤以识别不兼容性。

执行逻辑升级:

  1. 查看第 3.1 节,“开始之前”中的信息。

  2. 从先前的 MySQL 安装中导出现有数据:

    mysqldump -u root -p
      --add-drop-table --routines --events
      --all-databases --force > data-for-upgrade.sql
    

    注意

    如果您的数据库包含存储程序,请使用--routines--events选项与mysqldump(如上所示)。--all-databases选项包括转储中的所有数据库,包括保存系统表的mysql数据库。

    重要

    如果您的表包含生成列,请使用 MySQL 5.7.9 或更高版本提供的mysqldump实用程序创建转储文件。较早版本提供的mysqldump实用程序对生成列定义使用了不正确的语法(Bug #20769542)。您可以使用信息模式COLUMNS表来识别具有生成列的表。

  3. 关闭旧的 MySQL 服务器。例如:

    mysqladmin -u root -p shutdown
    
  4. 安装 MySQL 8.0。有关安装说明,请参见 Chapter 2, Installing MySQL

  5. 初始化新的数据目录,如 Section 2.9.1, “Initializing the Data Directory”中所述。例如:

    mysqld --initialize --datadir=*/path/to/8.0-datadir*
    

    复制临时显示在屏幕上或写入错误日志以供以后使用的 'root'@'localhost' 密码。

  6. 启动 MySQL 8.0 服务器,使用新的数据目录。例如:

    mysqld_safe --user=mysql --datadir=*/path/to/8.0-datadir* &
    
  7. 重置root密码:

    $> mysql -u root -p
    Enter password: ****  <- enter temporary root password
    
    mysql> ALTER USER USER() IDENTIFIED BY '*your new password*';
    
  8. 将先前创建的转储文件加载到新的 MySQL 服务器中。例如:

    mysql -u root -p --force < data-for-upgrade.sql
    

    注意

    当服务器启用 GTIDs(gtid_mode=ON)时,不建议加载包含系统表的转储文件。mysqldump 为使用非事务性 MyISAM 存储引擎的系统表发出 DML 指令,而在启用 GTIDs 时,这种组合是不允许的。还要注意,将从启用 GTIDs 的服务器中加载的转储文件加载到另一个启用 GTIDs 的服务器中会生成不同的事务标识符。

  9. 执行任何剩余的升级操作:

    • 在 MySQL 8.0.16 及更高版本中,关闭服务器,然后使用--upgrade=FORCE选项重新启动以执行剩余的升级任务:

      mysqladmin -u root -p shutdown
      mysqld_safe --user=mysql --datadir=*/path/to/8.0-datadir* --upgrade=FORCE &
      

      重新启动时使用--upgrade=FORCE,服务器会在 MySQL 5.7 和 MySQL 8.0 之间对mysql系统模式进行所需的任何更改,以便您可以利用新的权限或功能。它还会将性能模式、INFORMATION_SCHEMAsys模式更新到 MySQL 8.0,并检查所有用户模式与当前版本的 MySQL 的不兼容性。

    • 在 MySQL 8.0.16 之前,执行mysql_upgrade执行剩余的升级任务:

      mysql_upgrade -u root -p
      

      然后关闭并重新启动 MySQL 服务器,以确保对系统表所做的任何更改生效。例如:

      mysqladmin -u root -p shutdown
      mysqld_safe --user=mysql --datadir=*/path/to/8.0-datadir* &
      

注意

升级过程不会升级时区表的内容。有关升级说明,请参见第 7.1.15 节,“MySQL 服务器时区支持”。

如果升级过程使用mysql_upgrade(即在 MySQL 8.0.16 之前),该过程也不会升级帮助表的内容。在这种情况下的升级说明,请参见第 7.1.17 节,“服务器端帮助支持”。

注意

加载包含 MySQL 5.7 mysql模式的转储文件会重新创建两个不再使用的表:eventproc。(相应的 MySQL 8.0 表是eventsroutines,都是数据字典表并受到保护。)在确认升级成功后,您可以通过执行以下 SQL 语句删除eventproc表:

DROP TABLE mysql.event;
DROP TABLE mysql.proc;

MySQL Cluster 升级

本节信息是就地升级中描述的程序的附属内容,用于升级 MySQL Cluster。

从 MySQL 8.0.16 开始,MySQL Cluster 升级可以作为常规滚动升级执行,遵循通常的三个有序步骤:

  1. 升级 MGM 节点。

  2. 逐个升级数据节点。

  3. 逐个升级 API 节点(包括 MySQL 服务器)。

升级每个节点的方式几乎与 MySQL 8.0.16 之前相同,因为升级数据字典和升级系统表之间有区别。升级每个单独的mysqld有两个步骤:

  1. 导入数据字典。

    使用--upgrade=MINIMAL选项启动新服务器以升级数据字典但不升级系统表。这与 MySQL 8.0.16 之前启动服务器但不调用mysql_upgrade的操作基本相同。

    MySQL 服务器必须连接到NDB才能完成此阶段。如果存在任何NDBNDBINFO表,并且服务器无法连接到集群,则会显示错误消息并退出:

    Failed to Populate DD tables.
    
  2. 升级系统表。

    在 MySQL 8.0.16 之前,DBA 调用 mysql_upgrade 客户端来升级系统表。从 MySQL 8.0.16 开始,服务器执行此操作:为了升级系统表,重新启动每个单独的 mysqld,不使用 --upgrade=MINIMAL 选项。

3.8 使用 MySQL Yum 存储库升级 MySQL

原文:dev.mysql.com/doc/refman/8.0/en/updating-yum-repo.html

对于支持 Yum 的平台(参见第 2.5.1 节,“使用 MySQL Yum 存储库在 Linux 上安装 MySQL”,有一个列表),您可以使用 MySQL Yum 存储库执行就地升级 MySQL(即,替换旧版本,然后使用旧数据文件运行新版本)。

注意事项

  • 在对 MySQL 执行任何更新之前,请仔细遵循第三章,升级 MySQL中的说明。在那里讨论的其他说明中,特别重要的是在更新之前备份您的数据库。

  • 以下说明假设您已经使用 MySQL Yum 存储库或直接从MySQL 开发者区的 MySQL 下载页面下载的 RPM 包安装了 MySQL;如果不是这种情况,请按照使用 MySQL Yum 存储库替换 MySQL 的第三方发行版中的说明操作。

  1. 选择目标系列

    默认情况下,MySQL Yum 存储库将 MySQL 更新到您在安装过程中选择的发布系列的最新版本(有关详细信息,请参见选择发布系列),这意味着,例如,5.7.x 安装 不会 自动更新到 8.0.x 版本。要更新到另一个发布系列,您必须首先禁用已选择的系列的子存储库(默认情况下或自行选择),然后启用目标系列的子存储库。要执行此操作,请参见选择发布系列中给出的一般说明。对于从 MySQL 5.7 升级到 8.0,请执行选择发布系列中所示步骤的相反操作,禁用 MySQL 5.7 系列的子存储库,并启用 MySQL 8.0 系列的子存储库。

    一般规则是,要从一个发布系列升级到另一个发布系列,请先转到下一个系列,而不是跳过一个系列。例如,如果您当前运行 MySQL 5.6 并希望升级到 8.0,请先升级到 MySQL 5.7,然后再升级到 8.0。

    重要

    有关从 MySQL 5.7 升级到 8.0 的重要信息,请参阅从 MySQL 5.7 升级到 8.0。

  2. 升级 MySQL

    通过以下命令升级 MySQL 及其组件,对于不支持 dnf 的平台:

    sudo yum update mysql-server
    

    对于支持 dnf 的平台:

    sudo dnf upgrade mysql-server
    

    或者,您可以通过告诉 Yum 更新系统上的所有内容来更新 MySQL,这可能需要更多时间。对于未启用 dnf 的平台:

    sudo yum update
    

    对于启用 dnf 的平台:

    sudo dnf upgrade
    
  3. 重新启动 MySQL

    MySQL 服务器在通过 Yum 更新后总是重新启动。在 MySQL 8.0.16 之前,服务器重新启动后运行mysql_upgrade来检查并可能解决旧数据与升级软件之间的任何不兼容性。mysql_upgrade还执行其他功能;有关详细信息,请参见 Section 6.4.5,“mysql_upgrade — 检查和升级 MySQL 表”。从 MySQL 8.0.16 开始,不再需要此步骤,因为服务器执行了以前由mysql_upgrade处理的所有任务。

您还可以仅更新特定组件。使用以下命令列出所有已安装的 MySQL 组件的软件包(对于启用 dnf 的系统,请将命令中的 yum 替换为 dnf):

sudo yum list installed | grep "^mysql"

在确定您选择的组件的软件包名称后,使用以下命令更新软件包,将 package-name 替换为软件包的名称。对于未启用 dnf 的平台:

sudo yum update *package-name*

对于启用 dnf 的平台:

sudo dnf upgrade *package-name*

升级共享客户端库

使用 Yum 仓库更新 MySQL 后,使用旧版本共享客户端库编译的应用程序应继续工作。

*如果重新编译应用程序并动态链接它们与更新的库:*与新版本共享库一样,新旧库之间的符号版本之间存在差异或添加(例如,在新的标准 8.0 共享客户端库和一些较旧的—之前或变体—版本之间存在符号版本之间的差异或添加,这些共享库是由 Linux 发行版的软件仓库原生提供的,或来自其他来源),在这些更新的新共享库上编译的任何应用程序需要在部署应用程序的系统上使用这些更新的库。如预期的那样,如果这些库不在位,需要共享库的应用程序将失败。因此,请确保在这些系统上部署来自 MySQL 的共享库软件包。为此,请将 MySQL Yum 仓库添加到系统中(请参见添加 MySQL Yum 仓库)并按照使用 Yum 安装其他 MySQL 产品和组件中给出的说明安装最新的共享库。

3.9 通过 MySQL APT 存储库升级 MySQL

原文:dev.mysql.com/doc/refman/8.0/en/updating-apt-repo.html

在 Debian 和 Ubuntu 平台上,要执行 MySQL 及其组件的原地升级,请使用 MySQL APT 存储库。请参阅 A Quick Guide to Using the MySQL APT Repository 中的通过 MySQL APT 存储库升级 MySQL。

3.10 使用 MySQL SLES 仓库升级 MySQL

原文:dev.mysql.com/doc/refman/8.0/en/updating-sles-repo.html

在 SUSE Linux Enterprise Server (SLES) 平台上,要执行 MySQL 及其组件的就地升级,请使用 MySQL SLES 仓库。参见 使用 MySQL SLES 仓库升级 MySQL 在 使用 MySQL SLES 仓库快速指南 中。

3.11 在 Windows 上升级 MySQL

原文:dev.mysql.com/doc/refman/8.0/en/windows-upgrading.html

在 Windows 上升级 MySQL 有两种方法:

  • 使用 MySQL Installer

  • 使用 Windows ZIP 存档发行版

您选择的方法取决于现有安装是如何进行的。在继续之前,请查看第三章,“升级 MySQL”,了解有关升级 MySQL 的其他信息,这些信息不特定于 Windows。

注意

无论您选择哪种方法,在执行升级之前,始终要备份当前的 MySQL 安装。请参阅第 9.2 节,“数据库备份方法”。

不支持非 GA 版本之间的升级(或从非 GA 版本升级到 GA 版本)。非 GA 版本中会发生重大的开发变化,您可能会遇到兼容性问题或启动服务器时出现问题。

注意

MySQL Installer 不支持在社区版本和商业版本之间的升级。如果您需要此类型的升级,请使用 ZIP 存档方法进行。

使用 MySQL Installer 升级 MySQL

当当前服务器安装是使用 MySQL Installer 进行的,并且升级在当前发布系列内时,使用 MySQL Installer 进行升级是最佳方法。MySQL Installer 不支持在发布系列之间的升级,例如从 5.7 升级到 8.0,并且不提供升级指示器来提示您进行升级。有关在发布系列之间升级的说明,请参阅使用 Windows ZIP 发行版升级 MySQL。

要使用 MySQL Installer 进行升级:

  1. 启动 MySQL Installer。

  2. 从仪表板中,点击 Catalog 下载目录的最新更改。只有在仪表板显示服务器版本号旁边有箭头时,安装的服务器才能升级。

  3. 点击升级。现在,所有有更新版本的产品都会显示在列表中。

    注意

    MySQL Installer 取消里程碑版本(预发布)在同一发布系列中的服务器升级选项。此外,它显示警告以指示不支持升级,识别继续的风险,并提供手动执行升级步骤的摘要。您可以重新选择服务器升级并自行承担风险继续进行。

  4. 除非您打算此时升级其他产品,否则取消选择除 MySQL 服务器产品之外的所有产品,并点击下一步。

  5. 点击“执行”开始下载。下载完成后,点击“下一步”开始升级操作。

    升级到 MySQL 8.0.16 及更高版本可能会显示一个选项,跳过系统表的升级检查和处理。有关此选项的更多信息,请参阅 Important server upgrade conditions。

  6. 配置服务器。

使用 Windows ZIP 分发升级 MySQL。

使用 Windows ZIP 存档分发执行升级:

  1. dev.mysql.com/downloads/下载最新的 Windows ZIP 存档分发的 MySQL。

  2. 如果服务器正在运行,请停止它。如果服务器已安装为服务,请使用命令提示符中的以下命令停止服务:

    C:\> SC STOP *mysqld_service_name*
    

    或者,使用NET STOP mysqld_service_name

    如果未将 MySQL 服务器作为服务运行,请使用mysqladmin停止它。例如,在从 MySQL 5.7 升级到 8.0 之前,请使用 MySQL 5.7 中的mysqladmin如下:

    C:\> "C:\Program Files\MySQL\MySQL Server 5.7\bin\mysqladmin" -u root shutdown
    

    注意

    如果 MySQL root用户账户有密码,请使用带有-p选项的mysqladmin并在提示时输入密码。

  3. 解压 ZIP 存档。您可以覆盖现有的 MySQL 安装(通常位于C:\mysql),或将其安装到不同的目录,例如C:\mysql8。建议覆盖现有安装。

  4. 重新启动服务器。例如,如果将 MySQL 作为服务运行,请使用**SC START *mysqld_service_name*NET START mysqld_service_name命令,否则直接调用mysqld

  5. 在 MySQL 8.0.16 之前,以管理员身份运行mysql_upgrade检查您的表,必要时尝试修复它们,并更新授权表(如果已更改),以便利用任何新功能。请参阅 Section 6.4.5, “mysql_upgrade — Check and Upgrade MySQL Tables”。从 MySQL 8.0.16 开始,不再需要此步骤,因为服务器执行了以前由mysql_upgrade处理的所有任务。

  6. 如果遇到错误,请参阅 Section 2.3.5, “Troubleshooting a Microsoft Windows MySQL Server Installation”。

3.12 升级 Docker 安装的 MySQL

原文:dev.mysql.com/doc/refman/8.0/en/upgrade-docker-mysql.html

要升级 MySQL 的 Docker 安装,请参考 升级 MySQL 服务器容器。

3.13 升级故障排除

原文:dev.mysql.com/doc/refman/8.0/en/upgrade-troubleshooting.html

  • 在 MySQL 5.7 实例中,表的.frm文件与InnoDB数据字典之间的模式不匹配可能导致升级到 MySQL 8.0 失败。这种不匹配可能是由于.frm文件损坏造成的。要解决此问题,请在再次尝试升级之前转储和恢复受影响的表。

  • 如果出现问题,比如新的mysqld服务器无法启动,请验证您是否有来自先前安装的旧my.cnf文件。您可以使用--print-defaults选项来检查(例如,mysqld --print-defaults)。如果此命令显示除程序名称之外的任何内容,则您有一个影响服务器或客户端操作的活动my.cnf文件。

  • 如果在升级后,您遇到编译的客户端程序出现问题,比如Commands out of sync或意外的核心转储,那么您可能在编译程序时使用了旧的头文件或库文件。在这种情况下,请检查您的mysql.h文件和libmysqlclient.a库文件的日期,以验证它们是否来自新的 MySQL 发行版。如果不是,请使用新的头文件和库文件重新编译您的程序。如果共享客户端库的主要版本号已更改(例如,从libmysqlclient.so.20libmysqlclient.so.21),则可能还需要重新编译针对共享客户端库编译的程序。

  • 如果您已经创建了一个具有特定名称的可加载函数,并将 MySQL 升级到实现具有相同名称的新内置函数的版本,则该可加载函数将变得无法访问。要纠正此问题,请使用DROP FUNCTION删除可加载函数,然后使用CREATE FUNCTION使用不冲突的不同名称重新创建可加载函数。如果新版本的 MySQL 实现了与现有存储函数同名的内置函数,则情况也是如此。有关服务器如何解释对不同类型函数的引用的规则,请参见 Section 11.2.5, “Function Name Parsing and Resolution”。

  • 如果由于第 3.6 节“准备升级安装”中概述的任何问题而导致升级到 MySQL 8.0 失败,则服务器会将所有更改恢复到数据目录。在这种情况下,删除所有重做日志文件并在现有数据目录上重新启动 MySQL 5.7 服务器以解决错误。重做日志文件(ib_logfile*)默认位于 MySQL 数据目录中。在错误修复后,在尝试再次升级之前执行慢关闭(通过设置innodb_fast_shutdown=0)。

3.14 重建或修复表或索引

原文:dev.mysql.com/doc/refman/8.0/en/rebuilding-tables.html

本节描述了如何重建或修复表或索引,可能是由于:

  • MySQL 处理数据类型或字符集的更改。例如,校对错误可能已经被纠正,需要重建表以更新使用该校对的字符列的索引。

  • CHECK TABLE 报告的需要表修复或升级,mysqlcheckmysql_upgrade

重建表的方法包括:

  • 转储和重新加载方法

  • ALTER TABLE 方法

  • REPAIR TABLE 方法

转储和重新加载方法

如果您因为不同版本的 MySQL 在二进制(原地)升级或降级后无法处理它们而需要重建表,您必须使用转储和重新加载的方法。在使用原始版本的 MySQL 升级或降级之前转储表。然后在升级或降级之后重新加载表。

如果您仅使用转储和重新加载方法来重建表以重建索引,您可以在升级或降级之前或之后执行转储。重新加载仍然必须在之后进行。

如果您需要重建一个 InnoDB 表,因为 CHECK TABLE 操作指示需要进行表升级,请使用 mysqldump 创建一个转储文件,然后使用 mysql 重新加载文件。如果 CHECK TABLE 操作指示存在损坏或导致 InnoDB 失败,请参考 第 17.21.3 节,“强制 InnoDB 恢复” 了解如何使用 innodb_force_recovery 选项重新启动 InnoDB。要了解 CHECK TABLE 可能遇到的问题类型,请参考 第 15.7.3.2 节,“CHECK TABLE 语句” 中的 InnoDB 注释。

要通过转储和重新加载来重建表,使用 mysqldump 创建一个转储文件,然后使用 mysql 重新加载文件:

mysqldump *db_name* t1 > dump.sql
mysql *db_name* < dump.sql

要重建单个数据库中的所有表格,请指定数据库名称,不需要跟随任何表格名称:

mysqldump *db_name* > dump.sql
mysql *db_name* < dump.sql

要重建所有数据库中的所有表格,请使用--all-databases选项:

mysqldump --all-databases > dump.sql
mysql < dump.sql

ALTER TABLE 方法

要使用ALTER TABLE重新构建表格,请使用“null”修改;也就是说,使用一个ALTER TABLE语句“更改”表格以使用它已经具有的存储引擎。例如,如果t1是一个InnoDB表格,使用以下语句:

ALTER TABLE t1 ENGINE = InnoDB;

如果不确定在ALTER TABLE语句中指定哪个存储引擎,请使用SHOW CREATE TABLE来显示表格定义。

REPAIR TABLE 方法

REPAIR TABLE方法仅适用于MyISAMARCHIVECSV表格。

如果表格检查操作指示存在损坏或需要升级,则可以使用REPAIR TABLE。例如,要修复一个MyISAM表格,请使用以下语句:

REPAIR TABLE t1;

mysqlcheck --repair提供了对REPAIR TABLE语句的命令行访问。这可以是一个更方便的修复表格的方法,因为您可以使用--databases--all-databases选项来修复特定数据库中的所有表格或所有数据库中的所有表格:

mysqlcheck --repair --databases *db_name* ...
mysqlcheck --repair --all-databases

3.15 将 MySQL 数据库复制到另一台机器

原文:dev.mysql.com/doc/refman/8.0/en/copying-databases.html

在需要在不同架构之间传输数据库的情况下,您可以使用mysqldump创建包含 SQL 语句的文件。然后,您可以将文件传输到另一台机器,并将其作为输入提供给mysql客户端。

使用mysqldump --help查看可用的选项。

注意

如果在创建转储的服务器上使用了 GTIDs(gtid_mode=ON),默认情况下,mysqldump会在转储中包含gtid_executed集的内容,以将其传输到新机器。这样做的结果可能会因涉及的 MySQL 服务器版本而有所不同。查看mysqldump--set-gtid-purged选项的描述,以了解您正在使用的版本的情况,以及如何更改行为,如果默认行为的结果不适合您的情况。

将数据库在两台机器之间移动的最简单(尽管不是最快)方法是在存储数据库的机器上运行以下命令:

mysqladmin -h '*other_hostname*' create *db_name*
mysqldump *db_name* | mysql -h '*other_hostname*' *db_name*

如果您想要通过缓慢的网络从远程机器复制数据库,可以使用以下命令:

mysqladmin create *db_name*
mysqldump -h '*other_hostname*' --compress *db_name* | mysql *db_name*

您还可以将转储存储在文件中,将文件传输到目标机器,然后在那里将文件加载到数据库中。例如,您可以在源机器上将数据库转储到压缩文件中,如下所示:

mysqldump --quick *db_name* | gzip > *db_name*.gz

将包含数据库内容的文件传输到目标机器,并在那里运行这些命令:

mysqladmin create *db_name*
gunzip < *db_name*.gz | mysql *db_name*

您还可以使用mysqldumpmysqlimport来传输数据库。对于大表,这比简单使用mysqldump要快得多。在以下命令中,*DUMPDIR*代表您用于存储mysqldump输出的完整路径名。

首先,创建用于输出文件的目录并转储数据库:

mkdir *DUMPDIR*
mysqldump --tab=*DUMPDIR*
   *db_name*

然后将*DUMPDIR*目录中的文件传输到目标机器上的相应目录,并在那里将文件加载到 MySQL 中:

mysqladmin create *db_name*           # create database
cat *DUMPDIR*/*.sql | mysql *db_name*   # create tables in database
mysqlimport *db_name*
   *DUMPDIR*/*.txt   # load data into tables

不要忘记复制mysql数据库,因为那里存储着授权表。在新机器上,您可能需要以 MySQL root用户身份运行命令,直到mysql数据库就位。

在新机器上导入mysql数据库后,执行mysqladmin flush-privileges,以便服务器重新加载授权表信息。

第四章 降级 MySQL

原文:dev.mysql.com/doc/refman/8.0/en/downgrading.html

从 MySQL 8.0 降级到 MySQL 5.7,或者从 MySQL 8.0 的一个版本降级到之前的 MySQL 8.0 版本,都不受支持。唯一支持的替代方案是恢复在升级之前进行的备份。因此,在开始升级过程之前,务必备份您的数据。

第五章 教程

原文:dev.mysql.com/doc/refman/8.0/en/tutorial.html

目录

5.1 连接和断开服务器

5.2 输入查询

5.3 创建和使用数据库

5.3.1 创建和选择数据库

5.3.2 创建表

5.3.3 将数据加载到表中

5.3.4 从表中检索信息

5.4 获取有关数据库和表的信息

5.5 批处理模式中使用 mysql

5.6 常见查询示例

5.6.1 列的最大值

5.6.2 某列的最大行

5.6.3 每组列的最大值

5.6.4 某列的分组最大值的行

5.6.5 使用用户定义变量

5.6.6 使用外键

5.6.7 在两个键上搜索

5.6.8 计算每天的访问量

5.6.9 使用 AUTO_INCREMENT

5.7 使用 MySQL 与 Apache

本章通过展示如何使用mysql客户端程序创建和使用简单数据库,为您提供了 MySQL 的教程介绍。mysql(有时称为“终端监视器”或只是“监视器”)是一个交互式程序,可以让您连接到 MySQL 服务器,运行查询并查看结果。mysql也可以以批处理模式使用:您事先将查询放入文件中,然后告诉mysql执行文件的内容。这里涵盖了使用mysql的两种方式。

要查看mysql提供的选项列表,请使用--help选项调用它:

$> mysql --help

本章假设您的机器上已安装mysql,并且可以连接到可用的 MySQL 服务器。如果不是这样,请联系您的 MySQL 管理员。(如果是管理员,则需要查阅本手册的相关部分,如第七章,MySQL 服务器管理。)

本章描述了建立和使用数据库的整个过程。如果您只对访问现有数据库感兴趣,您可能想跳过描述如何创建数据库及其包含的表的部分。

由于本章是教程性质的,许多细节被必然省略了。请查阅手册中相关章节,以获取更多关于这里涵盖的主题的信息。

5.1 连接和断开服务器

原文:dev.mysql.com/doc/refman/8.0/en/connecting-disconnecting.html

要连接到服务器,通常需要在调用mysql时提供一个 MySQL 用户名,并且很可能需要一个密码。如果服务器运行在您登录的机器之外的机器上,您还必须指定主机名。联系管理员以找出应该使用哪些连接参数来连接(即使用哪个主机、用户名和密码)。一旦知道正确的参数,您应该能够像这样连接:

$> mysql -h *host* -u *user* -p
Enter password: ********

hostuser 分别代表您的 MySQL 服务器运行的主机名和您的 MySQL 帐户的用户名。替换适合您设置的值。********代表您的密码;当mysql显示输入密码:提示时输入。

如果一切正常,您应该看到一些简介信息,然后是一个mysql>提示符:

$> mysql -h *host* -u *user* -p
Enter password: ********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25338 to server version: 8.0.36-standard

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>

mysql>提示符告诉您mysql已准备好让您输入 SQL 语句。

如果您正在登录与 MySQL 运行在同一台机器上,您可以省略主机,并简单地使用以下内容:

$> mysql -u *user* -p

如果在尝试登录时出现错误消息,例如 ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2),这意味着 MySQL 服务器守护程序(Unix)或服务(Windows)未运行。请咨询管理员或查看适用于您操作系统的第二章 安装 MySQL部分。

如果在尝试登录时遇到其他常见问题,可以参考第 B.3.2 节 “使用 MySQL 程序时的常见错误”进行帮助。

一些 MySQL 安装允许用户以匿名(未命名)用户连接到在本地主机上运行的服务器。如果您的机器是这种情况,您应该能够通过调用mysql而不使用任何选项来连接到该服务器:

$> mysql

成功连接后,您可以随时在mysql>提示符处键入QUIT(或\q)来断开连接:

mysql> QUIT
Bye

在 Unix 上,您也可以通过按下 Control+D 来断开连接。

在接下来的章节中,大多数示例假定您已连接到服务器。它们通过mysql>提示符来指示这一点。

5.2 输入查询

原文:dev.mysql.com/doc/refman/8.0/en/entering-queries.html

确保您已连接到服务器,如前一节所述。这本身并不选择要使用的任何数据库,但没关系。此时,更重要的是了解如何发出查询的基本原则,而不是立即开始创建表,将数据加载到其中并从中检索数据。本节描述了输入查询的基本原则,使用几个您可以尝试以熟悉mysql工作方式的查询。

这是一个简单的查询,要求服务器告诉您其版本号和当前日期。按照这里显示的方式在mysql>提示后键入并按 Enter:

mysql> SELECT VERSION(), CURRENT_DATE;
+-----------+--------------+
| VERSION() | CURRENT_DATE |
+-----------+--------------+
| 5.8.0-m17 | 2015-12-21   |
+-----------+--------------+
1 row in set (0.02 sec)
mysql>

此查询说明了关于mysql的几个方面:

  • 查询通常由一个 SQL 语句后跟一个分号组成。(有一些例外情况,其中分号可以省略。QUIT,前面提到的,是其中之一。我们稍后会介绍其他情况。)

  • 当您发出查询时,mysql将其发送到服务器执行并显示结果,然后打印另一个mysql>提示,表示它已准备好接受另一个查询。

  • mysql以表格形式(行和列)显示查询输出。第一行包含列的标签。随后的行是查询结果。通常,列标签是您从数据库表中提取的列的名称。如果您检索的是表列的值而不是表列(如刚刚显示的示例中),mysql使用表达式本身标记列。

  • mysql显示了返回的行数以及查询执行所需的时间,这给出了服务器性能的大致概念。这些值不精确,因为它们代表挂钟时间(而不是 CPU 或机器时间),并且受到服务器负载和网络延迟等因素的影响。(为简洁起见,本章剩余示例中有时不显示“结果集中的行”行。)

关键字可以以任何大小写形式输入。以下查询是等效的:

mysql> SELECT VERSION(), CURRENT_DATE;
mysql> select version(), current_date;
mysql> SeLeCt vErSiOn(), current_DATE;

这是另一个查询。它演示了您可以将mysql用作简单的计算器:

mysql> SELECT SIN(PI()/4), (4+1)*5;
+------------------+---------+
| SIN(PI()/4)      | (4+1)*5 |
+------------------+---------+
| 0.70710678118655 |      25 |
+------------------+---------+
1 row in set (0.02 sec)

到目前为止显示的查询相对较短,是单行语句。您甚至可以在一行上输入多个语句。只需用分号结束每个语句:

mysql> SELECT VERSION(); SELECT NOW();
+-----------+
| VERSION() |
+-----------+
| 8.0.13    |
+-----------+
1 row in set (0.00 sec)

+---------------------+
| NOW()               |
+---------------------+
| 2018-08-24 00:56:40 |
+---------------------+
1 row in set (0.00 sec)

查询不必全部在一行上给出,因此需要多行的长查询不是问题。mysql通过查找终止分号来确定语句的结束位置,而不是查找输入行的结尾。(换句话说,mysql接受自由格式的输入:它收集输入行但直到看到分号才执行它们。)

这是一个简单的多行语句:

mysql> SELECT
 -> USER()
 -> ,
 -> CURRENT_DATE;
+---------------+--------------+
| USER()        | CURRENT_DATE |
+---------------+--------------+
| jon@localhost | 2018-08-24   |
+---------------+--------------+

在这个例子中,请注意在输入多行查询的第一行后,提示符从mysql>变为->。这是mysql表示它尚未看到完整语句并正在等待其余部分的方式。提示符是你的朋友,因为它提供了宝贵的反馈。如果你利用这个反馈,你就可以始终了解mysql正在等待什么。

如果您决定不想执行正在输入过程中的查询,请键入\c取消它:

mysql> SELECT
 -> USER()
 -> \c
mysql>

在这里,也请注意提示符。在键入\c后,它会切换回mysql>,提供反馈以指示mysql已准备好进行新查询。

以下表显示了您可能看到的每个提示符以及它们对mysql所处状态的含义。

提示符含义
mysql>准备新查询
->等待多行查询的下一行
'>等待下一行,等待完成以单引号(')开始的字符串
">等待下一行,等待完成以双引号(")开始的字符串

| ``>| 等待下一行,等待完成以反引号开始的标识符(```sql) | |/>| Waiting for next line, waiting for completion of a comment that began with/` |

Multiple-line statements commonly occur by accident when you intend to issue a query on a single line, but forget the terminating semicolon. In this case, mysql waits for more input:


mysql> SELECT USER()

->

```sql

If this happens to you (you think you've entered a statement but the only response is a `->` prompt), most likely **mysql** is waiting for the semicolon. If you don't notice what the prompt is telling you, you might sit there for a while before realizing what you need to do. Enter a semicolon to complete the statement, and **mysql** executes it:

mysql> SELECT USER()

-> ;

+---------------+

USER()

+---------------+

jon@localhost

+---------------+


The `'>` and `">` prompts occur during string collection (another way of saying that MySQL is waiting for completion of a string). In MySQL, you can write strings surrounded by either `'` or `"` characters (for example, `'hello'` or `"goodbye"`), and **mysql** lets you enter strings that span multiple lines. When you see a `'>` or `">` prompt, it means that you have entered a line containing a string that begins with a `'` or `"` quote character, but have not yet entered the matching quote that terminates the string. This often indicates that you have inadvertently left out a quote character. For example:

mysql> SELECT * FROM my_table WHERE name = 'Smith AND age < 30;

'>

If you enter this `SELECT` statement, then press **Enter** and wait for the result, nothing happens. Instead of wondering why this query takes so long, notice the clue provided by the `'>` prompt. It tells you that **mysql** expects to see the rest of an unterminated string. (Do you see the error in the statement? The string `'Smith` is missing the second single quotation mark.)

At this point, what do you do? The simplest thing is to cancel the query. However, you cannot just type `\c` in this case, because **mysql** interprets it as part of the string that it is collecting. Instead, enter the closing quote character (so **mysql** knows you've finished the string), then type `\c`:

mysql> SELECT * FROM my_table WHERE name = 'Smith AND age < 30;

'> '\c

mysql>


提示符改回`mysql>`,表示**mysql**已准备好进行新查询。

``>`提示符类似于`'>``">`提示符,但表示您已经开始但尚未完成反引号引用的标识符。

了解`'>``">```>`提示符的含义很重要,因为如果您错误地输入了一个未终止的字符串,您输入的任何进一步行似乎都会被**mysql**忽略,包括包含`QUIT`的行。这可能会令人困惑,特别是如果您不知道在取消当前查询之前需要提供终止引号。

注意

从这一点开始,多行语句将不再带有次要提示(`->`或其他),以便更容易复制并粘贴语句以供自己尝试。


# 5.3 创建和使用数据库

> 原文:[`dev.mysql.com/doc/refman/8.0/en/database-use.html`](https://dev.mysql.com/doc/refman/8.0/en/database-use.html)

5.3.1 创建和选择数据库

5.3.2 创建表

5.3.3 将数据加载到表中

5.3.4 从表中检索信息

一旦你知道如何输入 SQL 语句,你就可以准备访问数据库了。

假设你家里有几只宠物(你的动物园),你想要跟踪它们的各种信息。你可以通过创建表来保存你的数据,并加载所需的信息。然后,通过从表中检索数据,你可以回答关于你的动物的不同问题。本节将向你展示如何执行以下操作:

+   创建一个数据库

+   创建一个表

+   将数据加载到表中

+   以各种方式从表中检索数据

+   使用多个表

动物园数据库很简单(故意的),但很容易想象在现实世界中可能使用类似类型的数据库的情况。例如,这样的数据库可以被农民用来跟踪牲畜,或者被兽医用来跟踪患者记录。包含以下部分中使用的一些查询和示例数据的动物园分发可以从 MySQL 网站获取。它以压缩的**tar**文件和 Zip 格式提供在`dev.mysql.com/doc/`。

使用`SHOW`语句查找服务器上当前存在的数据库:

```sql
mysql> SHOW DATABASES;
+----------+
| Database |
+----------+
| mysql    |
| test     |
| tmp      |
+----------+

mysql数据库描述了用户访问权限。test数据库通常作为用户尝试事物的工作空间可用。

由该语句显示的数据库列表在您的机器上可能不同;如果您没有SHOW DATABASES权限,则SHOW DATABASES不会显示您没有权限的数据库。参见 Section 15.7.7.14, “SHOW DATABASES Statement”。

如果test数据库存在,尝试访问它:

mysql> USE test
Database changed

USE,像QUIT一样,不需要分号。(如果你愿意,可以用分号终止这样的语句;这并不会造成任何伤害。)USE语句在另一个方面也很特殊:它必须在一行上给出。

你可以在接下来的示例中使用test数据库(如果你有权限),但是任何你在该数据库中创建的内容都可以被其他人删除。因此,你应该向你的 MySQL 管理员请求使用自己的数据库的权限。假设你想要称之为menagerie。管理员需要执行类似这样的语句:

mysql> GRANT ALL ON menagerie.* TO 'your_mysql_name'@'your_client_host';

其中your_mysql_name是分配给您的 MySQL 用户名,your_client_host是您连接到服务器的主机。

5.3.1 创建和选择数据库

原文:dev.mysql.com/doc/refman/8.0/en/creating-database.html

如果管理员在设置权限时为您创建数据库,则可以开始使用它。否则,您需要自行创建:

mysql> CREATE DATABASE menagerie;

在 Unix 下,数据库名称区分大小写(不像 SQL 关键字),因此您必须始终将数据库称为menagerie,而不是MenagerieMENAGERIE或其他变体。表名也是如此。(在 Windows 下,此限制不适用,尽管您必须在给定查询中始终使用相同的大小写来引用数据库和表。但是,出于各种原因,推荐的最佳实践始终是使用创建数据库时使用的相同大小写。)

注意

如果在尝试创建数据库时出现诸如 ERROR 1044 (42000): Access denied for user 'micah'@'localhost' to database 'menagerie'的错误,这意味着您的用户帐户没有必要的权限来执行此操作。请与管理员讨论此问题或参见第 8.2 节,“访问控制和帐户管理”。

创建数据库不会自动选择它以供使用;您必须明确执行此操作。要使menagerie成为当前数据库,请使用以下语句:

mysql> USE menagerie
Database changed

您只需创建数据库一次,但每次开始mysql会话时,必须选择它以供使用。您可以通过发出如示例中所示的USE语句来执行此操作。或者,您可以在调用mysql时在命令行上选择数据库。只需在可能需要提供的任何连接参数之后指定其名称。例如:

$> mysql -h *host* -u *user* -p menagerie
Enter password: ********

重要

在刚刚显示的命令中,menagerie不是您的密码。如果您想在-p选项后的命令行上提供密码,必须在没有空格的情况下这样做(例如,作为-p*password*,而不是作为-p *password*)。但是,将密码放在命令行上并不推荐,因为这样做会使其暴露给其他登录到您的计算机上的用户。

注意

您可以随时使用SELECT DATABASE()查看当前选择的数据库。

5.3.2 创建表

原文:dev.mysql.com/doc/refman/8.0/en/creating-tables.html

创建数据库很容易,但此时它是空的,正如SHOW TABLES所告诉你的那样:

mysql> SHOW TABLES;
Empty set (0.00 sec)

更难的部分是决定你的数据库应该是什么结构:你需要哪些表,每个表应该有哪些列。

你需要一个包含每只宠物记录的表。这个表可以称为pet表,至少应该包含每只动物的名字。因为单独的名字并不是很有趣,表中应该包含其他信息。例如,如果你家里有多个人养宠物,你可能想列出每只动物的主人。你可能还想记录一些基本的描述信息,比如物种和性别。

年龄呢?这可能是有趣的,但不适合存储在数据库中。年龄随时间变化,这意味着你必须经常更新记录。相反,最好存储一个固定值,比如出生日期。然后,每当你需要年龄时,你可以将当前日期与出生日期之间的差计算出来。MySQL 提供了进行日期运算的函数,所以这并不困难。存储出生日期而不是年龄还有其他优点:

  • 你可以使用数据库来执行诸如生成即将到来的宠物生日提醒之类的任务。(如果你认为这种查询有点傻,注意这是你在业务数据库环境中可能会问的同样的问题,以确定需要在本周或本月发送生日祝福的客户,以实现计算机辅助的个人化关怀。)

  • 你可以根据除当前日期外的日期计算年龄。例如,如果你在数据库中存储了死亡日期,你可以轻松计算宠物死亡时的年龄。

你可能会想到其他类型的信息对pet表也很有用,但到目前为止已经确定的这些足够了:名字、主人、物种、性别、出生和死亡。

使用一个CREATE TABLE语句来指定表的布局:

mysql> CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20),
       species VARCHAR(20), sex CHAR(1), birth DATE, death DATE);

VARCHARnameownerspecies列的一个很好的选择,因为列值的长度不同。这些列定义中的长度不必相同,也不必是20。你通常可以选择从165535的任何长度,看起来对你来说最合理。如果你做出了一个糟糕的选择,后来发现需要一个更长的字段,MySQL 提供了一个ALTER TABLE语句。

在动物记录中,可以选择几种类型的值来表示性别,比如'm''f',或者'male''female'。使用单个字符'm''f'是最简单的。

对于birthdeath列使用DATE数据类型是一个相当明显的选择。

一旦您创建了一个表,SHOW TABLES应该会产生一些输出:

mysql> SHOW TABLES;
+---------------------+
| Tables in menagerie |
+---------------------+
| pet                 |
+---------------------+

要验证您的表是否按预期创建,请使用DESCRIBE语句:

mysql> DESCRIBE pet;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name    | varchar(20) | YES  |     | NULL    |       |
| owner   | varchar(20) | YES  |     | NULL    |       |
| species | varchar(20) | YES  |     | NULL    |       |
| sex     | char(1)     | YES  |     | NULL    |       |
| birth   | date        | YES  |     | NULL    |       |
| death   | date        | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

您可以随时使用DESCRIBE,例如,如果您忘记了表中列的名称或它们的类型。

关于 MySQL 数据类型的更多信息,请参阅第十三章,数据类型

5.3.3 将数据加载到表中

原文:dev.mysql.com/doc/refman/8.0/en/loading-tables.html

创建表后,你需要填充它。LOAD DATAINSERT 语句对此很有用。

假设你的宠物记录可以如下所示描述。(注意 MySQL 期望日期以 '*YYYY-MM-DD*' 格式;这可能与你习惯的格式不同。)

名字主人种类性别出生死亡
FluffyHaroldf1993-02-04
ClawsGwenm1994-03-17
BuffyHaroldf1989-05-13
FangBennym1990-08-27
BowserDianem1979-08-311995-07-29
ChirpyGwenf1998-09-11
WhistlerGwen1997-12-09
SlimBennym1996-04-29

因为你从一个空表开始,一个简单的方法是创建一个包含每只动物一行的文本文件,然后用一条语句将文件内容加载到表中。

你可以创建一个文本文件 pet.txt,每行包含一条记录,值之间用制表符分隔,并按照 CREATE TABLE 语句中列出的顺序给出。对于缺失的值(例如未知性别或仍然活着的动物的死亡日期),你可以使用 NULL 值。在文本文件中表示这些值,使用 \N(反斜杠,大写 N)。例如,鸟 Whistler 的记录将如下所示(值之间的空格是一个制表符字符):

Whistler        Gwen    bird    \N      1997-12-09      \N

要将文本文件 pet.txt 加载到 pet 表中,请使用以下语句:

mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet;

如果你在 Windows 上使用以 \r\n 作为行终止符的编辑器创建文件,你应该使用这条语句:

mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet
       LINES TERMINATED BY '\r\n';

(在运行 macOS 的 Apple 机器上,你可能想使用 LINES TERMINATED BY '\r'。)

如果你愿意,可以在 LOAD DATA 语句中明确指定列值分隔符和行结束标记,但默认值是制表符和换行符。这对于语句正确读取文件 pet.txt 是足够的。

如果语句失败,很可能是因为你的 MySQL 安装默认没有启用本地文件功能。请参阅 Section 8.1.6, “LOAD DATA LOCAL 安全注意事项”,了解如何更改此设置。

当您想逐个添加新记录时,INSERT语句很有用。在其最简单的形式中,您按照CREATE TABLE语句中列出列的顺序为每一列提供值。假设黛安获得了一只名为“Puffball”的新仓鼠。您可以使用类似于以下的INSERT语句添加新记录:

mysql> INSERT INTO pet
       VALUES ('Puffball','Diane','hamster','f','1999-03-30',NULL);

字符串和日期值在此处被指定为带引号的字符串。此外,使用INSERT,您可以直接插入NULL表示缺失值。您不像在LOAD DATA中使用\N

从这个例子中,您应该能够看到,使用多个INSERT语句来最初加载您的记录将涉及更多的输入,而不是使用单个LOAD DATA语句。

5.3.4 从表中检索信息

原文:dev.mysql.com/doc/refman/8.0/en/retrieving-data.html

5.3.4.1 选择所有数据

5.3.4.2 选择特定行

5.3.4.3 选择特定列

5.3.4.4 排序行

5.3.4.5 日期计算

5.3.4.6 处理 NULL 值

5.3.4.7 模式匹配

5.3.4.8 计算行数

5.3.4.9 使用多个表

SELECT 语句用于从表中提取信息。语句的一般形式为:

SELECT *what_to_select*
FROM *which_table*
WHERE *conditions_to_satisfy*;

what_to_select 表示您想要查看的内容。这可以是列的列表,或者使用 * 表示“所有列”。 which_table 表示您要从中检索数据的表。WHERE 子句是可选的。如果存在,conditions_to_satisfy 指定行必须满足的一个或多个条件才能符合检索条件。

原文:dev.mysql.com/doc/refman/8.0/en/selecting-all.html

5.3.4.1 选择所有数据

最简单的SELECT形式从表中检索所有内容:

mysql> SELECT * FROM pet;
+----------+--------+---------+------+------------+------------+
| name     | owner  | species | sex  | birth      | death      |
+----------+--------+---------+------+------------+------------+
| Fluffy   | Harold | cat     | f    | 1993-02-04 | NULL       |
| Claws    | Gwen   | cat     | m    | 1994-03-17 | NULL       |
| Buffy    | Harold | dog     | f    | 1989-05-13 | NULL       |
| Fang     | Benny  | dog     | m    | 1990-08-27 | NULL       |
| Bowser   | Diane  | dog     | m    | 1979-08-31 | 1995-07-29 |
| Chirpy   | Gwen   | bird    | f    | 1998-09-11 | NULL       |
| Whistler | Gwen   | bird    | NULL | 1997-12-09 | NULL       |
| Slim     | Benny  | snake   | m    | 1996-04-29 | NULL       |
| Puffball | Diane  | hamster | f    | 1999-03-30 | NULL       |
+----------+--------+---------+------+------------+------------+

这种形式的SELECT使用*,它是“选择所有列”的简写。如果您想要查看整个表格,例如,在刚刚加载初始数据集后,这是很有用的。例如,您可能会觉得 Bowser 的出生日期似乎不太对。查阅您的原始血统文件,您发现正确的出生年份应该是 1989 年,而不是 1979 年。

至少有两种方法可以解决这个问题:

  • 编辑文件pet.txt以更正错误,然后使用DELETELOAD DATA清空表格并重新加载:

    mysql> DELETE FROM pet;
    mysql> LOAD DATA LOCAL INFILE 'pet.txt' INTO TABLE pet;
    

    但是,如果这样做,您还必须重新输入 Puffball 的记录。

  • 仅使用UPDATE语句修复错误记录:

    mysql> UPDATE pet SET birth = '1989-08-31' WHERE name = 'Bowser';
    

    UPDATE仅更改相关记录,无需重新加载表格。

有一个例外,即SELECT *选择所有列的原则。如果表中包含不可见列,则*不包括它们。有关更多信息,请参见第 15.1.20.10 节,“不可见列”。

原文:dev.mysql.com/doc/refman/8.0/en/selecting-rows.html

5.3.4.2 选择特定行

如前一节所示,检索整个表很容易。只需从SELECT语句中省略WHERE子句。但通常你不想看到整个表,特别是当它变得很大时。相反,你通常更感兴趣的是回答一个特定的问题,这时你会对你想要的信息指定一些约束条件。让我们看一些关于回答有关宠物的问题的选择查询。

你可以仅选择表中特定的行。例如,如果你想验证对鲍泽的出生日期所做的更改,可以像这样选择鲍泽的记录:

mysql> SELECT * FROM pet WHERE name = 'Bowser';
+--------+-------+---------+------+------------+------------+
| name   | owner | species | sex  | birth      | death      |
+--------+-------+---------+------+------------+------------+
| Bowser | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
+--------+-------+---------+------+------------+------------+

输出确认年份正确记录为 1989 年,而不是 1979 年。

字符串比较通常是不区分大小写的,因此你可以将名称指定为'bowser''BOWSER'等。查询结果是相同的。

你可以对任何列指定条件,不仅仅是name。例如,如果你想知道哪些动物是在 1998 年或之后出生的,请测试birth列:

mysql> SELECT * FROM pet WHERE birth >= '1998-1-1';
+----------+-------+---------+------+------------+-------+
| name     | owner | species | sex  | birth      | death |
+----------+-------+---------+------+------------+-------+
| Chirpy   | Gwen  | bird    | f    | 1998-09-11 | NULL  |
| Puffball | Diane | hamster | f    | 1999-03-30 | NULL  |
+----------+-------+---------+------+------------+-------+

你可以组合条件,例如,定位雌性狗:

mysql> SELECT * FROM pet WHERE species = 'dog' AND sex = 'f';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

前面的查询使用了AND逻辑运算符。还有一个OR运算符:

mysql> SELECT * FROM pet WHERE species = 'snake' OR species = 'bird';
+----------+-------+---------+------+------------+-------+
| name     | owner | species | sex  | birth      | death |
+----------+-------+---------+------+------------+-------+
| Chirpy   | Gwen  | bird    | f    | 1998-09-11 | NULL  |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL  |
| Slim     | Benny | snake   | m    | 1996-04-29 | NULL  |
+----------+-------+---------+------+------------+-------+

ANDOR可以混合使用,尽管ANDOR具有更高的优先级。如果同时使用这两个运算符,最好使用括号明确指示条件应如何分组:

mysql> SELECT * FROM pet WHERE (species = 'cat' AND sex = 'm')
       OR (species = 'dog' AND sex = 'f');
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

原文:dev.mysql.com/doc/refman/8.0/en/selecting-columns.html

5.3.4.3 选择特定列

如果您不想看到表中的整行数据,只需按逗号分隔感兴趣的列名即可。例如,如果您想知道动物的出生日期,选择namebirth列:

mysql> SELECT name, birth FROM pet;
+----------+------------+
| name     | birth      |
+----------+------------+
| Fluffy   | 1993-02-04 |
| Claws    | 1994-03-17 |
| Buffy    | 1989-05-13 |
| Fang     | 1990-08-27 |
| Bowser   | 1989-08-31 |
| Chirpy   | 1998-09-11 |
| Whistler | 1997-12-09 |
| Slim     | 1996-04-29 |
| Puffball | 1999-03-30 |
+----------+------------+

要找出谁拥有宠物,请使用此查询:

mysql> SELECT owner FROM pet;
+--------+
| owner  |
+--------+
| Harold |
| Gwen   |
| Harold |
| Benny  |
| Diane  |
| Gwen   |
| Gwen   |
| Benny  |
| Diane  |
+--------+

注意,查询仅从每条记录中检索owner列,并且有些列会出现多次。为了最小化输出,通过添加关键字DISTINCT仅检索每个唯一的输出记录一次:

mysql> SELECT DISTINCT owner FROM pet;
+--------+
| owner  |
+--------+
| Benny  |
| Diane  |
| Gwen   |
| Harold |
+--------+

您可以使用WHERE子句将行选择与列选择结合起来。例如,要仅获取狗和猫的出生日期,请使用以下查询:

mysql> SELECT name, species, birth FROM pet
       WHERE species = 'dog' OR species = 'cat';
+--------+---------+------------+
| name   | species | birth      |
+--------+---------+------------+
| Fluffy | cat     | 1993-02-04 |
| Claws  | cat     | 1994-03-17 |
| Buffy  | dog     | 1989-05-13 |
| Fang   | dog     | 1990-08-27 |
| Bowser | dog     | 1989-08-31 |
+--------+---------+------------+

原文:dev.mysql.com/doc/refman/8.0/en/sorting-rows.html

5.3.4.4 排序行

您可能已经注意到在前面的示例中,结果行没有按特定顺序显示。当行以某种有意义的方式排序时,检查查询输出通常更容易。要对结果进行排序,请使用ORDER BY子句。

这里是按日期排序的动物生日:

mysql> SELECT name, birth FROM pet ORDER BY birth;
+----------+------------+
| name     | birth      |
+----------+------------+
| Buffy    | 1989-05-13 |
| Bowser   | 1989-08-31 |
| Fang     | 1990-08-27 |
| Fluffy   | 1993-02-04 |
| Claws    | 1994-03-17 |
| Slim     | 1996-04-29 |
| Whistler | 1997-12-09 |
| Chirpy   | 1998-09-11 |
| Puffball | 1999-03-30 |
+----------+------------+

在字符类型列上,排序——就像所有其他比较操作一样——通常以不区分大小写的方式执行。这意味着对于除大小写外完全相同的列,排序是未定义的。您可以通过使用BINARY来强制对列进行区分大小写排序,如下所示:ORDER BY BINARY *col_name*

默认排序顺序是升序,最小值优先。要以相反(降序)顺序排序,请在要排序的列名后添加DESC关键字:

mysql> SELECT name, birth FROM pet ORDER BY birth DESC;
+----------+------------+
| name     | birth      |
+----------+------------+
| Puffball | 1999-03-30 |
| Chirpy   | 1998-09-11 |
| Whistler | 1997-12-09 |
| Slim     | 1996-04-29 |
| Claws    | 1994-03-17 |
| Fluffy   | 1993-02-04 |
| Fang     | 1990-08-27 |
| Bowser   | 1989-08-31 |
| Buffy    | 1989-05-13 |
+----------+------------+

您可以按多个列进行排序,并且可以按不同方向对不同列进行排序。例如,要按动物类型升序排序,然后按动物类型内的出生日期降序排序(最年轻的动物优先),请使用以下查询:

mysql> SELECT name, species, birth FROM pet
       ORDER BY species, birth DESC;
+----------+---------+------------+
| name     | species | birth      |
+----------+---------+------------+
| Chirpy   | bird    | 1998-09-11 |
| Whistler | bird    | 1997-12-09 |
| Claws    | cat     | 1994-03-17 |
| Fluffy   | cat     | 1993-02-04 |
| Fang     | dog     | 1990-08-27 |
| Bowser   | dog     | 1989-08-31 |
| Buffy    | dog     | 1989-05-13 |
| Puffball | hamster | 1999-03-30 |
| Slim     | snake   | 1996-04-29 |
+----------+---------+------------+

DESC关键字仅适用于紧随其后的列名(birth);它不会影响species列的排序顺序。

原文:dev.mysql.com/doc/refman/8.0/en/date-calculations.html

5.3.4.5 日期计算

MySQL 提供了几个函数,您可以使用这些函数对日期进行计算,例如计算年龄或提取日期的部分。

要确定每只宠物的年龄,可以使用TIMESTAMPDIFF()函数。它的参数是您希望结果表达的单位,以及要计算差异的两个日期。以下查询显示了每只宠物的出生日期、当前日期和年龄。使用别名age)使最终输出列标签更有意义。

mysql> SELECT name, birth, CURDATE(),
       TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
       FROM pet;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
+----------+------------+------------+------+

查询是有效的,但如果按某种顺序呈现行,则结果可以更容易地扫描。这可以通过添加一个ORDER BY name子句来实现,以按名称对输出进行排序:

mysql> SELECT name, birth, CURDATE(),
       TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
       FROM pet ORDER BY name;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
+----------+------------+------------+------+

要按age而不是name对输出进行排序,只需使用不同的ORDER BY子句:

mysql> SELECT name, birth, CURDATE(),
       TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
       FROM pet ORDER BY age;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
+----------+------------+------------+------+

可以使用类似的查询来确定已经去世动物的死亡年龄。通过检查death值是否为NULL来确定这些动物,然后对具有非NULL值的动物计算deathbirth值之间的差异:

mysql> SELECT name, birth, death,
       TIMESTAMPDIFF(YEAR,birth,death) AS age
       FROM pet WHERE death IS NOT NULL ORDER BY age;
+--------+------------+------------+------+
| name   | birth      | death      | age  |
+--------+------------+------------+------+
| Bowser | 1989-08-31 | 1995-07-29 |    5 |
+--------+------------+------------+------+

查询使用death IS NOT NULL而不是death <> NULL,因为NULL是一个特殊值,不能使用通常的比较运算符进行比较。这将在后面讨论。请参阅第 5.3.4.6 节,“处理 NULL 值”。

如果您想知道哪些动物下个月过生日怎么办?对于这种类型的计算,年份和日期都不重要;您只需提取birth列的月份部分。MySQL 提供了几个用于提取日期部分的函数,例如YEAR()MONTH()DAYOFMONTH()。在这里,MONTH()是适当的函数。要查看它的工作原理,请运行一个显示birthMONTH(birth)值的简单查询:

mysql> SELECT name, birth, MONTH(birth) FROM pet;
+----------+------------+--------------+
| name     | birth      | MONTH(birth) |
+----------+------------+--------------+
| Fluffy   | 1993-02-04 |            2 |
| Claws    | 1994-03-17 |            3 |
| Buffy    | 1989-05-13 |            5 |
| Fang     | 1990-08-27 |            8 |
| Bowser   | 1989-08-31 |            8 |
| Chirpy   | 1998-09-11 |            9 |
| Whistler | 1997-12-09 |           12 |
| Slim     | 1996-04-29 |            4 |
| Puffball | 1999-03-30 |            3 |
+----------+------------+--------------+

查找下个月过生日的动物也很简单。假设当前月份是四月。那么月份值为4,您可以这样查找出生在五月(月份5)的动物:

mysql> SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
+-------+------------+
| name  | birth      |
+-------+------------+
| Buffy | 1989-05-13 |
+-------+------------+

如果当前月份是十二月,会有一个小复杂性。您不能简单地将月份数字(12)加一,然后查找出生在第13个月的动物,因为没有这样的月份。相反,您应该查找出生在一月(月份1)的动物。

您可以编写查询,使其无论当前月份是什么都能正常工作,这样您就不必使用特定月份的数字。DATE_ADD()使您能够向给定日期添加时间间隔。如果您将一个月添加到CURDATE()的值,然后用MONTH()提取月份部分,结果将产生要查找生日的月份:

mysql> SELECT name, birth FROM pet
       WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL 1 MONTH));

通过在使用模运算(MOD)将月份值包装为0后,将1添加到当前月份以获得下一个月份的另一种方法:

mysql> SELECT name, birth FROM pet
       WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;

MONTH()返回112之间的数字。而MOD(某物,12)返回011之间的数字。因此,加法操作必须在MOD()之后进行,否则我们会从十一月(11)跳到一月(1)。

如果计算使用无效日期,则计算将失败并产生警告:

mysql> SELECT '2018-10-31' + INTERVAL 1 DAY;
+-------------------------------+
| '2018-10-31' + INTERVAL 1 DAY |
+-------------------------------+
| 2018-11-01                    |
+-------------------------------+
mysql> SELECT '2018-10-32' + INTERVAL 1 DAY;
+-------------------------------+
| '2018-10-32' + INTERVAL 1 DAY |
+-------------------------------+
| NULL                          |
+-------------------------------+
mysql> SHOW WARNINGS;
+---------+------+----------------------------------------+
| Level   | Code | Message                                |
+---------+------+----------------------------------------+
| Warning | 1292 | Incorrect datetime value: '2018-10-32' |
+---------+------+----------------------------------------+

原文:dev.mysql.com/doc/refman/8.0/en/working-with-null.html

5.3.4.6 处理 NULL 值

直到你习惯了,NULL值可能会让人感到惊讶。从概念上讲,NULL表示“缺失的未知值”,并且它与其他值的处理方式略有不同。

要测试NULL,请使用IS NULLIS NOT NULL运算符,如下所示:

mysql> SELECT 1 IS NULL, 1 IS NOT NULL;
+-----------+---------------+
| 1 IS NULL | 1 IS NOT NULL |
+-----------+---------------+
|         0 |             1 |
+-----------+---------------+

您不能使用算术比较运算符如=<<>来测试NULL。为了自己演示这一点,请尝试以下查询:

mysql> SELECT 1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL;
+----------+-----------+----------+----------+
| 1 = NULL | 1 <> NULL | 1 < NULL | 1 > NULL |
+----------+-----------+----------+----------+
|     NULL |      NULL |     NULL |     NULL |
+----------+-----------+----------+----------+

因为与NULL进行任何算术比较的结果也是NULL,所以您无法从这些比较中获得任何有意义的结果。

在 MySQL 中,0NULL表示假,而其他任何值表示真。布尔运算的默认真值为1

这种对NULL的特殊处理是为什么在前一节中需要使用death IS NOT NULL而不是death <> NULL来确定哪些动物不再活着。

两个NULL值在GROUP BY中被视为相等。

在进行ORDER BY时,如果使用ORDER BY ... ASC,则NULL值会首先呈现,如果使用ORDER BY ... DESC,则会最后呈现。

处理NULL时的一个常见错误是假设无法将零或空字符串插入定义为NOT NULL的列,但事实并非如此。这些实际上是值,而NULL表示“没有值”。您可以通过使用IS [NOT] NULL来轻松测试,如下所示:

mysql> SELECT 0 IS NULL, 0 IS NOT NULL, '' IS NULL, '' IS NOT NULL;
+-----------+---------------+------------+----------------+
| 0 IS NULL | 0 IS NOT NULL | '' IS NULL | '' IS NOT NULL |
+-----------+---------------+------------+----------------+
|         0 |             1 |          0 |              1 |
+-----------+---------------+------------+----------------+

因此,完全可以将零或空字符串插入NOT NULL列,因为这些实际上是NOT NULL。参见 Section B.3.4.3, “Problems with NULL Values”。

原文:dev.mysql.com/doc/refman/8.0/en/pattern-matching.html

5.3.4.7 模式匹配

MySQL 提供标准 SQL 模式匹配以及一种基于扩展正则表达式的模式匹配形式,类似于 Unix 实用程序(如vigrepsed)中使用的形式。

SQL 模式匹配使您可以使用_匹配任意单个字符和%匹配任意数量的字符(包括零个字符)。在 MySQL 中,默认情况下,SQL 模式是不区分大小写的。这里显示了一些示例。在使用 SQL 模式时,请不要使用=<>。请改用LIKENOT LIKE比较运算符。

要查找以b开头的名称:

mysql> SELECT * FROM pet WHERE name LIKE 'b%';
+--------+--------+---------+------+------------+------------+
| name   | owner  | species | sex  | birth      | death      |
+--------+--------+---------+------+------------+------------+
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL       |
| Bowser | Diane  | dog     | m    | 1989-08-31 | 1995-07-29 |
+--------+--------+---------+------+------------+------------+

要查找以fy结尾的名称:

mysql> SELECT * FROM pet WHERE name LIKE '%fy';
+--------+--------+---------+------+------------+-------+
| name   | owner  | species | sex  | birth      | death |
+--------+--------+---------+------+------------+-------+
| Fluffy | Harold | cat     | f    | 1993-02-04 | NULL  |
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL  |
+--------+--------+---------+------+------------+-------+

要查找包含w的名称:

mysql> SELECT * FROM pet WHERE name LIKE '%w%';
+----------+-------+---------+------+------------+------------+
| name     | owner | species | sex  | birth      | death      |
+----------+-------+---------+------+------------+------------+
| Claws    | Gwen  | cat     | m    | 1994-03-17 | NULL       |
| Bowser   | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL       |
+----------+-------+---------+------+------------+------------+

要查找包含正好五个字符的名称,请使用五个_模式字符的实例:

mysql> SELECT * FROM pet WHERE name LIKE '_____';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

MySQL 提供的另一种模式匹配类型使用扩展正则表达式。当您测试此类型模式的匹配时,请使用REGEXP_LIKE()函数(或REGEXPRLIKE运算符,它们是REGEXP_LIKE()的同义词)。

以下列表描述了扩展正则表达式的一些特征:

  • .匹配任意单个字符。

  • 字符类[...]匹配括号内的任何字符。例如,[abc]匹配abc。要命名一系列字符,请使用短划线。[a-z]匹配任何字母,而[0-9]匹配任何数字。

  • *匹配其前面的内容的零个或多个实例。例如,x*匹配任意数量的x字符,[0-9]*匹配任意数量的数字,.*匹配任意数量的任何内容。

  • 如果正则表达式模式匹配值的任何位置,则正则表达式模式匹配成功。(这与LIKE模式匹配不同,后者仅在模式完全匹配值时才成功。)

  • 要锚定模式,使其必须匹配值的开头或结尾,请在模式的开头使用^或在结尾使用$

为了演示扩展正则表达式的工作原理,先前显示的LIKE查询在这里重新编写为使用REGEXP_LIKE()

要查找以b开头的名称,请使用^匹配名称的开头:

mysql> SELECT * FROM pet WHERE REGEXP_LIKE(name, '^b');
+--------+--------+---------+------+------------+------------+
| name   | owner  | species | sex  | birth      | death      |
+--------+--------+---------+------+------------+------------+
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL       |
| Bowser | Diane  | dog     | m    | 1979-08-31 | 1995-07-29 |
+--------+--------+---------+------+------------+------------+

要强制正则表达式比较区分大小写,请使用区分大小写的排序规则,或使用BINARY关键字使其中一个字符串成为二进制字符串,或指定c匹配控制字符。每个查询仅匹配名称开头的小写b

SELECT * FROM pet WHERE REGEXP_LIKE(name, '^b' COLLATE utf8mb4_0900_as_cs);
SELECT * FROM pet WHERE REGEXP_LIKE(name, BINARY '^b');
SELECT * FROM pet WHERE REGEXP_LIKE(name, '^b', 'c');

要查找以fy结尾的名称,请使用$来匹配名称的结尾:

mysql> SELECT * FROM pet WHERE REGEXP_LIKE(name, 'fy$');
+--------+--------+---------+------+------------+-------+
| name   | owner  | species | sex  | birth      | death |
+--------+--------+---------+------+------------+-------+
| Fluffy | Harold | cat     | f    | 1993-02-04 | NULL  |
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL  |
+--------+--------+---------+------+------------+-------+

要查找包含w的名称,请使用此查询:

mysql> SELECT * FROM pet WHERE REGEXP_LIKE(name, 'w');
+----------+-------+---------+------+------------+------------+
| name     | owner | species | sex  | birth      | death      |
+----------+-------+---------+------+------------+------------+
| Claws    | Gwen  | cat     | m    | 1994-03-17 | NULL       |
| Bowser   | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL       |
+----------+-------+---------+------+------------+------------+

因为正则表达式模式匹配的是值中的任何位置,所以在前面的查询中,不需要在模式的两侧放置通配符来使其匹配整个值,这与 SQL 模式不同。

要查找恰好包含五个字符的名称,请使用^$来匹配名称的开头和结尾,并在中间使用五个.的实例:

mysql> SELECT * FROM pet WHERE REGEXP_LIKE(name, '^.....$');
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

使用{*n*}(“重复-n-次”)操作符,你也可以编写前面的查询:

mysql> SELECT * FROM pet WHERE REGEXP_LIKE(name, '^.{5}$');
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

有关正则表达式语法的更多信息,请参见第 14.8.2 节,“正则表达式”。

译文:dev.mysql.com/doc/refman/8.0/en/counting-rows.html

5.3.4.8 计算行数

数据库经常用于回答问题,“表中某种类型的数据发生多少次?” 例如,您可能想知道您有多少宠物,或每个所有者有多少宠物,或者您可能想对您的动物执行各种普查操作。

计算您拥有的动物总数与“pet 表中有多少行?”是同一个问题,因为每只宠物有一条记录。COUNT(*) 计算行数,因此计算您的动物的查询如下所示:

mysql> SELECT COUNT(*) FROM pet;
+----------+
| COUNT(*) |
+----------+
|        9 |
+----------+

之前,您检索了拥有宠物的人的姓名。如果您想知道每个所有者有多少宠物,可以使用 COUNT()

mysql> SELECT owner, COUNT(*) FROM pet GROUP BY owner;
+--------+----------+
| owner  | COUNT(*) |
+--------+----------+
| Benny  |        2 |
| Diane  |        2 |
| Gwen   |        3 |
| Harold |        2 |
+--------+----------+

前面的查询使用 GROUP BY 将每个 owner 的所有记录分组。与 GROUP BY 结合使用 COUNT() 有助于对各种分组下的数据进行表征。以下示例展示了执行动物普查操作的不同方法。

每种物种的动物数量:

mysql> SELECT species, COUNT(*) FROM pet GROUP BY species;
+---------+----------+
| species | COUNT(*) |
+---------+----------+
| bird    |        2 |
| cat     |        2 |
| dog     |        3 |
| hamster |        1 |
| snake   |        1 |
+---------+----------+

每种性别的动物数量:

mysql> SELECT sex, COUNT(*) FROM pet GROUP BY sex;
+------+----------+
| sex  | COUNT(*) |
+------+----------+
| NULL |        1 |
| f    |        4 |
| m    |        4 |
+------+----------+

(在此输出中,NULL 表示性别未知。)

每种物种和性别组合的动物数量:

mysql> SELECT species, sex, COUNT(*) FROM pet GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| bird    | NULL |        1 |
| bird    | f    |        1 |
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
| hamster | f    |        1 |
| snake   | m    |        1 |
+---------+------+----------+

当使用 COUNT() 时,您无需检索整个表。例如,仅对狗和猫执行上一个查询时,如下所示:

mysql> SELECT species, sex, COUNT(*) FROM pet
       WHERE species = 'dog' OR species = 'cat'
       GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
+---------+------+----------+

或者,如果您只想知道已知性别的动物每种性别的数量:

mysql> SELECT species, sex, COUNT(*) FROM pet
       WHERE sex IS NOT NULL
       GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| bird    | f    |        1 |
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
| hamster | f    |        1 |
| snake   | m    |        1 |
+---------+------+----------+

如果您在选择要选择的列时还命名了列以外的 COUNT() 值,则应该存在一个包含命名相同列的 GROUP BY 子句。否则,将发生以下情况:

  • 如果启用了 ONLY_FULL_GROUP_BY SQL 模式,则会出现错误:

    mysql> SET sql_mode = 'ONLY_FULL_GROUP_BY';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SELECT owner, COUNT(*) FROM pet;
    ERROR 1140 (42000): In aggregated query without GROUP BY, expression
    #1 of SELECT list contains nonaggregated column 'menagerie.pet.owner';
    this is incompatible with sql_mode=only_full_group_by
    
  • 如果未启用 ONLY_FULL_GROUP_BY,则查询将通过将所有行视为单个组来处理,但为每个命名列选择的值是不确定的。服务器可以从任何行中选择值:

    mysql> SET sql_mode = '';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SELECT owner, COUNT(*) FROM pet;
    +--------+----------+
    | owner  | COUNT(*) |
    +--------+----------+
    | Harold |        8 |
    +--------+----------+
    1 row in set (0.00 sec)
    

参见 第 14.19.3 节,“MySQL 对 GROUP BY 的处理”。有关 COUNT(*expr*) 的行为和相关优化,请参见 第 14.19.1 节,“聚合函数描述”。