1小时钟回顾MySQL语法(中)

六.聚集函数

 

使用聚集函数,可以方便的分析数据,主要有以下几种应用场景:

1.确定表的行数 (统计)

2.对某一列值进行求和

3.找出表的列 最大值,最小值 或平均值

特点: 使用聚集函数,返回的结果 是单行单列

用处:一般用于子查询 或  与分组搭配使用.  更多的是搭配分组使用

 

以下函数 distinct 为可选值 

6.1 AVG([DISTINCT] expr)     #求某一列平均值 会自动去除 内容为null 的列

例如:select avg(age) from student # 求 学生的平均年龄 

 

6.2 COUNT([DISTINCT] expr)  #统计某一列出现的行数 会自动去除 内容为null 的列

例如: select * from student   # 统计共有多少条学生记录

 

6.3 MAX([DISTINCT] expr)  #求出某一列的最大值  

例如: select max(age) from student   # 计算学生中最大的年龄是

 

6.4 MIN([DISTINCT] expr)  #求出某一列的最小值

例如: select min(age) from student   # 计算学生中最小的年龄是

 

max 与 min 通常用来查找 数值或日期的最 大/小 值

 

6.5 SUM([DISTINCT] expr)  # 对某一列的所有值进行求和

例如: select sum(age) from student   # 统计学生年龄的总和

 

 

六-2.数据分组

 

一般聚集函数都是与分组搭配使用,分组是按照某一特点 把表中的数据分成多个组,分组后分别再进行聚集操作

 

6.1 关键字 : group by cloum

select sex,count(*) as 总人数  from  student group by sex #统计不同性别的人数

特点:先分组 再聚集

需注意:

1.虽然没有明确要求, 但是一般 用于分组的列 , 都要在 select 语句中查询出

2.关键字优先级: group by  必需在 where 语句之后 , order by 之前

3.group by 后不可跟聚集函数, 不可跟别名

4.如果分组中存在null值, 会将null单独作为一个分组. 如果有多个null ,可将多个null作为一个分组

 

6.2 过滤分组数据 HAVING 

where 与 having 的区别

where 是对原始数据的每行数据进行过滤, 不存在分组的概念,

having 是对分组后的每组数的每一行进行的过滤

 

HAVING支持所有的 where子句中的操作符,语法完全一致 .

举个例子 :从学生表中分别统计男生,女生中 ,年龄在20岁以上的 人数,并且人数在5个以上

select sex,count(*)  from student where age > 20 group by sex  having count(*) > 5

having 后的表达式,必须是 select 后面出现的非别名的有效表达式, 

 

6.3 分组后排序 按照人数进行排序 (各个关键字的顺序不可以错)

 #having 后 可以使用 select 语句中的别名

例如:显示每个地区的总人口数和总面积.仅显示那些面积超过1000000的地区。

SELECT region, SUM(population), SUM(area)
FROM bbc
GROUP BY region
HAVING SUM(area)>1000000

 

6.4 关键字的顺序

从前至后: select  , from  , where  , group by ,   having ,  order by  ,limit

 

七 .子查询

 

从1个查询中,查询出的结果 ,被其他查询利用;

1.where 子句中的 子查询

假设现在有两个表 ,学生表 student (sid,sname...)  与 成绩表 score(id,sid,degree...)

成绩表中列出了每个学生的成绩,但是成绩表中只存储了 学生的id,并没有存储学生的姓名

先有一个需求,查询出任意一门成绩在90分以上的学生的sid 与 姓名

分析: 现在两张表中,没有一张表能同时包含,成绩 与 学生姓名,此时需要先从成绩表中查询出 大于90分的学生id集合

       再从学生表中,查询出 id在上述查询结果集中的 学生的姓名即可

   select sid,name from student where  sid in ( select sid from score where score > 90)

 

如果要查询 90分以下的,则只需要再 in 前面加上 not 即表示否定

select sid,name from student where  sid not in ( select sid from score where score > 90)

注意:

1.如需使用子查询,则要将子查询 用() 引起来

