08.DQL-从select开始 - 图1

    SELECT开始的SQL查询语句

    本章节我们将从select语句开始学习如何对数据进⾏检索,
    ⽐如对数据进⾏单列或多列的获取,对数据进⾏过滤,排序,分组等操作。
    这些都是⾮常基础且⾮常重要的基本语法,只有掌握了这些基础我们才能在后⾯的章节中学习更
    复杂的SQL。
    SQL示例:
    — 创建表
    CREATE TABLE stu (
    id int(10 ) unsigned NOT NULL AUTO_INCREMENT ,
    name varchar(10 ) NOT NULL ,
    email char(50 ) NOT NULL ,
    phone char(11 ) NOT NULL ,
    age tinyint(4 ) NOT NULL ,
    sex char(1 ) DEFAULT NULL ,
    class_id int(10 ) DEFAULT NULL ,
    PRIMARY KEY (id)
    ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
    — 添加数据
    insert into stu values
    (null , ‘张三’ , ‘zhangsan@qq.com ‘ , ‘13701104321’ ,21 , ‘男 ‘ ,1 ),
    (null , ‘李四 ‘ , ‘lisi@qq.com ‘ , ‘13701104322’ ,22 , ‘男 ‘ ,1 ),
    (null , ‘王五 ‘ , ‘wangwu@qq.com ‘ , ‘13701104323’ ,20 , ‘⼥ ‘ ,1 ),
    (null , ‘赵六 ‘ , ‘zhaoliu@qq.com ‘ , ‘13701104324’ ,19 , ‘男 ‘ ,1 ),
    (null , ‘ ⽥七’ , ‘tianqi@qq.com ‘ , ‘13701104325’ ,23 , ‘⼥ ‘ ,1 ),
    (null , ‘王五六 ‘ , ‘wangwuliu@qq.com ‘ , ‘13701104326’ ,23 , ‘⼥ ‘ ,1 ),
    (null , ‘熊⼤’ , ‘xiongda@qq.com ‘ , ‘13701104327’ ,25 , ‘男 ‘ ,2 ),
    (null , ‘熊⼆ ‘ , ‘xionger@qq.com ‘ , ‘13701104328’ ,22 , ‘男 ‘ ,2 ),
    (null , ‘⼀⼀ ‘ , ‘yiyi@qq.com ‘ , ‘13701104329’ ,19 , ‘⼥ ‘ ,2 ),
    (null , ‘呀呀’ , ‘yaya@qq.com ‘ , ‘13701104320’ ,22 , ‘男 ‘ ,2 );
    — 查看表数据
    select * from stu;

    检索数据select

    检索单个列
    select name from stu
    08.DQL-从select开始 - 图2

    如果没有明确排序查询结果(下⼀章介绍),则返回的数据的顺序没有特殊意义。
    返回数据的顺序可能是数据被添加到表中的顺序,也可能不是。只要返回相同数⽬的⾏,就是正 常的

    检索多个列
    select id,name,age,sex from stu

    在选择多个列时,⼀定要在列名之间加上逗号,但最后⼀个列名后不加。
    如果在最后⼀个列名后加了逗号,将出现错误。

    检索所有列
    select * from stu

    使⽤通配符 ⼀般,除⾮你确实需要表中的每个列,否则最好别使⽤*通配符。
    虽然使⽤通配符可能会使你⾃⼰省事,不⽤明确列出所需列,但检索不需要的列通常会降低检索 和应⽤程序的性能。
    使⽤通配符有⼀个⼤优点。由于不明确指定列名(因为星号检索每个列),所以能检索出名字未 知的列。

    检索不同的⾏ DISTINCT
    select distinct classid from stu

    DISTINCT关键字,顾名思义,此关键字指示MySQL只返回不同的值
    DISTINCT关键字应⽤于所有列⽽不仅是前置它的列。
    如果给出SELECT DISTINCTvend_id, prod_price,除⾮指定的两个列都不同,否则所有⾏都将被检 索出来

    限制结果 LIMIT
    select * from stu limit 3,4

    LIMIT 3, 4的含义是从⾏3开始的4⾏.(跳过前3⾏ ,取4⾏)

    08.DQL-从select开始 - 图3

    替代语法 LIMIT 4 OFFSET 3意为从⾏3开始取4⾏,就像LIMIT 3,4⼀样。

    使⽤完全限定的表名
    select name from stu
    select stu.name from stu
    select stu.name from chuange.stu

    对检索数据进⾏排序 ORDER BY

    排序数据 ORDER BY
    select * from stu order by age

    默认查询出的数据,并不是随机排序的,如果没有指定排序,数据⼀般将以它在底层表中出现的顺序显 示
    关系数据库设计理论认为,如果不明确规定排序顺序,则不应该假定检索出的数据的顺序有意义 通常, ORDER BY⼦句中使⽤的列将是为显示所选择的列。
    但是,实际上并不⼀定要这样,⽤⾮检索的列排序数据是完全合法的。

    按多个列排序
    select * from stu order by classid,age

    在需要对多列数据进⾏排序时,使⽤逗号分隔列名,并会按照前后顺序依次对⽐排序
    order by 的排序默认升序,可以使⽤DESC设置降序排列
    select * from stu order by classid,age DESC

    以上语句就是先对classid进⾏升序排序,然后在结果中对age进⾏降序排序

    注意

    ORDER BY⼦句的位置 在给出ORDER BY⼦句时,应该保证它位于FROM⼦句之后。
    如果使⽤LIMIT,它必须位于ORDER BY之后。使⽤⼦句的次序不对将产⽣错误消息

    08.DQL-从select开始 - 图4

    数据检索条件过滤WHERE

    过 滤 数 据 WHERE

    数据库表⼀般包含⼤量的数据,很少需要检索表中所有⾏。
    通常只会根据特定操作或报告的需要提取表数据的⼦集。
    只检索所需数据需要指定搜索条件(searchcriteria),搜索条件也称为过滤条件(filter
    condition)。 在SELECT语句中,数据根据WHERE⼦句中指定的搜索条件进⾏过滤。
    select name from stu where age = 22
    在同时使⽤ORDER BY和WHERE⼦句时,应该让ORDER BY位于WHERE之后,否则将会产⽣错误


    WHERE⼦句操作符
    操作符 说明
    = 等于
    <> != 不等于
    < ⼩于
    <= ⼩于等于
    > ⼤于
    >= ⼤于等于
    BETWEEN .. and.. 指定两个值之间
    IS NULL 空值

    组合WHERE⼦句

    MySQL允许给出多个WHERE⼦句。
    这些⼦句可以两种⽅式使⽤:以AND⼦句的⽅式或OR⼦句的⽅式使⽤。也称为逻辑操作符
    select name from stu where age = 22 and sex = ‘m’

    AND ⽤在WHERE⼦句中的关键字,⽤来指示检索满⾜所有给定条件的⾏
    select name from stu where age = 22 or sex = ‘m’

    OR ⽤来表示检索匹配任⼀给定条件的⾏

    ANDOR

    WHERE可包含任意数⽬的AND和OR操作符。允许两者结合以进⾏复杂和⾼级的过滤。
    但是,组合AND和OR带来了⼀个有趣的问题。
    例如:我需要在数据库中查询出1期或2期的学员,并且要求是⼥⽣
    select name from stu where class_id=1 or class_id =2 and sex=’m’
    以上语句不会按照预期检索出正确的数据,问题出在哪⾥?
    原因在于计算的次序。 SQL在处理OR操作符前,优先处理AND操作符。
    当SQL看到上述WHERE⼦句时,它理解是 1期班级的所有⼥⽣,或者2期的所有学员,⽽不分性别。换句话 说,由于AND在计算次序中优先级更⾼,操作符被错误地组合了

    因此想要解决就需要提升优先级,使⽤圆括号明确地分组相应的操作符
    select name from stu where (class_id=1 or class_id =2) and sex=’m’
    因为圆括号具有较AND或OR操作符⾼的计算次序,数据库⾸先过滤圆括号内的OR条件。
    这时, SQL语句变成了获取1期或2期的学员并且要求是⼥⽣

    INNOT

    IN操作符⽤来指定条件范围,范围中的每个条件都可以进⾏匹配。
    select name from stu where class_id in (1,2)

    IN WHERE⼦句中⽤来指定要匹配值的清单的关键字,功能与OR相当
    为什么要使⽤IN操作符?其优点具体如下。
    08.DQL-从select开始 - 图5 在使⽤⻓的合法选项清单时, IN操作符的语法更清楚且更直观。
    08.DQL-从select开始 - 图6 在使⽤IN时,计算的次序更容易管理(因为使⽤的操作符更少)。
    08.DQL-从select开始 - 图7 IN操作符⼀般⽐OR操作符清单执⾏更快。
    08.DQL-从select开始 - 图8 IN的最⼤优点是可以包含其他SELECT语句,使得能够更动态地建⽴WHERE⼦句。

    NOT WHERE⼦句中⽤来否定后跟条件的关键字

    08.DQL-从select开始 - 图908.DQL-从select开始 - 图1008.DQL-从select开始 - 图11select name from stu where class_id not in (1,2)

    为什么使⽤NOT?对于简单的WHERE⼦句,使⽤NOT确实没有什么优势。
    但在更复杂的⼦句中, NOT是⾮常有⽤的。
    例如,在与IN操作符联合使⽤时, NOT使找出与条件列表不匹配的⾏⾮常简单。

    通配符的使⽤

    LIKE与通配符

    前⾯介绍的所有操作符都是针对已知值进⾏过滤的。
    但是,这种过滤⽅法并不是任何时候都好⽤。
    例如,怎样搜索姓名中包含⽂本a的所有⽤户?⽤简单的⽐较操作符肯定不⾏,必须使⽤通配符。 为在搜索⼦句中使⽤通配符,必须使⽤LIKE操作符。
    LIKE指示MySQL,后跟的搜索模式利⽤通配符匹配⽽不是直接相等匹配进⾏⽐较。
    08.DQL-从select开始 - 图12 百分号(%)通配符 在搜索串中, %表示任何字符出现任意次数
    select name from stu where name like ‘a%’
    select name from stu where name like ‘%a’
    select name from stu where name like ‘%a%’
    下划线(_)通配符 下划线的⽤途与%⼀样,但下划线只匹配单个字符⽽不是多个字符
    使⽤通配符的技巧

    正如所⻅, MySQL的通配符很有⽤。
    但这种功能是有代价的:通配符搜索的处理⼀般要⽐前⾯讨论的其他搜索所花时间更⻓。
    这⾥给出⼀些使⽤通配符要记住的技巧
    08.DQL-从select开始 - 图13 不要过度使⽤通配符。如果其他操作符能达到相同的⽬的,应该使⽤其他操作符。
    08.DQL-从select开始 - 图14 在确实需要使⽤通配符时,除⾮绝对有必要,否则不要把它们⽤在搜索模式的开始处。 08.DQL-从select开始 - 图15 把通配符置于搜索模式的开始处,搜索起来是最慢的。
    08.DQL-从select开始 - 图16 仔细注意通配符的位置。如果放错地⽅,可能不会返回想要的数据
    08.DQL-从select开始 - 图17 了解下MySQL的正则 REGEXP

    所有种类的程序设计语⾔、⽂本编辑器、操作系统等都⽀持正则表达式
    select name from stu where name regexp ‘[0-5]abc’

    08.DQL-从select开始 - 图1808.DQL-从select开始 - 图1908.DQL-从select开始 - 图2008.DQL-从select开始 - 图2108.DQL-从select开始 - 图2208.DQL-从select开始 - 图23

    字段的计算

    存储在数据库表中的数据⼀般不是应⽤程序所需要的格式。下⾯举⼏个例⼦
    如果想在⼀个字段中既显示⽤户名,⼜显示班级号,但这两个信息⼀般包含在不同的表列中。
    同时姓名、⼿机号和地址存储在不同的列中(应该这样),但快递单打印程序却需要把它们作为⼀ 个恰当格式的字段检索出来。
    列数据是⼤⼩写混合的,但报表程序需要把所有数据按⼤写表示出来。
    物品订单表存储物品的价格和数量,但不需要存储每个物品的总价格(⽤价格乘以数量即可)。为 打印发票,需要物品的总价格。
    需要根据表数据进⾏总数、平均数计算或其他计算

    计算字段并不实际存在于数据库表中。计算字段是运⾏时在SELECT语句内创建的
    拼接 Concat

    stu表包含⽤户名和⼿机号码信息。假如要⽣成⼀个学⽣报表,需要在学⽣的名字中按照
    name(phone)这样的格式列出
    解决办法是把两个列拼接起来。在MySQL的SELECT语句中,可使⽤Concat()函数来拼接两个列
    select concat(name,’(‘,phone’)’) from stu
    使⽤别名 AS

    SELECT语句拼接字段可以完成。但此新计算列的名字是什么呢?实际上它没有名字,它只是⼀个 值。
    如果仅在SQL查询⼯具中查看⼀下结果,这样没有什么不好。
    但是,⼀个未命名的列不能⽤于客户机应⽤中,因为客户机没有办法引⽤它。
    为了解决这个问题, SQL⽀持列别名。别名(alias)是⼀个字段或值的替换名。别名⽤AS关键字赋 予
    select concat(name,’(‘,phone’)’) from stu as name_phone from stu

    计算

    在mysql中可以对列中的字段进⾏计算,使⽤基本算术操作符,此外,圆括号可⽤来区分优先顺序。

    运算符 说明
    +
    -
    *
    /

    SELECT提供了测试和试验函数与计算的⼀个很好的办法。
    虽然SELECT通常⽤来从表中检索数据,但可以省略FROM⼦句以便简单地访问和处理表达式。
    例如, SELECT 3*2;将返回6, SELECT Trim(‘abc’);将返回abc,⽽SELECT Now()利⽤Now()函数返回当 前⽇期和时间。
    通过这些例⼦,可以明⽩如何根据需要使⽤SELECT进⾏试验

    函数的使⽤

    与其他⼤多数计算机语⾔⼀样, SQL⽀持利⽤函数来处理数据。
    函数⼀般是在数据上执⾏的,它给数据的转换和处理提供了⽅便。
    08.DQL-从select开始 - 图24
    ⽂本处理函数
    https://dev.mysql.com/doc/refman/5.7/en/string-functions.html

    函数 说明
    Le!() 返回串左边的字符
    Length() 返回串的⻓度
    Locate() 找出串的⼀个⼦串
    Lower() 将串转换为⼩写
    LTrim() 去掉串左边的空格
    Right() 返回串右边的字符
    RTrim() 去掉串右边的空格
    SubString() 返回⼦串的字符 **
    Upper() 将串转换为⼤写
    Concat 拼接字符串. **

    ⽇期和时间处理函数
    https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html

    函数 说明
    AddTime() 增加⼀个时间(时、分等)
    CurDate() 返回当前⽇期 **
    CurTime() 返回当前时间 **
    Date() 返回⽇期时间的⽇期部分
    DateDi”() 计算两个⽇期之差. **
    Date_Add() ⾼度灵活的⽇期运算函数
    Date_Format() 返回⼀个格式化的⽇期或时间串
    Day() 返回⼀个⽇期的天数部分
    DayOfWeek() 对于⼀个⽇期,返回对应的星期⼏
    Hour() 返回⼀个时间的⼩时部分
    Minute() 返回⼀个时间的分钟部分
    Month() 返回⼀个⽇期的⽉份部分
    Now() 返回当前⽇期和时间. **
    Second() 返回⼀个时间的秒部分
    Time() 返回⼀个⽇期时间的时间部分
    Year() 返回⼀个⽇期的年份部分

    数值处理函数
    https://dev.mysql.com/doc/refman/5.7/en/numeric-functions.html

    08.DQL-从select开始 - 图2508.DQL-从select开始 - 图2608.DQL-从select开始 - 图2708.DQL-从select开始 - 图28

    函 数 说 明
    Abs() 返回⼀个数的绝对值
    Cos() 返回⼀个⻆度的余弦
    Exp() 返回⼀个数的指数值
    Mod() 返回除操作的余数
    Pi() 返回圆周率
    Rand() 返回⼀个随机数. **
    Sin() 返回⼀个⻆度的正弦
    Sqrt() 返回⼀个数的平⽅根
    Tan() 返回⼀个⻆度的正切

    聚集函数与分组 Group By

    聚集函数的使⽤

    我们经常需要汇总数据⽽不⽤把它们实际检索出来,为此MySQL提供了专⻔的函数。
    使⽤这些函数, MySQL查询可⽤于检索数据,以便分析和报表⽣成
    确定表中⾏数(或者满⾜某个条件或包含某个特定值的⾏数)。
    获得表中⾏组的和。
    找出表列(或所有⾏或某些特定的⾏)的最⼤值、最⼩值和平均值。

    上述例⼦都需要对表中数据(⽽不是实际数据本身)汇总。
    因此,返回实际表数据是对时间和处理资源的⼀种浪费

    函数 说明
    COUNT() 返回某列的⾏数
    MAX() 返回某列的最⼤值
    MIN() 返回某列的最⼩值
    SUM() 返回某列值之和
    AVG() 返回某列的平均值

    注意

    在使⽤count时,如果指定列名,则指定列的值为空的⾏被忽略,但如果COUNT()函数中⽤的是星号 (*),则不忽略

    数据分组 GROUP BYHAVING
    GROUP BY

    SQL聚集函数可⽤来汇总数据。这使我们能够对⾏进⾏计数,计算和与平均数,获得最⼤和最⼩值
    ⽽不⽤检索所有数据
    ⽬前为⽌的所有计算都是在表的所有数据或匹配特定的WHERE⼦句的数据上进⾏的。
    例如我们需要获取某个班级的学员⼈数:
    select count(*) as nums from stu where class_id = 2
    但如果要返回每个班级的⼈数怎么办?

    此时就需要使⽤分组了,分组允许把数据分为多个逻辑组,以便能对每个组进⾏聚集计算。
    select class_id,count(*) as nums from stu group by class_id

    那么如果需要返回⼈数少于5⼈的班级怎么办?
    或返回班级⼈数⼤于5⼈的班级怎么办?
    HAVING

    除了能⽤GROUP BY分组数据外, MySQL还允许过滤分组,规定包括哪些分组,排除哪些分组。 例如,需要获取班级⼈数⼩于等于5⼈的班级。
    事实上,⽬前为⽌所学过的所有类型的WHERE⼦句都可以⽤HAVING来替代。
    唯⼀的差别是WHERE过滤⾏,⽽HAVING过滤分组。

    08.DQL-从select开始 - 图29
    select class_id,count() as nums from stu group by class_id;
    select class_id,count(
    ) as nums from stu group by class_id having nums >= 5;

    使⽤Group By进⾏分组时的注意点

    在使⽤group by进⾏分组时,按照标准的SQL模式,需要把select查询中的所有列(除了聚集函数 外)全部都列在group by后⾯
    mysql> select class_id,count(*) as nums from stu group by class_id;
    +—————+———+
    | class_id | nums |
    +—————+———+
    1 | 7 |
    |
    2 | 5 |
    |
    +—————+———+
    2 rows in set (0.00 sec)
    — 在select⾥⾯有 class_id,class_name, 但是group by 后⾯只有 class_id ,那么语法错误。
    mysql> select class_id,class_name,count(*) as nums from stu group by class_id;
    ERROR 1055 (42000 ): Expression #2 of SELECT list is not in GROUP BY clause and
    contains nonaggregated column ‘chuange.stu.class_name’ which is not functionally
    dependent on columns in GROUP BY clause; this is incompatible with
    sql_mode=only_full_group_by
    — 正确的语句,应该是,把select中所以列都作为分组条件
    mysql> select class_id,class_name,count(*) as nums from stu group by
    class_id,class_name;
    +—————+——————+———+
    | class_id | class_name | nums |
    +—————+——————+———+
    | 7 |
    |
    1 | 1班
    | 5 |
    |
    2 | 2班
    +—————+——————+———+
    2 rows in set (0.00 sec)

    总结SELECT⼦句及其顺序

    ⼦句 说明 是否必须
    SELECT 要返回的列或表达式
    FROM 从中检索数据的表 仅在从表选择数据时使⽤
    WHERE ⾏级过滤
    GROUP BY 分组说明 仅在按组计算聚集时使⽤
    HAVING 组级过滤
    ORDER BY 输出排序顺序
    LIMIT 要检索的⾏数