select 的 5 种子句:

  • where:条件查询
  • group by:分组
  • having:筛选
  • order by:排序
  • limit:限制结果条数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
where
where 条件

where常用运算符:
=:等于 <:小于 >:大于 <=:小于或等于 >=:大于或等于
!= 或 <>:不等于 in:在某集合内 between:在某范围内

where逻辑运算符:
and 或 &&:与
or 或 ||: 或
not 或 !: 非

in:在某集合内,是对一个集合来操作的。而between对某个范围内的。
in(值1,值2...值n)
//查询goods中id等于3、5或者6的商品
select id,name,price from goods where id in(3,5,6);
between 值1 and 值2
//查询goods中id在3-6这个范围的上面
select id,name,price from goods where id between 3 and 6;
或者:
select id,name,price from goods where id>=3 and id<=6;
优先级别:not > and > or
//查询2-4和5-7的商品
select * from goods where id>=2 and id<=4 or id>=5 and id<=7;

//not可以和in一起用
select * from goods where id not in(4,5);
等于:
select * from goods where id!=4 and id!=5;

模糊查询
like
%:通配任意字符
_:通配单个字符

注意,如果想要模糊查询,但是又没有通配符的时候,查询语句是没有报错的,

但是,查询的结果可能是空的。如,
select goods_id,goods_name from goods where goods_name like '诺基亚';
Empty set (0.00sec)
//查询结果是空的,因为good_name中有“诺基亚”的商品都是有后缀的,没有单独

的诺基亚商品,如诺基亚N85、诺基亚N96、诺基亚耳机等等。
所以,要加通配符
//查询任意字符用%,如查询有“诺基亚”的所有商品
select goods_id, goods_name from goods where goods_name like '诺基亚%'
//查询诺基亚N系列而且N后面是两个字符的商品,也就是Nxx系列。
select goods_id, goods_name from goods where goods_name like '诺基亚N__'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
group by
作用:把行按字段分组

max:求最大
min:求最小
sum:求总和
avg:求平均
count:求总行数

//查询shop_price最大的商品的价格
select max(shop_price) from goods;
//下面的语句是没有意义的
select goods_id, goods_name max(goods_price) from goods;
因为,goods_id和goods_name是第一列的值,而goods_price是最大值。查询出

来的结果goods_id、goods_name和goods_price是不搭配的。

//查询每个商品所积压的货款
select goods_id, goods_name, goods_number*shop_price from goods;
//查询该商店积压的总货款
select sum(goods_number*shop_price) from goods;

我们的商品不但有goods_id号还有栏号cat_id,如果我们想查出每一栏目中价格

最贵的商品,怎么查
select cat_id, max(shop_price) from goods group by cat_id;
如果加good_id,goodd_id也是没有意义的。
//按栏目查询最便宜的商品
select cat_id, min(shop_price) from goods group by cat_id;
//按栏目查询商品平均价格,也就是每一栏的平均价格
select cat_id, avg(shop_price) from goods group by cat_id;
//按栏目查询商品种类,也就是每个栏目下的商品种类
select cat_id, count(*) from goods group by cat_id;

//查询商品的平均价格
select avg(good_price) from goods;
//查询所有商品的数量,也就是求有多少行
select count(*) from goods;

我们要养成一种思想:把列当成变量来看。
//查询出本店每个商品比市场价格低多少钱
select goods_id, goods_name, market_price - shop_price from goods;
注意,显示出来的结果是会有market_price - shop_price这一列的。

//查询每个栏目下积压的货款,也就是库存*价格
select cat_id, sum(shop_price*good_number) from goods group by cat_id;
我们可以看到查询结果有sum(shop_price*goods_number)这一列,不过列名实在是有点长,这时候,我们可以起一个别名,用as。
select cat_id, sum(shop_price*good_number) as hk from goods group by

cat_id;
这时候,我们查询结果看到的就是hk这一列代表积压货款。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
having

//查询出本店价格比市场价格低多少钱,并且把低200元以上的商品选出来
//查询出本店价格比市场价格低多少钱,我们可以这样实现
select goods_id, goods_name, sum(market_price-goods_price) as cheap from goods;
//当你还想要把低于200元以上的商品选出来的时候,我们首先想到的是用where条件来实现,即
select goods_id, goods_name, sum(market_price-goods_price) as cheap from goods where cheap>200;
结果:ERROR 1054 <42S22>: Unkown column 'cheap' in 'where clause'
注意,这样是不能够实习的!因为,where是对表起作用的,而不是对查询到的结果起作用。那么,我们要怎么样才能够实现:查询出本店价格比市场价格低多少钱,并且把低于200元以上的商品选出来呢?

