SQL操作

一、查询语句

        1、基本查询        

        SELECT * FROM <表名>

        查询表的所有行:    SELECT * FROM students;

         2、条件查询       

        SELECT * FROM <表名> WHERE <条件表达式>

        查询分数在80分以上的学生记录:

SELECT * FROM students WHERE score >= 80;

         2.1 条件表达式可以用<条件1> AND <条件2>表达满足条件1并且满足条件2

        例:符合条件“分数在80分或以上”,并且还符合条件“男生”             

  SELECT * FROM students WHERE score >= 80 AND gender = 'M';

        2.2 第二种条件是<条件1> OR <条件2>,表示满足条件1或者满足条件2

        例:把上述AND查询的两个条件改为OR,查询结果就是“分数在80分或以上”或者“男生”,满足任意之一的条件即选出该记录

     SELECT * FROM students WHERE score >= 80 OR gender = 'M';

       2.3  第三种条件是NOT <条件>,表示“不符合该条件”的记录

        例如,写一个“不是2班的学生”这个条件,可以先写出“是2班的学生”:class_id = 2,再加上NOTNOT class_id = 2

SELECT * FROM students WHERE NOT class_id = 2;

        2.4 要组合三个或者更多的条件,就需要用小括号()表示如何进行条件运算

        例如,编写一个复杂的条件:分数在80以下或者90以上,并且是男生:

   SELECT * FROM students WHERE (score < 80 OR score > 90) AND gender = 'M';

 注意:如果不加括号,条件运算按照NOTANDOR的优先级进行,即NOT优先级最高,其次是AND,最后是OR。加上括号可以改变优先级。

 3、投影查询

        使用SELECT * FROM <表名> WHERE <条件>可以选出表中的若干条记录。我们注意到返回的二维表结构和原表是相同的,即结果集的所有列与原表的所有列都一一对应。如果我们只希望返回某些列的数据,而不是所有列的数据,我们可以用SELECT 列1, 列2, 列3 FROM ...,让结果集仅包含指定列。这种操作称为投影查询。

   SELECT id, score, name FROM students;

        3.1   使用SELECT 列1, 列2, 列3 FROM ...时,还可以给每一列起个别名,这样,结果集的列名就可以与原表的列名不同。它的语法是SELECT 列1 别名1, 列2 别名2, 列3 别名3 FROM ...

 例如,以下SELECT语句将列名score重命名为points,而idname列名保持不变:

SELECT id, score points, name FROM students;

         3.2 投影查询同样可以接WHERE条件,实现复杂的查询:

SELECT id, score points, name FROM students WHERE gender = 'M';

 4、排序

 4.1  我们使用SELECT查询时,细心的读者可能注意到,查询结果集通常是按照id排序的,也就是根据主键排序。这也是大部分数据库的做法。如果我们要根据其他条件排序怎么办?可以加上ORDER BY子句

例如按照成绩从低到高进行排序:

 SELECT id, name, gender, score FROM students ORDER BY score;

如果要反过来,按照成绩从高到底排序,我们可以加上DESC表示“倒序”:

        

SELECT id, name, gender, score FROM students ORDER BY score DESC;

 4.2  如果score列有相同的数据,要进一步排序,可以继续添加列名。例如,使用ORDER BY score DESC, gender表示先按score列倒序,如果有相同分数的,再按gender列排序:

按score, gender排序:
SELECT id, name, gender, score FROM students ORDER BY score DESC, gender;

 4.3  默认的排序规则是ASC:“升序”,即从小到大。ASC可以省略,即ORDER BY score ASCORDER BY score效果一样。

如果有WHERE子句,那么ORDER BY子句要放到WHERE子句后面。

例如,查询一班的学生成绩,并按照倒序排序:

SELECT id, name, gender, score
FROM students
WHERE class_id = 1
ORDER BY score DESC;

 5、分页查询

使用SELECT查询时,如果结果集数据量很大,比如几万行数据,放在一个页面显示的话数据量太大,不如分页显示,每次显示100条。