2.子查询可以嵌套多层, 凡涉及到的子查询 最好每个都要格式化显示, 便于阅读,上述SQL语句改造如下(虽然还是不好看.格式化语句在复杂的SQL语句时,显得格外重要)

  select sid,name from student

  where  sid in (

     select sid from score where score > 90)

3.sql的执行 总是从内向外执行

 

2.子查询作为计算字段

假设有两张表 , 客户表(customers)与订单表(orders) ,客户表 存放客户的相关信息, 主键为c_id ,

订单表存放客户c_id 与 客户所下的订单号

现有需求,需要显示每位客户的id, name ,与他们的订单总量

select c_id ,name, (

  select count(*) from orders

    where  orders.c_id = customers.c_id ) AS  order_count

from customers 

如上所示, 将从订单表中查询的统计的数量作为计算字段, 进行展示

注意:

1.select子句作为计算字段, 必需要保证 子查询的行数与 外层 的行数保持一致 即没一行,都对应计算字段的一个值 ,不然会报错

2.因为from的优先级最高,因此可以在子句中访问到 customers的c_id ,但是两张表中都有c_id 这个字段,进行条件关联时,需要指明是哪个表下的(表明.列名)即完全限定名,否则会出现歧义

 

 

八.联结表

 此章节后,所有的表均采用 <<sql必知必会 >> 书籍中所用的表与数据 , 构建表,去插入数据的脚本,请前往以下网址自行下载:http://www.forta.com/books/0672327120/

 

表之间的关联如下所示,即通过外键

 

 

 

正如上面所提到的例子, 订单表,与客户表 ,往往两个关联的表之间都是 一个表中只保存着另一个表中的主键即可(即外键)

优势:

1.当表中更新了内容后, 不需要更改关联表,便于表的维护

2.减少了字段的冗余

3.表的专一性更强, 只保存某些信息

 

 

8.1.使用where 子句进行联结  

例:查询产品名称,价格,供应商名称 关联条件 即 供应商id

select products.prod_name,products.prod_price,vendors.vend_name 

from  vendors, products

where  vendors.vend_id = products.vend_id

只会筛选出 符合where 条件的记录

 

8.2 笛卡尔积 

select * from vendors,products  如果么有建立where条件 , 这时查询出来的结果数 = vendors行数 * products  行数, 进行组合 这种现象叫做笛卡尔积

去除笛卡尔积就需要使用 where 条件进行表的联结

 

8.3 内部联结

使用where子句进行联结 为 等值联结. 可以更换为内联, 同样也是只匹配出 满足联结条件的数据 结果与 where条件联结 完全一致

其中inner 可以省略, 使用join 默认就是内联

关键字: table1 inner join table2  on 条件

select  * from vendors inner join products   on vendors.vend_id = products.vend_id

 

8.4 支持联结多张表

例如 : 订单明细表中会存储商品的ID ,此时可以 将 供应商, 商品,订单 三张表关联

select * from orderitems

  inner join products  on orderitems.prod_id =  products.prod_id

  inner join vendors on products.vend_id =  vendors .vend_id 

联结表时是非常消耗性能的,应当减少不必要的联结

 

8.5 联结时,也可以使用表的别名,优势:

1.缩短SQL

2.通过别名,允许在单句SQL中多次使用 相同的一张表

select  * from vendors  as v

  inner join products  as p

     on v.v_id = p.v_id

 

8.6 自联结

即用一张表,关联自己

需求: prod_id 为 DTNTR 的产品存在质量问题,想知道该产品的供应商下的其他产品是否也存在质量问题

分析: 可以通过 prod_id = 'DTNTR '  查询出 vend_id (供应商id) ,再根据 查询得到的 vend_id 得到所有的产品,因此可以使用子查询完成

select * from products

where vend_id = (

  select vend_id from products where prod_id = 'DTNTR'

)

 

除了子查询外,还可以通过表的自联结来查询出 该供应商下的所有产品,

注意:因为要查询供应商下所有商品, 因此关联条件应该是供应商的id 

select p1.* from products as p1 , products as p2 

where p1.vend_id = p2.vend_id  and  p1.prod_id = 'DTNTR'

 

