where子句(条件查询):按照“条件表达式”指定的条件进行查询。
group by子句(分组):按照“属性名”指定的字段进行分组。group by子句通常和count()、sum()等聚合函数一起使用。
having子句(筛选):有group by才能having子句,只有满足“条件表达式”中指定的条件的才能够输出。
order by子句(排序):按照“属性名”指定的字段进行排序。排序方式由“asc”和“desc”两个参数指出,默认是按照“asc”来排序,即升序。
limit(限制结果集)。
mysql中五种常用的聚合函数:
(1)max(列名):求最大值。
(2)min(列名):求最小值。
(2)sum(列名):求和。
(4)avg(列名):求平均值。
(5)count(列名):统计记录的条数。
为了方便查看数据,可以对数据进行排序
语法:order by 字段 asc 或者 desc
说明 按照某字段正序,倒序使用desc
将行数据按照列1进行排序,如果某些行列1的值相同时,则按照列2排序,以此类推
默认按照列值从小到大排列(asc)
asc从小到大排列,即升序
desc从大到小排序,即降序
例1:查询学生信息,按学号降序
select * from student order by id desc
例2:查询学生信息,按名称升序
select * from student order by name asc
例3:显示所有的学生信息,先按照年龄从大-->小排序,当年龄相同时 按照身高从高-->矮排序
select * from student order by age desc,height desc
聚合函数
1、count函数
①count(*):返回表中满足where条件的行的数量
mysql> select count(*) from salary_tab where salary='1000';
+----------+
| count(*) |
+----------+
| 2 |
+----------+
mysql> select count(*) from salary_tab; #没有条件,默认统计表数据行数
+----------+
| count(*) |
+----------+
| 5 |
+----------+
②count(列):返回列值非空的行的数量
mysql> select count(salary) from salary_tab;
+---------------+
| count(salary) |
+---------------+
| 4 |
+---------------+
③count(distinct 列):返回列值非空的、并且列值不重复的行的数量
mysql> select count(distinct salary) from salary_tab;
+------------------------+
| count(distinct salary) |
+------------------------+
| 3 |
+------------------------+
④count(expr):根据表达式统计数据
mysql> select * from TT;
+------+------------+
| UNIT | DATE |
+------+------------+
| a | 2018-04-03 |
| a | 2017-12-12 |
| b | 2018-01-01 |
| b | 2018-04-03 |
| c | 2016-06-06 |
| d | 2018-03-03 |
+------+------------+
6 rows in set (0.00 sec)
mysql> select UNIT as '单位',
-> COUNT(TO_DAYS(DATE)=TO_DAYS(NOW()) or null) as '今日统计',
-> COUNT(YEAR(DATE)=YEAR(NOW()) or null) as '今年统计'
-> from v_jjd
-> group by JJDW;
+------+----------+----------+
| 单位 | 今日统计 | 今年统计 |
+------+----------+----------+
| a | 1 | 1 |
| b | 1 | 2 |
| c | 0 | 0 |
| d | 0 | 1 |
+------+----------+----------+
4 rows in set (0.00 sec)
2、max和min函数---统计列中的最大最小值
mysql> select max(salary) from salary_tab;
+-------------+
| max(salary) |
+-------------+
| 3000.00 |
+-------------+
mysql> select min(salary) from salary_tab;
+-------------+
| min(salary) |
+-------------+
| 1000.00 |
+-------------+
注意:如果统计的列中只有NULL值,那么MAX和MIN就返回NULL
3、sum和avg函数---求和与求平均
!!表中列值为null的行不参与计算
mysql> select sum(salary) from salary_tab;
+-------------+
| sum(salary) |
+-------------+
| 7000.00 |
+-------------+
mysql> select avg(salary) from salary_tab;
+-------------+
| avg(salary) |
+-------------+
| 1750.000000 |
+-------------+
mysql> select avg(ifnull(salary,0)) from salary_tab;
+-----------------------+
| avg(ifnull(salary,0)) |
+-----------------------+
| 1400.000000 |
+-----------------------+
注意:要想列值为NULL的行也参与组函数的计算,必须使用IFNULL函数对NULL值做转换。
二、分组SELECT
SELECT select_expr [, select_expr ...]
[FROM table_references
[PARTITION partition_list]
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
分组SELECT的基本格式:
select [聚合函数] 字段名 from 表名
[where 查询条件]
[group by 字段名]
[having 过滤条件]
1、group by子句
根据给定列或者表达式的每一个不同的值将表中的行分成不同的组,使用组函数返回每一组的统计信息
规则:
①出现在SELECT子句中的单独的列,必须出现在GROUP BY子句中作为分组列
②分组列可以不出现在SELECT子句中
③分组列可出现在SELECT子句中的一个复合表达式中
④如果GROUP BY后面是一个复合表达式,那么在SELECT子句中,它必须整体作为一个表达式的一部分才能使用。
1)指定一个列进行分组
mysql> select salary,count(*) from salary_tab
-> where salary>=2000
-> group by salary;
+---------+----------+
| salary | count(*) |
+---------+----------+
| 2000.00 | 1 |
| 3000.00 | 1 |
+---------+----------+
2)指定多个分组列,‘大组中再分小组’
mysql> select userid,count(salary) from salary_tab
-> where salary>=2000
-> group by salary,userid;
+--------+---------------+
| userid | count(salary) |
+--------+---------------+
| 2 | 1 |
| 3 | 1 |
+--------+---------------+
3)根据表达式分组
mysql> select year(payment_date),count(*)
-> from PENALTIES
-> group by year(payment_date);
+--------------------+----------+
| year(payment_date) | count(*) |
+--------------------+----------+
| 1980 | 3 |
| 1981 | 1 |
| 1982 | 1 |
| 1983 | 1 |
| 1984 | 2 |
+--------------------+----------+
5 rows in set (0.00 sec)
4)带有排序的分组:如果分组列和排序列相同,则可以合并group by和order by子句
mysql> select teamno,count(*)
-> from MATCHES
-> group by teamno
-> order by teamno desc;
+--------+----------+
| teamno | count(*) |
+--------+----------+
| 2 | 5 |
| 1 | 8 |
+--------+----------+
2 rows in set (0.00 sec)
mysql> select teamno,count(*)
-> from MATCHES
-> group by teamno desc; #可以把desc(或者asc)包含到group by子句中简化
+--------+----------+
| teamno | count(*) |
+--------+----------+
| 2 | 5 |
| 1 | 8 |
+--------+----------+
2 rows in set (0.00 sec)
对于分组聚合注意:
通过select在返回集字段中,这些字段要么就要包含在group by语句后面,作为分组的依据,要么就要被包含在聚合函数中。我们可以将group by操作想象成如下的一个过程:首先系统根据select语句得到一个结果集,然后根据分组字段,将具有相同分组字段的记录归并成了一条记录。这个时候剩下的那些不存在与group by语句后面作为分组依据的字段就很有可能出现多个值,但是目前一种分组情况只有一条记录,一个数据格是无法放入多个数值的,所以这个时候就需要通过一定的处理将这些多值的列转化成单值,然后将其放在对应的数据格中,那么完成这个步骤的就是前面讲到的聚合函数,这也就是为什么这些函数叫聚合函数了。
2、GROUP_CONCAT()函数
函数的值等于属于一个组的指定列的所有值,以逗号隔开,并且以字符串表示。
例1:对于每个球队,得到其编号和所有球员的编号
mysql> select teamno,group_concat(playerno)
-> from MATCHES
-> group by teamno;
+--------+------------------------+
| teamno | group_concat(playerno) |
+--------+------------------------+
| 1 | 6,6,6,44,83,2,57,8 |
| 2 | 27,104,112,112,8 |
+--------+------------------------+
2 rows in set (0.01 sec)
如果没有group by子句,group_concat返回一列的所有值
例2:得到所有的罚款编号列表
mysql> select group_concat(paymentno)
-> from PENALTIES;
+-------------------------+
| group_concat(paymentno) |
+-------------------------+
| 1,2,3,4,5,6,7,8 |
+-------------------------+
1 row in set (0.00 sec)
3、with rollup子句:用来要求在一条group by子句中进行多个不同的分组
用的比较少点,但是有时可以根据具体的需求使用
如果有子句GROUP BY E1,E2,E3,E4 WITH ROLLUP
那么将分别执行以下分组:[E1,E2,E3,E4]、[E1,E2,E3]、[E1,E2]、[E1]、[]
注意:[ ]表示所有行都分在一组中
示例:按照球员的性别和居住城市,统计球员的总数;统计每个性别球员的总数;统计所有球员的总数
mysql> select sex,town,count(*)
-> from PLAYERS
-> group by sex,town with rollup;
+-----+-----------+----------+
| sex | town | count(*) |
+-----+-----------+----------+
| F | Eltham | 2 |
| F | Inglewood | 1 |
| F | Midhurst | 1 |
| F | Plymouth | 1 |
| F | NULL | 5 |
| M | Douglas | 1 |
| M | Inglewood | 1 |
| M | Stratford | 7 |
| M | NULL | 9 |
| NULL | NULL | 14 |
+-----+-----------+----------+
10 rows in set (0.00 sec)
4、HAVING子句:对分组结果进行过滤
注意:
不能使用WHERE子句对分组后的结果进行过滤
不能在WHERE子句中使用组函数,仅用于过滤行
mysql> select playerno
-> from PENALTIES
-> where count(*)>1
-> group by playerno;
ERROR 1111 (HY000): Invalid use of group function
因为WHERE子句比GROUP BY先执行,而组函数必须在分完组之后才执行,且分完组后必须使用having子句进行结果集的过滤。
基本语法:
SELECT select_expr [, select_expr ...]
FROM table_name
[WHERE where_condition]
[GROUP BY {col_name | expr} [ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
!!!having子语句与where子语句区别:
where子句在分组前对记录进行过滤;
having子句在分组后对记录进行过滤
mysql> select salary,count(*) from salary_tab
-> where salary>=2000
-> group by salary
-> having count(*)>=0;
+---------+----------+
| salary | count(*) |
+---------+----------+
| 2000.00 | 1 |
| 3000.00 | 1 |
+---------+----------+
1)HAVING可以单独使用而不和GROUP BY配合,如果只有HAVING子句而没有GROUP BY,表中所有的行分为一组
2)HAVING子句中可以使用组函数
3)HAVING子句中的列,要么出现在一个组函数中,要么出现在GROUP BY子句中(否则出错)
mysql> select town,count(*)
-> from PLAYERS
-> group by town
-> having birth_date>'1970-01-01';
ERROR 1054 (42S22): Unknown column 'birth_date' in 'having clause'
mysql> select town,count(*)
-> from PLAYERS
-> group by town
-> having town in ('Eltham','Midhurst');
+----------+----------+
| town | count(*) |
+----------+----------+
| Eltham | 2 |
| Midhurst | 1 |
+----------+----------+
2 rows in set (0.00 sec)
limit 分段取值
语法 limi m,n
m为其实位置,n为取几个
select * from student limit 0,2
从第一个开始取,取两个,如果不写第一个参数,就是默认从第一个参数取值,取n个
select * from student limit 2
连接查询(join 语法)
join 用于多表中字段之间的联系,语法如下:
... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona
table1:左表;table2:右表。
JOIN 按照功能大致分为如下三类:
INNER JOIN(内连接,或等值连接):取得两个表中存在连接匹配关系的记录。
LEFT JOIN(左连接):取得左表(table1)完全记录,即是右表(table2)并无对应匹配记录。
RIGHT JOIN(右连接):与 LEFT JOIN 相反,取得右表(table2)完全记录,即是左表(table1)并无匹配对应记录。
注意:mysql不支持Full join,不过可以通过UNION 关键字来合并 LEFT JOIN 与 RIGHT JOIN来模拟FULL join.
接下来给出一个列子用于解释下面几种分类。如下两个表(A,B)
- mysql> select A.id,A.name,B.name from A,B where A.id=B.id;
- +----+-----------+-------------+
- | id | name | name |
- +----+-----------+-------------+
- | 1 | Pirate | Rutabaga |
- | 2 | Monkey | Pirate |
- | 3 | Ninja | Darth Vader |
- | 4 | Spaghetti | Ninja |
- +----+-----------+-------------+
- 4 rows in set (0.00 sec)
二.Inner join
内连接,也叫等值连接,inner join产生同时符合A和B的一组数据。
- mysql> select * from A inner join B on A.name = B.name;
- +----+--------+----+--------+
- | id | name | id | name |
- +----+--------+----+--------+
- | 1 | Pirate | 2 | Pirate |
- | 3 | Ninja | 4 | Ninja |
- +----+--------+----+--------+
三.Left join
- mysql> select * from A left join B on A.name = B.name;
- #或者:select * from A left outer join B on A.name = B.name;
- +----+-----------+------+--------+
- | id | name | id | name |
- +----+-----------+------+--------+
- | 1 | Pirate | 2 | Pirate |
- | 2 | Monkey | NULL | NULL |
- | 3 | Ninja | 4 | Ninja |
- | 4 | Spaghetti | NULL | NULL |
- +----+-----------+------+--------+
- 4 rows in set (0.00 sec)
left join,(或left outer join:在Mysql中两者等价,推荐使用left join.)左连接从左表(A)产生一套完整的记录,与匹配的记录(右表(B)) .如果没有匹配,右侧将包含null。
如果想只从左表(A)中产生一套记录,但不包含右表(B)的记录,可以通过设置where语句来执行,如下:
- mysql> select * from A left join B on A.name=B.name where A.id is null or B.id is null;
- +----+-----------+------+------+
- | id | name | id | name |
- +----+-----------+------+------+
- | 2 | Monkey | NULL | NULL |
- | 4 | Spaghetti | NULL | NULL |
- +----+-----------+------+------+
- 2 rows in set (0.00 sec)
同理,还可以模拟inner join. 如下:
- mysql> select * from A left join B on A.name=B.name where A.id is not null and B.id is not null;
- +----+--------+------+--------+
- | id | name | id | name |
- +----+--------+------+--------+
- | 1 | Pirate | 2 | Pirate |
- | 3 | Ninja | 4 | Ninja |
- +----+--------+------+--------+
- 2 rows in set (0.00 sec)
求差集:
根据上面的例子可以求差集,如下:
- SELECT * FROM A LEFT JOIN B ON A.name = B.name
- WHERE B.id IS NULL
- union
- SELECT * FROM A right JOIN B ON A.name = B.name
- WHERE A.id IS NULL;
- # 结果
- +------+-----------+------+-------------+
- | id | name | id | name |
- +------+-----------+------+-------------+
- | 2 | Monkey | NULL | NULL |
- | 4 | Spaghetti | NULL | NULL |
- | NULL | NULL | 1 | Rutabaga |
- | NULL | NULL | 3 | Darth Vader |
- +------+-----------+------+-------------+
四.Right join
- mysql> select * from A right join B on A.name = B.name;
- +------+--------+----+-------------+
- | id | name | id | name |
- +------+--------+----+-------------+
- | NULL | NULL | 1 | Rutabaga |
- | 1 | Pirate | 2 | Pirate |
- | NULL | NULL | 3 | Darth Vader |
- | 3 | Ninja | 4 | Ninja |
- +------+--------+----+-------------+
- 4 rows in set (0.00 sec)
同left join。
Mysql建表思路理解
一对一 one on one
一张表的一条记录一定只能与另外一张表的一条记录进行对应,反之亦然。 学生表:姓名,性别,年龄,身高,体重,籍贯,家庭住址,紧急联系人 其中姓名、性别、年龄、身高,体重属于常用数据,但是籍贯、住址和联系人为不常用数据 如果每次查询都是查询所有数据,不常用的数据就会影响效率,实际又不用 常用信息表:ID(P),姓名,性别,年龄,身高,体重 不常用信息表:ID(P),籍贯,家庭住址,紧急联系人 解决方案:将常用的和不常用的信息分享存储,分成两张表 不常用信息表和常用信息表,保证不常用信息表与常用信息表能够对应上:找一个具有唯一性的 字段来共同连接两张表。 一个常用表中的一条记录永远只能在一张不常用表中匹配一条记录,反之亦然。
一对多 one on many
一张表中有一条记录可以对应另外一张表中的多条记录;但是反过来,另外一张表的一条记录 只能对应第一张表的一条记录,这种关系就是一对多或多对一 母亲与孩子的关系:母亲,孩子两个实体 母亲表:ID(P),名字,年龄,性别 孩子表:ID(P),名字,年龄,性别 以上关系:一个妈妈可以在孩子表中找到多条记录(也可能是一条),但是一个孩子只能找到一个妈妈 是一种典型的一对多的关系。 但是以上设计:解决了实体的设计表问题,但是没有解决关系问题,孩子找不到母亲,母亲也找不到孩子 解决方案:在某一张表中增加一个字段,能够找到另外一张表中的记录:在孩子表中增加一个字段 指向母亲表,因为孩子表的记录只能匹配到一条母亲表的记录。 母亲表:ID(P),名字,年龄,性别 孩子表:ID(P),名字,年龄,性别,母亲表ID(母亲表主键)
多对多 many on many
一对表中(A)的一条记录能够对应另外一张表(B)中的多条记录;同时B表中的一条记录 也能对应A表中的多条记录 老师和学生 老师表 T_ID(P),姓名,性别 学生表 S_ID(P),姓名,性别 以上设计方案:实现了实体的设计,但是没有维护实体的关系 一个老师教过多个学生,一个学生也被多个老师教过 解决方案:增加一张中间关系表 老师与学生的关系表:ID(P),T_ID,S_ID 老师表与中间表形成一对多的关系,而中间表是多表;维护了能够唯一找到一表的关系; 同样的学生表与中间表也是一个一对多的关系; 学生找老师:找出学生ID--->中间表寻找匹配记录(多条)--->老师表匹配(一条) 老师找学生:找出老师ID--->中间表寻找匹配记录(多条)--->学生表匹配(一条)