要实现分页功能,实际上就是从结果集中显示第1~100条记录作为第1页,显示第101~200条记录作为第2页,以此类推。

因此,分页实际上就是从结果集中“截取”出第M~N条记录。这个查询可以通过LIMIT <M> OFFSET <N>子句实现

5.1  我们把结果集分页,每页3条记录。要获取第1页的记录,可以使用LIMIT 3 OFFSET 0

 --查询第1页
SELECT id, name, gender, score
FROM students
ORDER BY score DESC
LIMIT 3 OFFSET 0;

上述查询LIMIT 3 OFFSET 0表示,对结果集从0号记录开始,最多取3条。注意SQL记录集的索引从0开始。 

如果要查询第2页,那么我们只需要“跳过”头3条记录,也就是对结果集从3号记录开始查询,把OFFSET设定为3:

SELECT id, name, gender, score
FROM students
ORDER BY score DESC
LIMIT 3 OFFSET 3;

类似的,查询第3页的时候,OFFSET应该设定为6:

SELECT id, name, gender, score
FROM students
ORDER BY score DESC
LIMIT 3 OFFSET 6;

查询第4页的时候,OFFSET应该设定为9:

SELECT id, name, gender, score
FROM students
ORDER BY score DESC
LIMIT 3 OFFSET 9;

由于第4页只有1条记录,因此最终结果集按实际数量1显示。LIMIT 3表示的意思是“最多3条记录”。

可见,分页查询的关键在于,首先要确定每页需要显示的结果数量pageSize(这里是3),然后根据当前页的索引pageIndex(从1开始),确定LIMITOFFSET应该设定的值:

  • LIMIT总是设定为pageSize
  • OFFSET计算公式为pageSize * (pageIndex - 1)

这样就能正确查询出第N页的记录集。

如果原本记录集一共就10条记录,但我们把OFFSET设置为20,会得到什么结果呢?

SELECT id, name, gender, score
FROM students
ORDER BY score DESC
LIMIT 3 OFFSET 20;

OFFSET超过了查询的最大数量并不会报错,而是得到一个空的结果集。

 注意:

OFFSET是可选的,如果只写LIMIT 15,那么相当于LIMIT 15 OFFSET 0

在MySQL中,LIMIT 15 OFFSET 30还可以简写成LIMIT 30, 15

使用LIMIT <M> OFFSET <N>分页时,随着N越来越大,查询效率也会越来越低。

 6、聚合查询

        如果我们要统计一张表的数据量,例如,想查询students表一共有多少条记录,难道必须用SELECT * FROM students查出来然后再数一数有多少行吗?

这个方法当然可以,但是比较弱智。对于统计总数、平均数这类计算,SQL提供了专门的聚合函数,使用聚合函数进行查询,就是聚合查询,它可以快速获得结果。

仍然以查询students表一共有多少条记录为例,我们可以使用SQL内置的COUNT()函数查询:

SELECT COUNT(*) FROM students;
COUNT(*)
10

COUNT(*)表示查询所有列的行数,要注意聚合的计算结果虽然是一个数字,但查询的结果仍然是一个二维表,只是这个二维表只有一行一列,并且列名是COUNT(*)

通常,使用聚合查询时,我们应该给列名设置一个别名,便于处理结果:

-- 使用聚合查询并设置结果集的列名为num:
SELECT COUNT(*) num FROM students;
num
10

COUNT(*)COUNT(id)实际上是一样的效果。另外注意,聚合查询同样可以使用WHERE条件,因此我们可以方便地统计出有多少男生、多少女生、多少80分以上的学生等: 

-- 使用聚合查询并设置WHERE条件:
SELECT COUNT(*) boys FROM students WHERE gender = 'M';

除了COUNT()函数外,SQL还提供了如下聚合函数:

函数说明
SUM计算某一列的合计值,该列必须为数值类型
AVG计算某一列的平均值,该列必须为数值类型
MAX计算某一列的最大值
MIN计算某一列的最小值

 注意:MAX()MIN()函数并不限于数值类型。如果是字符类型,MAX()MIN()会返回排序最后和排序最前的字符。