上述:p1.* 表示结果集中只显示 p1下的所有列,  p2中的不予显示 ,这样就避免相同的列重复出现多次

 

8.7 外链接

外链接分为左外联 与 右外联     

左联结:

需求:查看每位顾客的订单记录

由于存在顾客没有下过订单, 如果仍然使用内联接 ,就会导致检索出的顾客 不是全部顾客,也就是未能满足需求,此时需要用到外联结

方式1:使用左联结

select * from customers
left outer join orders
on customers.cust_id = orders.cust_id

方式2:使用右联结

 select * from customers 
right outer join orders 
on customers.cust_id = orders.cust_id

 

特点:

1.左与右是相对概念 ,是针对 表在 outer join 语句的左右位置来判定的 

2.其中left outer join  与 right outer join 中的 outer可以省略

3.要显示左边全部记录,则使用 left outer join 即可 ,同理要显示 右边表全部记录,则使用right outer join 即可

 

一张自制图表示inner join  ,left outer join,right outer join

 

 

 

8.8 联结 后 使用聚集函数 

 

需求: 查询出所有客户所下的订单数 

所有客户即要检索出客户表中所有记录

 

select customers.cust_id,cust_name , COUNT(orders.order_num) as order_counts
from customers
left outer join orders
on customers.cust_id = orders.cust_id
GROUP BY customers.cust_id

 

小结:

1.平时需求中,更多的是使用内部联结,外部联结需要分清方向

2.联结时为了消除笛卡尔积 ,所有的连接都应使用 联结条件

3.联结条件要使用正确, 否则会出现很多错误的结果

 

 

九.组合查询 

 

9.1 关键字: union

作用:将两个SQL语句执行的结果 ,组合在一起 进行返回,作用如下图所示

 

 

 

 

举个栗子:假如需要查询出价格小于等于5的所有物品的一个列表 ,同时包含供应商1001 和 1002 生产的所有产品(没有价格限制)

该需求可以使用where 条件句 使用 or 连接两个条件 来实现,如下所示:

select * from products
where prod_price <= 5 or
vend_id in (1001,1002)
order by vend_id

 

同样也可以使用 union 来完成,代码如下:

select * from products where prod_price <= 5
UNION
select * from products where vend_id in (1001,1002)
order by vend_id

 

union规则:

1.必需是两条或两条以上的select 子句之间使用union ,3个select语句 则 需要使用两次union

2.union 连接的两个select 结果集必需拥有相同数量的列,表达式,或聚集函数

3.结果集中 对应的列 ,必需保证数据可以兼容

 

9.2 union  与 union all

union 会自动去除重复的行,而 union all 则不会,因此union all的效率 要高于 union

如果需要显示所有的行,则应该使用union all 

 

9.3 union 与 order by

如果要对 union后的结果进行排序 ,则只能在最后一个select 语句 末尾 加上 order by ,其他的 select语句则不能加!

union 可以使复杂的 sql 语句 阅读起来更加清晰,直观 , 必须要时应当使用 union 代替复杂的 where子句

 

 

十.全文本检索

 

10.理解全文本检索

 

并不是所有数据库存储引擎都支持全文本检索, mysql中最常用的搜索引擎为MyISAM和 InnoDB ,前者 MyISAM 引擎支持全文本检索,而mysql5.6版本之前,InnoDB不支持

在mysql5.6之后的版本innodb 引擎也开始支持 

第八章给出的样例表中 :productnotes 的存储引擎为:MyISAM

 

之前的模糊匹配 like 关键字 利用 % 与 _ 匹配文本  , 如果满足不了需求,还可以使用 关键则 regexp 使用正则进行匹配更加复杂的值

但是使用like 与 regexp 都存在几个重要限制:

1.性能问题 : 通配符 与 正则表达式 ,通常会要求匹配表中所有的行(通常情况,检索的列基本都没有建立索引),随着数据不断的累积,行数不断的增加,耗时也会越来越久

2.明确控制: 使用通配 与 正则,很难控制同时  a.满足匹配关键字的条件   b.满足 排除关键字的条件 

  例如:我想要筛选 包含 Comment, 但是内容不包含vendor的行 虽然like 和 正则 可以轻松满足行中包含 comment关键字的要求,但是并不容易过滤掉包含vendor的内容