方法一:
select goods_id, goods_name, sum(market_price-goods_price) as cheap from goods where market_price-goods_price>200;
这种方法可以实现,不过查询的时候计算了两次。注意,结果显示的是cheap列,而不是market_price-goods_price列。

方法二:
having,可以筛选查询的结果。
select goods_id, goods_name, sum(market_price-goods_price) as cheap from goods having cheap>200;

//当一条语句有where和having,where在前
//查询本店价格比市场价格低多少钱,并且把第3个栏目下比市场价格低于200元以上的商品选出来
select goods_id, cat_id, goods_name, sum(market_price-goods_price) as cheap from goods where cat_id=3 having cheap>200;

//查询积压货款超过2W元的栏目,以及该栏目积压的货款
//先查询栏目积压的货款
select cat_id, sum(goods_number*shop_price) as jyhk from goods group by cat_id;
//然后用having jyhk>2W筛选积压货款超过2W的栏目
select cat_id, sum(goods_number*shop_price) as jyhk from goods group by cat_id having jyhk>2W;

总结:where针对表中的列发挥作用,查询数据;having针对查询结果中的列发挥作用,筛选数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
练习:设有成绩表grades如下:
姓名:张三 张三 张三 李四 李四 王五
科目:数学 语文 地理 语文 政治 政治
分数: 90 50 40 55 45 30
查询两门及两门以上不及格同学的平均分。要求只用一个select。


//先创建表grades
create table grades (
name varchar(10) not null default '',
subject varchar(10) not null default '',
score tinyint not null default 0
);

//插入数据
insert into grades
values
('张三', '数学', 90),
('张三', '语文', 50),
('张三', '地理', 40),
('李四', '语文', 55),
('李四', '政治', 45),
('王五', '政治', 30);

mysql> select * from grades;
+--------+---------+-------+
| name | subject | score |
+--------+---------+-------+
| 张三 | 数学 | 90 |
| 张三 | 语文 | 50 |
| 张三 | 地理 | 40 |
| 李四 | 语文 | 55 |
| 李四 | 政治 | 45 |
| 王五 | 政治 | 30 |
+--------+---------+-------+
6 rows in set (0.00 sec)

//先求每个人成绩的平均分
mysql> select name, avg(score) from grades group by name;
+--------+------------+
| name | avg(score) |
+--------+------------+
| 张三 | 60.0000 |
| 李四 | 50.0000 |
| 王五 | 30.0000 |
+--------+------------+
3 rows in set (0.10 sec)

//再查找有两门及两门以上不及格的同学
select name, count(score<60) as fail from grades group by name having fail>=2;
+--------+------+
| name | fail |
+--------+------+
| 张三 | 3 |
| 李四 | 2 |
+--------+------+
2 rows in set (0.04 sec)

//最后,合成一条select语句
select name, avg(score), count(score<60) as fail from grades group by name having fail>=2;
+--------+------------+------+
| name | avg(score) | fail |
+--------+------------+------+
| 张三 | 60.0000 | 3 |
| 李四 | 50.0000 | 2 |
+--------+------------+------+
2 rows in set (0.00 sec)

注意,这种做法看上去像是对的。其实,这是错误的做法。因为,查询结果显示张三挂了3科,其实他只挂了2科。count是计算所有行数,它不论大于还是小于60分的都计算了的。我们可以验证一下:
insert into grades
values
('赵六', '语文',99),
('赵六', '数学',98),
('赵六', '政治',97);

mysql> select * from grades;
+--------+---------+-------+
| name | subject | score |
+--------+---------+-------+
| 张三 | 数学 | 90 |
| 张三 | 语文 | 50 |
| 张三 | 地理 | 40 |
| 李四 | 语文 | 55 |
| 李四 | 政治 | 45 |
| 王五 | 政治 | 30 |
| 赵六 | 语文 | 99 |
| 赵六 | 数学 | 98 |
| 赵六 | 政治 | 97 |
+--------+---------+-------+
9 rows in set (0.00 sec)

select name, avg(score), count(score<60) as fail from grades group by name having fail>=2;
+--------+------------+------+
| name | avg(score) | fail |
+--------+------------+------+
| 张三 | 60.0000 | 3 |
| 李四 | 50.0000 | 2 |
| 赵六 | 98.0000 | 3 |
+--------+------------+------+
3 rows in set (0.00 sec)
我们可以看到count(score<60)并不是计算score<60的行数,而是计算所有行的行数。因为
select name, score<60 from grades;
+--------+----------+
| name | score<60 |
+--------+----------+
| 张三 | 0 |
| 张三 | 1 |
| 张三 | 1 |
| 李四 | 1 |
| 李四 | 1 |
| 王五 | 1 |
| 赵六 | 0 |
| 赵六 | 0 |
| 赵六 | 0 |
+--------+----------+
9 rows in set (0.00 sec)
score<60的值不是0就是1,因为,score<60不是真就是假的。但是,无论是0还是1,score<60都有一行,而count(score<60)是计算score<60的行数。
所以,我们可以计算score<60的值为1的行数来查看不及格的门数。
select name, avg(score), sum(score<60) as fail from grades group by name having fail>=2;
+--------+------------+------+
| name | avg(score) | fail |
+--------+------------+------+
| 张三 | 60.0000 | 2 |
| 李四 | 50.0000 | 2 |
+--------+------------+------+
2 rows in set (0.02 sec)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
order by