-- 使用聚合查询计算男生平均成绩:

SELECT AVG(score) average FROM students WHERE gender = 'M';

 要特别注意:如果聚合查询的WHERE条件没有匹配到任何行,COUNT()会返回0,而SUM()AVG()MAX()MIN()会返回NULL

-- WHERE条件gender = 'X'匹配不到任何行:
SELECT AVG(score) average FROM students WHERE gender = 'X';
average
NULL

 分组:

如果我们要统计一班的学生数量,我们知道,可以用SELECT COUNT(*) num FROM students WHERE class_id = 1;。如果要继续统计二班、三班的学生数量,难道必须不断修改WHERE条件来执行SELECT语句吗?

对于聚合查询,SQL还提供了“分组聚合”的功能。我们观察下面的聚合查询:

 -- 按class_id分组:

SELECT COUNT(*) num FROM students GROUP BY class_id;

 结果:

num
4
3
3

执行这个查询,COUNT()的结果不再是一个,而是3个,这是因为,GROUP BY子句指定了按class_id分组,因此,执行该SELECT语句时,会把class_id相同的列先分组,再分别计算,因此,得到了3行结果。

但是这3行结果分别是哪三个班级的,不好看出来,所以我们可以把class_id列也放入结果集中:

-- 按class_id分组:
SELECT class_id, COUNT(*) num FROM students GROUP BY class_id;
class_idnum
14
23
33
这下结果集就可以一目了然地看出各个班级的学生人数。我们再试试把 name放入结果集:
SELECT name, class_id, COUNT(*) num FROM students GROUP BY class_id;
nameclass_idnum
NULL14
NULL23
NULL33

 不出意外,执行这条查询我们会得到一个语法错误,因为在任意一个分组中,只有class_id都相同,name是不同的,SQL引擎不能把多个name的值放入一行记录中。因此,聚合查询的列中,只能放入分组的列。

也可以使用多个列进行分组。例如,我们想统计各班的男生和女生人数:

-- 按class_id, gender分组:
SELECT class_id, gender, COUNT(*) num FROM students GROUP BY class_id, gender;
class_idgendernum
1M2
1F2
2F1
2M2
3F2
3M1
上述查询结果集一共有6条记录,分别对应各班级的男生和女生人数。

 7、多表查询

SELECT查询不但可以从一张表查询数据,还可以从多张表同时查询数据。查询多张表的语法是:SELECT * FROM <表1> <表2>

例如,同时从students表和classes表的“乘积”,即查询数据,可以这么写:

SELECT * FROM students, classes;
idclass_idnamegenderscoreidname
11小明M901一班
11小明M902二班
11小明M903三班
11小明M904四班
21小红F951一班
21小红F952二班
21小红F953三班
21小红F954四班
31小军M881一班
31小军M882二班
31小军M883三班
31小军M884四班
41小米F731一班
41小米F732二班
41小米F733三班
41小米F734四班
52小白F811一班
52小白F812二班
52小白F813三班
52小白F814四班
62小兵M551一班
62小兵M552二班
62小兵M553三班
62小兵M554四班
72小林M851一班
72小林M852二班
72小林M853三班
72小林M854四班
83小新F911一班
83小新F912二班
83小新F913三班
83小新F914四班
93小王M891一班
93小王M892二班
93小王M893三班
93小王M894四班
103小丽F881一班
103小丽F882二班
103小丽F883三班
103小丽F884四班

 这种一次查询两个表的数据,查询的结果也是一个二维表,它是students表和classes表的“乘积”,即students表的每一行与classes表的每一行都两两拼在一起返回。结果集的列数是students表和classes表的列数之和,行数是students表和classes表的行数之积。

这种多表查询又称笛卡尔查询,使用笛卡尔查询时要非常小心,由于结果集是目标表的行数乘积,对两个各自有100行记录的表进行笛卡尔查询将返回1万条记录,对两个各自有1万行记录的表进行笛卡尔查询将返回1亿条记录。