3.智能化结果 : 1个匹配 通常只会匹配到 满足关键字的行, 他不会关心,这个关键字出现了一次或是多次 .换言之如果一个关键字重复出现多次,可能匹配程度就越高,但是like 正则 只会默认的对结果进行展示,并不关心匹配的优先级

  如果我的需求是 想要查询包含指定关键字的行, 同时 关键字出现在内容前端的 或者 关键字出现 多次的 会在结果集展示中排在靠前的位置 这种需求不是 like 与 regexp 能满足的

此时这种需求 可以使用全文本检索来实现 , 此时MySQL不需要分别查看每个行,不需要分别分析和处理每个词.MySQL可以快速有效地决定哪些词匹配

哪些词不匹配,他们的匹配频率等等...

 

10.2 使用全文本检索

 两个关键字 match(希望检索的列名) , against(search_content)

在productnotes表中, 搜索出包含 rabbit 的行,此时语法为:

select note_text from productnotes

where match(note_text) against('rabbit')

结果如下:

 

同样也可以使用 like 进行查找

 select note_text from productnotes where note_text like '%rabbit%'

结果如下:

 

乍一看,都能正确按照需求检索出满足条件的行, 仅仅是排序不一致而已;但是仔细看一下,就是这个排序有很大的关系

怎么说呢? like 查找出的结果集中, 第一行的尾部 包含 rabbit 关键字 , 第二行 是第四个单词就包含了 rabbit 关键字;

通常情况下,越靠前的那个更加是你想要找的那个结果,因此此时 全文检索相对 like 提供了排序的功能.并且在数据量越来越大的时候,能更明显的缩短检索所需的时间

 

此时变化一下我们的sql语句,将 match() against() 作为计算字段,如下:

select note_text ,match(note_text) against('rabbit') as rank from productnotes

得出的结果集为:

 

 从结果可以看到, 不匹配的等级 rank 都为 0 ,匹配到两条记录rank值 都有1个大于0的值,而这个值 就表示了匹配的等级 ,等级值越大就表示该结果更可能是你想要的行,这一点是like所做不到的;

 

10.3 全文检索 之 查询扩展 

 查询拓展的目的是,设法放宽全文本检索搜索的结果范围

假如: 我想找到note_text 列中包含 anvils的内容, 但是只有一行内容包含 anvils ,但是我还想找出 与 这一行 内容非常相似的行 ,那我该怎么办呢?

此时就需要使用到 查询拓展, 他会帮你开启脑洞模式, 查询拓展会对 查询内容 与 索引 扫描两次 ,来完成这次检索:

首先:正儿八级的找出满足条件的所有行 (在这个例子中,满足条件的只有一行)

其次:MySQL 会检索出第一步筛选出来的行,找出这些行中 有用的词(有用的词这个标准由mysql自行判断)

最后:mysql 会根据 输入的关键字  和 第二步 得到的有用的词  ,再进行一遍检索, 将此次检索的结果集作为最终结果返回;

 

使用查询拓展的语法为: against(select_content WITH QUERY EXPANSION)

不使用查询拓展的结果如下:

select note_text from productnotes where MATCH(note_text) AGAINST('anvils')

 

 使用查询扩展的结果如下:

select note_text from productnotes
where MATCH(note_text) AGAINST('anvils' WITH QUERY EXPANSION)

 

 分析查询的结果:这次返回了7行. 第一行是包含我们要检索的内容 等级自然最高 ,第二行 虽然与anvils无关,但是他包含了第一行当中的两个单词 customer 与 recommend 所以也被检索了出来

同理第三行也包含了这两个相同的词.虽然mysql很智能的帮你筛选出来了一些与 你想查询结果 非常相近的结果,但是同时也极大的增多了返回的行数,更多的可能是你不想要的

所以视情况决定要不要使用查询拓展

 

10.4 全文检索 之 布尔文本搜索

格式 match(列名)  against(search_content IN BOOLEAN MODE)

功能:

1.要匹配的词