order by:排序功能,按一个或多个字段对查询结果进行排序,可以是升序排序,也可以是降序排序,默认是升序排序。
select goods_id, cat_id, goods_name, shop_price from goods where cat_id=3

order by shop_price;
这条语句查询显示的结果是按照shop_price的值由小到大排序的(默认升序),如果我们想查询结果由降序排序,要怎么做呢?可以在后面加上字段名desc。
desc:按降序排序; asc:按升序排序(默认也是升序排序,所以可以不加)
select goods_id, cat_id, goods_name, shop_price from goods where cat_id=3 order by shop_price desc;

//查询栏目号从低到高,而且栏目内的商品价格从高到低的排序结果
select goods_id, cat_id, goods_name, shop_price from goods where goods_id<50 order by cat_id, shop_price desc; //先按cat_id排序,再在栏目内

按shop_price从高到低排序

//按发布时间从早到晚排序(发布时间add_time是用时间戳来存储的)
select goods_id, goods_name, add_time from goods order by add_time;
我们可以看到add_time的值是int型的,表示1970-01-01 00:00:00 到当前的秒数。

我们可以按字段排序,查询显示的结果是符合条件所有行的数据。如果我们只是想要显示排序之后的某几行,又要怎么做呢?limit语句可以帮我们实现。
limit [offset] [n]
offset:偏移量,offset如果不写,则相当于0,即limit 0, n。所以,是从第offset+1个开始。如limit 3,2 表示偏移前3个,从第4个开始选择2个,即4、5
n:取出的条目
//取出商品价格最贵的3个商品的信息
select goods_id, cat_id, goods_name,shop_price from goods order by shop_price desc limit 3;
//取出最新发布的商品
select goods_id, cat_id, goods_name, add_time, shop_price from goods order by add_time desc limit 1;


//取出每个栏目下最贵的商品

错误一:
select goods_id, cat_id, goods_name, max(shop_price) from goods;
这种做法查出来的是的确是shop_price最大的商品,不过shop_price不一定是和前面的goods_id, cat_id, goods_name是对应的。

错误二:
select goods_id, cat_id, goods_name, shop_price from goods group by cat_id order by shop_price desc;
这种做法也能查出每个栏目下的一个商品来,不过这个商品不一定是每个栏目下最贵的商品,这个商品只是每个栏目下的第一个商品。仔细观察我们可以发现查询结果中shop_price列的价格是按照从大到小来排序的,这就说明了,这条语句查询的结果是把每个栏目下的第一个商品拿出来,后面的order by shop_price desc实现的是对拿出来的每个栏目下的第一个商品按照shop_price从大到小排序。

错误三:
根据上面的做法,有的同学就会想到:如果我先实现 order by shop_price desc 再来实现group by不就行了吗。
select goods_id, cat_id, goods_name, shop_price from goods order by

shop_price desc group by cai_id;
这种做法也一样是不正确的,因为:
select的5种子句:where、group by、having、order by、limit是按照顺序来使用的,不能把后面的子句放到前面来先使用。

错误四:
有的同学可能会想,我先构造一张表goods1,表里面的数据是按照order by cat_id asc,shop_price desc顺序排列的,然后,再调用group by来取出每个栏目下的第一个商品不就行了吗。
//创建表goods1
create lzs.goods1 like goods;
//将表goods的数据按照order by cat_id,shop_price desc顺序保存到goods1
insert into goods1 select * from goods order by cat_id, shop_price desc;
//最后,用group by取出每个栏目下的第一个商品就可以了
select goods_id, cat_id, goods_name, shop_price from goods1 group by cat_id;
这种做法也是错误的,因为在将表goods的数据按照order by cat_id asc,

shop_price desc顺序存进goods1的时候,保存进goods1的数据根本就没有按照想要的顺序排序,而是按照goods原来的顺序保存进来的。所以,最后group by取出来的数据还是不对的。

其实,我们不用创建一张新表,因为查询的结果本来就可以在内存中当作表来使用,所以我们可以这样实现:
select * from (selcet goods_id, cat_id, goods_name, shop_price from goods order by cat_id asc, shop_price desc) as goods1 group by cat_id;
这种做法是from型子查询,但是,如果要求只用一个select,又要怎么去实现呢?

