• GROUP BY 之后不能直接引用原表中的列


    标准 SQL 规定,在对表进行聚合查询的时候,只能在 SELECT 子句中写下面 3 种内容:通过 GROUP BY 子句指定的聚合键、聚合函数(SUM 、AVG 等)、常量。我们来看个例子:

    我们有 学生班级表(tb_student_class) 以及 数据如下 :

    DROP TABLE IF EXISTS tb_student_class;
    CREATE TABLE tb_student_class (
      id int(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
      sno varchar(12) NOT NULL COMMENT '学号',
      cno varchar(5) NOT NULL COMMENT '班级号',
      cname varchar(20) NOT NULL COMMENT '班级名',
      PRIMARY KEY (id)
    ) COMMENT='学生班级表';
    
    -- ----------------------------
    -- Records of tb_student_class
    -- ----------------------------
    INSERT INTO tb_student_class VALUES ('1', '20200607001', '0607', '7班');
    INSERT INTO tb_student_class VALUES ('2', '20200607002', '0607', '7班');
    INSERT INTO tb_student_class VALUES ('3', '20200608003', '0608', '8班');
    INSERT INTO tb_student_class VALUES ('4', '20200608004', '0608', '8班');
    INSERT INTO tb_student_class VALUES ('5', '20200609005', '0609', '9班');
    INSERT INTO tb_student_class VALUES ('6', '20200609006', '0609', '9班');

      如果我们想统计各个班(班级号、班级名)一个有多少人、以及最大的学号,那么这个SQL应该 如下:

    SELECT cno,cname,count(sno),MAX(sno) 
    FROM tb_student_class
    GROUP BY cno,cname;

      cno 和 cname 本来就是一对一,cno 一旦确定,cname 也就确定了,那 SQL 是不是可以这么写 ?

    SELECT cno,cname,count(sno),MAX(sno) 
    FROM tb_student_class
    GROUP BY cno;

      执行报错了:

    [Err] 1055 - Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.tb_student_class.cname' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

      提示信息:SELECT 列表中的第二个表达式(cname)不在 GROUP BY 的子句中,同时它也不是聚合函数;这与 sql 模式:ONLY_FULL_GROUP_BY 不相容。

    SQL 模式

      MySQL 服务器可以在不同的 SQL 模式下运行,并且可以针对不同的客户端以不同的方式应用这些模式,具体取决于 sql_mode 系统变量的值。DBA 可以设置全局SQL模式以匹配站点服务器操作要求,并且每个应用程序可以将其会话 SQL 模式设置为其自己的要求。

      模式会影响 MySQL 支持的 SQL 语法以及它执行的 数据验证检查,这使得在不同环境中使用MySQL以及将MySQL与其他数据库服务器一起使用变得更加容易。更多详情请查阅官网:Server SQL Modes。

      MySQL 版本不同,内容会略有不同(包括默认值),查阅的时候注意与自身的 MySQL 版本保持一致。

      SQL 模式主要分两类:语法支持类和数据检查类,常用的如下

    语法支持类

    • ONLY_FULL_GROUP_BY

        对于 GROUP BY 聚合操作,如果在 SELECT 中的列、HAVING 或者 ORDER BY 子句的列,没有在GROUP BY中出现,那么这个SQL是不合法的。

    • ANSI_QUOTES

        启用 ANSI_QUOTES 后,不能用双引号来引用字符串,因为它被解释为识别符,作用与 ` 一样。设置它以后,update t set f1="" …,会报 Unknown column ‘’ in field list 这样的语法错误。

    • PIPES_AS_CONCAT

        将 || 视为字符串的连接操作符而非 或 运算符,这和Oracle数据库是一样的,也和字符串的拼接函数 CONCAT() 相类似。

    • NO_TABLE_OPTIONS

        使用 SHOW CREATE TABLE 时不会输出MySQL特有的语法部分,如 ENGINE ,这个在使用 mysqldump 跨DB种类迁移的时候需要考虑。

    • NO_AUTO_CREATE_USER

        字面意思不自动创建用户。在给MySQL用户授权时,我们习惯使用 GRANT … ON … TO dbuser 顺道一起创建用户。设置该选项后就与oracle操作类似,授权之前必须先建立用户。

    数据检查类

    • NO_ZERO_DATE

        认为日期 ‘0000-00-00’ 非法,与是否设置后面的严格模式有关

      1、如果设置了严格模式,则 NO_ZERO_DATE 自然满足。但如果是 INSERT IGNORE UPDATE IGNORE’0000-00-00’依然允许且只显示warning

      2、如果在非严格模式下,设置了NO_ZERO_DATE,效果与上面一样,’0000-00-00’ 允许但显示warning;如果没有设置NO_ZERO_DATEno warning,当做完全合法的值;

      3NO_ZERO_IN_DATE情况与上面类似,不同的是控制日期和天,是否可为 0 ,即 2010-01-00 是否合法;

    • NO_ENGINE_SUBSTITUTION

        使用 ALTER TABLE 或 CREATE TABLE 指定 ENGINE 时, 需要的存储引擎被禁用或未编译,该如何处理。启用 NO_ENGINE_SUBSTITUTION 时,那么直接抛出错误;不设置此值时,CREATE用默认的存储引擎替代,ATLER不进行更改,并抛出一个 warning

    • STRICT_TRANS_TABLES

        设置它,表示启用严格模式。注意 STRICT_TRANS_TABLES 不是几种策略的组合,单独指 INSERT、UPDATE 出现少值或无效值该如何处理:

        1、前面提到的把 ‘’ 传给int,严格模式下非法,若启用非严格模式则变成 0,产生一个warning;

        2、Out Of Range,变成插入最大边界值;

        3、当要插入的新行中,不包含其定义中没有显式DEFAULT子句的非NULL列的值时,该列缺少值;

    默认模式

      当我们没有修改配置文件的情况下,MySQL 是有自己的默认模式的;版本不同,默认模式也不同;

    -- 查看 MySQL 版本
    SELECT VERSION();
    -- 查看 sql_mode
    SELECT @@sql_mode;

      当我们将“ONLY_FULL_GROUP_BY”模式去掉后发现上述报错的 SQL能正常执行了。但是一般情况下不推荐这样配置,线上环境往往是“严格模式”,而不是“宽松模式”;虽然案例中,无论是“严格模式”,还是“宽松模式”,结果都是对的,那是因为 cno 与 cname 唯一对应的,如果 cno 与 cname 不是唯一对应,那么在“宽松模式下” cname 的值是随机的,这就会造成难以排查的问题,有兴趣的可以去试试。

  • 相关阅读:
    JavaScript连载32-常用的鼠标事件
    Java连载138-数据库删除数据以及编译预处理
    C连载22-scanf转换说明中的修饰符
    Android连载32-实现登录密码存储功能
    JavaScript连载31-图片动态切换以及关闭图片案例
    搭建一个开源项目15-解决安装mysql不成功的问题
    Java连载137-更新数据和删除数据
    从零开始学VUE之组件化开发(注册父子结构组件)
    从零开始学VUE之组件化开发(注册局部组件)
    从零开始学VUE之组件化开发(注册全局组件)
  • 原文地址:https://www.cnblogs.com/ljch/p/13685987.html
Copyright © 2020-2023  润新知