你可能还注意到了,上述查询的结果集有两列id和两列name,两列id是因为其中一列是students表的id,而另一列是classes表的id,但是在结果集中,不好区分。两列name同理

要解决这个问题,我们仍然可以利用投影查询的“设置列的别名”来给两个表各自的idname列起别名:

SELECT
    students.id sid,
    students.name,
    students.gender,
    students.score,
    classes.id cid,
    classes.name cname
FROM students, classes;
sidnamegenderscorecidcname
1小明M901一班
1小明M902二班
1小明M903三班
1小明M904四班
2小红F951一班
2小红F952二班
2小红F953三班
2小红F954四班
3小军M881一班
3小军M882二班
3小军M883三班
3小军M884四班
4小米F731一班
4小米F732二班
4小米F733三班
4小米F734四班
5小白F811一班
5小白F812二班
5小白F813三班
5小白F814四班
6小兵M551一班
6小兵M552二班
6小兵M553三班
6小兵M554四班
7小林M851一班
7小林M852二班
7小林M853三班
7小林M854四班
8小新F911一班
8小新F912二班
8小新F913三班
8小新F914四班
9小王M891一班
9小王M892二班
9小王M893三班
9小王M894四班
10小丽F881一班
10小丽F882二班
10小丽F883三班
10小丽F884四班

注意:多表查询时,要使用表名.列名这样的方式来引用列和设置别名,这样就避免了结果集的列名重复问题。但是,用表名.列名这种方式列举两个表的所有列实在是很麻烦,所以SQL还允许给表设置一个别名,让我们在投影查询中引用起来稍微简洁一点:

SELECT
    s.id sid,
    s.name,
    s.gender,
    s.score,
    c.id cid,
    c.name cname
FROM students s, classes c;

 多表查询也是可以添加WHERE条件的,我们来试试:

SELECT
    s.id sid,
    s.name,
    s.gender,
    s.score,
    c.id cid,
    c.name cname
FROM students s, classes c
WHERE s.gender = 'M' AND c.id = 1;
sidnamegenderscorecidcname
1小明M901一班
3小军M881一班
6小兵M551一班
7小林M851一班
9小王M891一班
这个查询的结果集每行记录都满足条件 s.gender = 'M'c.id = 1。添加 WHERE条件后结果集的数量大大减少了。

 8、连接查询

连接查询是另一种类型的多表查询。连接查询对多个表进行JOIN运算,简单地说,就是先确定一个主表作为结果集,然后,把其他表的行有选择性地“连接”在主表结果集上。

例如,我们想要选出students表的所有学生信息,可以用一条简单的SELECT语句完成:

SELECT s.id, s.name, s.class_id, s.gender, s.score FROM students s;
idnameclass_idgenderscore
1小明1M90
2小红1F95
3小军1M88
4小米1F73
5小白2F81
6小兵2M55
7小林2M85
8小新3F91
9小王3M89
10小丽3F88

但是,假设我们希望结果集同时包含所在班级的名称,上面的结果集只有class_id列,缺少对应班级的name列。

现在问题来了,存放班级名称的name列存储在classes表中,只有根据students表的class_id,找到classes表对应的行,再取出name列,就可以获得班级名称。

这时,连接查询就派上了用场。我们先使用最常用的一种内连接——INNER JOIN来实现:

-- 选出所有学生,同时返回班级名称
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
INNER JOIN classes c
ON s.class_id = c.id;
idnameclass_idclass_namegenderscore
1小明1一班M90
2小红1一班F95
3小军1一班M88
4小米1一班F73
5小白2二班F81
6小兵2二班M55
7小林2二班M85
8小新3三班F91
9小王3三班M89
10小丽3三班F88

注意:INNER JOIN查询的写法是:

  1. 先确定主表,仍然使用FROM <表1>的语法;
  2. 再确定需要连接的表,使用INNER JOIN <表2>的语法;
  3. 然后确定连接条件,使用ON <条件...>,这里的条件是s.class_id = c.id,表示students表的class_id列与classes表的id列相同的行需要连接;
  4. 可选:加上WHERE子句、ORDER BY等子句。