1
2
3
4
良好的理解模型:
where 表达式:把表达式放在行中,看表达式是否为真
列:理解成变量,可以运算
取出结果:可以理解成一张临时表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
子查询

where型子查询:把内层查询的结果作为外层查询的比较条件
from型子查询:把内层查询的结果当成临时表,供外层再次查询
exists型子查询:把外层的查询结果,拿到内层,看内层的查询是否成立

//查询最新的商品(以id最大为最新)
select goods_id, cat_id, goods_name from goods order by goods_id desc limit 1;
如果,不能用order by呢?又要怎么做?我们可以用where子查询来实现:
select goods_id, cat_id, goods_name from goods where goods_id = (select

max(goods_id) from goods);

//查询每个栏目下最贵的商品
select goods_id, cat_id, goods_name shop_price from goods where shop_price in(select max(shop_price) from goods group by cat_id)
//上面我们用from型子查询是这样实现的:
select * from (select goods_id, cat_id, goods_name, shop_price from goods order by shop_price desc) as goods1 group by cat_id;
from型子查询,是先select内层order by shop_price desc查询的结果,然后利用group by cat_id来取出第一个。注意,from型子查询必须要给内层查询的结果加上一个别名。
而where型子查询,是选出每栏目下max(shop_price)的商品,如果一个栏目下有几个商品都是同价格的而且还是最贵的,这几个商品都是内层查询的结果来的,都是在in里面的值。所以,都是可以查询出来的。

//利用上面的grades表,如何用子查询查出挂科两门及两门以上同学的平均分
//不用子查询
select name, avg(score), sum(score<60) as fail from grades group by name having fail>=2;
//子查询
//先查出挂机两门及两门以上的同学
select name, sum(score<60) as fail from grades group by name having fail>=2;//不过,我们要的只是name,所以,还要选出name来:
select name from (select name, sum(score<60) as fail from grades group by name having fail>=2);
//最后根据名字查找平均分
select name, avg(score) from grades where name in(select name from (select name, sum(score<60) as fail from grades group by name having fail>=2) as tmp) group by name;
注意,from型子查询必须要给内层查询的结果加上一个别名,所以,要as tmp

//或者
select name, avg(score) from grades where name in(select name from (select name, count(*) as fail from grades where score<60 group by name having fail>=2) as tmp) group by name;

//查询有商品的栏目(栏目表名category)
//我们先看一下,下面这条语句实现的是什么
select cat_id, cat_name from category where exists(select * form goods);
显示的结果是:所有的cat_id,cat_name。因为,只要goods里面有内容,exists(select * from goods)就会为真,显示的结果就是所有的cat_id和cat_name。那么如果要查询有商品的栏目,我们只要将exists里面的判断设置为有商品就为真就可以了。
select cat_id, cat_name from category where exists(select * from goods where goods.cat_id=category.cat_id);
exists(select * from goods where goods.cat_id=category.cat_id)的意思是,只要商品表goods里面有栏目表category相等的id就表示,有商品里面有该栏目的商品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
select的5中子句的总结:

where 表达式
表达式在哪一行成立,哪一行就取出来
where常用运算符:=,!=/<>,<,>,<=,>=,in(),between and
where逻辑运算符:and、or、not
模糊查询:like 通配符:%,任意字符;_,单个字符

group by:
分组,一般和统计函数配合使用
常用的统计函数:max(),min(),avg(),sum(),count()

having 表达式
数据在表中,表在硬盘或者内存以文件形式存在。
查询出的结果,也可以看成一张表,其文件一般临时存放在缓冲区。
where,针对表文件发挥作用;
having,针对查询结果发挥作用。

order by:
作用:对字段排序,可以升序asc,也可以降序desc。
有可能一个字段排不出结果,可以选用其它字段继续排序:
order by 字段1[asc/desc], 字段2[asc/desc] ...
如,order by cat_id, shop_price desc

limit:
作用:限制条目
limit [offset] n
offset:偏移量,不填就是默认0
n:限制取出的条目数量
如,取出shop_price最贵的3个商品:
... order by shop_price desc limit 3;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
select的子查询

where型子查询:
内层的查询结果作为外层查询的比较条件
例如,查询最新商品(以goods_id最大为最新)
select * from goods where goods_id = 最大的goods_id;
select * from goods where goods_id = (select max(goods_id) from goods);

from型子查询:
把内层的查询结果供外层再次查询。
注意,内层的查询结果看成临时表,要加'as 临时表名'

exists型子查询:
把外层的查询结果代入到内层,看内层是否成立。
例如,查询有商品的栏目
select cat_id, cat_name from category where exists(select * from goods.cat_id=category.cat_id);
如果select * from goods where goods.cat_id;的结果有值就说明 cat_id 有商品。