2.要排斥的词

3.排列提示

4.表达式分组

5.另外一些内容

注意:

1.布尔文本搜索没有fulltext也可以使用,但是执行效率会降低

2.在正常的检索中默认会将结果集按照等级值降序排列,但布尔文本表达式中,不会对查询的结果按照等级值降序排列.

 

布尔操作符 

+    表示该词必需存在

-     排除,表示该词必须不能出现

>    包含,且出现该词增加等级值

<    包含,且出现该词降低等级值

()    多个操作符嵌套作用一个 或 一组词语

~    取消一个词的排序值

*    词尾通配符,匹配任意个数的任意字符

""    定义一个短语, 匹配与 包含 必需 是针对这个短语整体的操作

 

需求:查询productnotes中note_text 中 包含heavy 但不包含任意以 rope开头的单词

首先查询出所有包含 note_text 的记录

select note_text from productnotes
where match(note_text) AGAINST('heavy' IN BOOLEAN MODE)

共两条记录,结果如下:

 

此时第1条记录虽然包含了 heavy 但是同时也包含了 ropes , 这不是我们想要的,需要使用排除符 将其排除 ,使用语法如下:
select note_text from productnotes 
where match(note_text) AGAINST('heavy -rope*'  IN BOOLEAN MODE)

结果如下:

成功的排除了第一行包含ropes单词的记录...

 

其他操作符介绍:

无操作符

需求:检索出包含 rabbit 或 bait  的记录

select note_text from productnotes 
where match(note_text) AGAINST('rabbit bait' IN BOOLEAN MODE)

 

 

+ : 必需包含 

需求:检索出同时包含 rabbit 与 bait 的记录

select note_text from productnotes
where match(note_text) AGAINST('+rabbit +bait' IN BOOLEAN MODE)

 

 

"":双引号内 整体匹配 或 排除

需求:检索出 包含 ''rabbit bait" 整体的 记录

select note_text from productnotes 
where match(note_text) AGAINST('"rabbit bait"' IN BOOLEAN MODE)

 

 >: 出现该词会提高等级评分 

< : 出现该词会降低等级评分 

需求:检索出 包含 rabbit 或 carrot的记录, 并且 包含rabbit 的等级要高. carrot的优先级要降低

select note_text from productnotes
where match(note_text) AGAINST('>rabbit <carrot' IN BOOLEAN MODE)

 

():多个操作符嵌套作用一个 或 一组词语

需求:检索必需同时包含safe 和 combination 的记录.并且降低出现 combination  的等级

select note_text from productnotes
where match(note_text) AGAINST('+safe +(<combination)' IN BOOLEAN MODE)

 

 

10.5 全文检索 之 中文

由于中文分隔符的原因,不能很好的支持中文(日文)的全文检索, 可以说基本不支持 .

如果需要适配,提供简单思路:

1.内容中加上合适空格 逗号 或其他分隔符, 

2.同时到配置文件my.ini文件 中修改最小检索长度,

  ft_min_word_len = 2(ft_min_word_len 默认是4 )

  修改后,保存文件 需重启mysql服务方能生效

  查看当前ft_min_word_len  的值 ,使用命令 : SHOW VARIABLES LIKE 'ft_min_word_len'

最好是使用Apache组织 开发的 Lucene 全文检索工具类.

 

10.6 全文检索使用说明:

  1 在索引全文本数据时,短词被忽略且从索引中排除。短词定义为具有3个或3个以下字符的词(可以更改)
  2 mysql带有一个内建的非用词表(stopword),这些词在全文本搜索中被忽略 (如需要这个表可覆盖)
  3 mysql规定了一条50%规则,如果一个词出现在50%以上的行中,则将它忽略,不使用与in boolean mode
  4 如果表中的行少于3行,全文本搜索不返回结果(因为每次词或者不出现,或者至少50%出现)
  5 忽略词中的个单引号,例如: dot't 索引为 dont
  6.不具有词分隔符的语言(汉语,日语) 不能恰当的返回全文检索的结果
posted @ 2018-07-24 12:18  yi杆烟枪  阅读(209)  评论(0编辑  收藏  举报