使用别名不是必须的,但可以更好地简化查询语句。

 那什么是内连接(INNER JOIN)呢?先别着急,有内连接(INNER JOIN)就有外连接(OUTER JOIN)。我们把内连接查询改成外连接查询,看看效果:

 -- 使用OUTER JOIN

SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
RIGHT OUTER JOIN classes c
ON s.class_id = c.id;
idnameclass_idclass_namegenderscore
1小明1一班M90
2小红1一班F95
3小军1一班M88
4小米1一班F73
5小白2二班F81
6小兵2二班M55
7小林2二班M85
8小新3三班F91
9小王3三班M89
10小丽3三班F88
NULLNULLNULL四班NULLNULL

 

执行上述RIGHT OUTER JOIN可以看到,和INNER JOIN相比,RIGHT OUTER JOIN多了一行,多出来的一行是“四班”,但是,学生相关的列如namegenderscore都为NULL

这也容易理解,因为根据ON条件s.class_id = c.idclasses表的id=4的行正是“四班”,但是,students表中并不存在class_id=4的行。

有RIGHT OUTER JOIN,就有LEFT OUTER JOIN,以及FULL OUTER JOIN。它们的区别是:

INNER JOIN只返回同时存在于两张表的行数据,由于students表的class_id包含1,2,3,classes表的id包含1,2,3,4,所以,INNER JOIN根据条件s.class_id = c.id返回的结果集仅包含1,2,3。

RIGHT OUTER JOIN返回右表都存在的行。如果某一行仅在右表存在,那么结果集就会以NULL填充剩下的字段。

LEFT OUTER JOIN则返回左表都存在的行。如果我们给students表增加一行,并添加class_id=5,由于classes表并不存在id=5的行,所以,LEFT OUTER JOIN的结果会增加一行,对应的class_nameNULL

 -- 先增加一列class_id=5: INSERT INTO students (class_id, name, gender, score) values (5, '新生', 'M', 88); -- 使用LEFT OUTER JOIN

SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
LEFT OUTER JOIN classes c
ON s.class_id = c.id;
idnameclass_idclass_namegenderscore
1小明1一班M90
2小红1一班F95
3小军1一班M88
4小米1一班F73
5小白2二班F81
6小兵2二班M55
7小林2二班M85
8小新3三班F91
9小王3三班M89
10小丽3三班F88
11新生5NULLM88

 最后,我们使用FULL OUTER JOIN,它会把两张表的所有记录全部选择出来,并且,自动把对方不存在的列填充为NULL:

-- 使用FULL OUTER JOIN
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
FULL OUTER JOIN classes c
ON s.class_id = c.id;
idnameclass_idclass_namegenderscore
1小明1一班M90
2小红1一班F95
3小军1一班M88
4小米1一班F73
5小白2二班F81
6小兵2二班M55
7小林2二班M85
8小新3三班F91
9小王3三班M89
10小丽3三班F88
11新生5NULLM88
NULLNULLNULL四班NULLNULL

 对于这么多种JOIN查询,到底什么使用应该用哪种呢?其实我们用图来表示结果集就一目了然了。

SELECT ... FROM tableA ??? JOIN tableB ON tableA.column1 = tableB.column2;

 我们把tableA看作左表,把tableB看成右表,那么INNER JOIN是选出两张表都存在的记录:

 

inner-join

LEFT OUTER JOIN是选出左表存在的记录:

left-outer-join

RIGHT OUTER JOIN是选出右表存在的记录:

right-outer-join

FULL OUTER JOIN则是选出左右表都存在的记录:

full-outer-join

JOIN查询需要先确定主表,然后把另一个表的数据“附加”到结果集上;

INNER JOIN是最常用的一种JOIN查询,它的语法是SELECT ... FROM <表1> INNER JOIN <表2> ON <条件...>

JOIN查询仍然可以使用WHERE条件和ORDER BY排序。

 

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值