03-MySQL多表操作

一、表之间的关系

  1、如何找出两张表之间的关系

分析步骤:
#1、先站在左表的角度去找
是否左表的多条记录可以对应右表的一条记录,如果是,则证明左表的一个字段foreign key 右表一个字段(通常是id)

#2、再站在右表的角度去找
是否右表的多条记录可以对应左表的一条记录,如果是,则证明右表的一个字段foreign key 左表一个字段(通常是id)

#3、总结:
#一对多:
如果只有步骤1成立,则是左表多对一右表
如果只有步骤2成立,则是右表多对一左表

#多对多
如果步骤1和2同时成立,则证明这两张表时一个双向的多对一,即多对多,需要定义一个这两张表的关系表来专门存放二者的关系

#一对一:
如果1和2都不成立,而是左表的一条记录唯一对应右表的一条记录,反之亦然。这种情况很简单,就是在左表foreign key右表的基础上,将左表的外键字段设置成unique即可

  2、建立两张表之间的关系

# 一对多关系
# 咱们以出版社和书籍为例(大家分析一下,二者之间的关系)。
# 1、创建表
create table press(
id int primary key auto_increment,
name varchar(32)
);

create table book(
id int primary key auto_increment,
name varchar(32),
press_id int,
foreign key(press_id) references press(id) on delete cascade on update cascade
);

# 2、插入记录
# 先插被关联的表
insert into press(name) values
('华山出版社'),
('少林出版社'),
('明教出版社'),
('武当出版社'),
('丐帮出版社');


# 再插关联的表
insert into book(name,press_id) values
('独孤九剑',1),
('华山剑法',1),
('九阳神功',2),
('九阴真经',2),
('易筋经',2),
('葵花宝典',3),
('乾坤大挪移',3),
('太极拳',4),
('天罡北斗阵',4);

insert into book(name) values("吸星大法");
# 多对多关系 #
1、创建表 # 先建作者表 create table author( id int primary key auto_increment, name varchar(32) ); # 再建存放作者与书的关系表,即查询二者的关系查这表就可以了 create table author2book( id int not null unique auto_increment, author_id int, book_id int, foreign key(author_id) references author(id) on delete cascade on update cascade, foreign key(book_id) references book(id) on delete cascade on update cascade, primary key(author_id,book_id) ); # 2、插入记录 # 先插作者表 insert into author(name) values ('张无忌'), ('令狐冲'), ('东方不败'), ('张三丰'), ('冲虚道长'); # 再插入对应的关系记录 insert into author2book(author_id,book_id) values (1,3), (1,7), (2,1), (2,2), (3,6), (4,8); # 一对一关系 # 两张表:学生表和客户表 # 一对一:一个学生是一个客户,一个客户有可能变成一个学校,即一对一的关系 # 关联方式:foreign key+unique # 1、创建表 # 一定是student来foreign key表customer,这样就保证了: # 1 学生一定是一个客户, # 2 客户不一定是学生,但有可能成为一个学生 # 先建客户表 create table customer( id int primary key auto_increment, name varchar(32) not null, qq varchar(32)not null, tel varchar(32) not null ); # 再建学生表 create table student( id int primary key auto_increment, class_name varchar(32) not null, customer_id int unique, # 该字段一定要是唯一的 foreign key(customer_id) references student(id) on delete cascade on update cascade ); # 2、插入记录 # 先插客户表 insert into customer(name,qq,tel) values ('alex',1223,135666544), ('egon',34322,13438895), ('yuan',84322,13954893); insert into student(class_name,customer_id) values ('脱产2期',1), ('脱产3期',2), ('周末13期',3);

二、多表连接查询

  1、外连接语法

SELECT 字段列表
  FROM 表1 INNER|LEFT|RIGHT JOIN 表2
  ON 表1.字段 = 表2.字段;

  2、交叉连接(笛卡尔积)

# 注意:这种连接毫无意义
mysql> select * from book,press;  # 简单粗暴的将两张表连接起来 
+----+------------+----------+----+------------+
| id | name       | press_id | id | name       |
+----+------------+----------+----+------------+
|  1 | 独孤九剑   |        1 |  1 | 华山出版社 |
|  1 | 独孤九剑   |        1 |  2 | 少林出版社 |
|  1 | 独孤九剑   |        1 |  3 | 明教出版社 |
|  1 | 独孤九剑   |        1 |  4 | 武当出版社 |
|  1 | 独孤九剑   |        1 |  5 | 丐帮出版社 |
|  2 | 华山剑法   |        1 |  1 | 华山出版社 |
|  2 | 华山剑法   |        1 |  2 | 少林出版社 |
|  2 | 华山剑法   |        1 |  3 | 明教出版社 |
|  2 | 华山剑法   |        1 |  4 | 武当出版社 |
|  2 | 华山剑法   |        1 |  5 | 丐帮出版社 |
|  3 | 九阳神功   |        2 |  1 | 华山出版社 |
|  3 | 九阳神功   |        2 |  2 | 少林出版社 |
|  3 | 九阳神功   |        2 |  3 | 明教出版社 |
|  3 | 九阳神功   |        2 |  4 | 武当出版社 |
|  3 | 九阳神功   |        2 |  5 | 丐帮出版社 |
|  4 | 九阴真经   |        2 |  1 | 华山出版社 |
|  4 | 九阴真经   |        2 |  2 | 少林出版社 |
|  4 | 九阴真经   |        2 |  3 | 明教出版社 |
|  4 | 九阴真经   |        2 |  4 | 武当出版社 |
|  4 | 九阴真经   |        2 |  5 | 丐帮出版社 |
|  5 | 易筋经     |        2 |  1 | 华山出版社 |
|  5 | 易筋经     |        2 |  2 | 少林出版社 |
|  5 | 易筋经     |        2 |  3 | 明教出版社 |
|  5 | 易筋经     |        2 |  4 | 武当出版社 |
|  5 | 易筋经     |        2 |  5 | 丐帮出版社 |
|  6 | 葵花宝典   |        3 |  1 | 华山出版社 |
|  6 | 葵花宝典   |        3 |  2 | 少林出版社 |
|  6 | 葵花宝典   |        3 |  3 | 明教出版社 |
|  6 | 葵花宝典   |        3 |  4 | 武当出版社 |
|  6 | 葵花宝典   |        3 |  5 | 丐帮出版社 |
|  7 | 乾坤大挪移 |        3 |  1 | 华山出版社 |
|  7 | 乾坤大挪移 |        3 |  2 | 少林出版社 |
|  7 | 乾坤大挪移 |        3 |  3 | 明教出版社 |
|  7 | 乾坤大挪移 |        3 |  4 | 武当出版社 |
|  7 | 乾坤大挪移 |        3 |  5 | 丐帮出版社 |
|  8 | 太极拳     |        4 |  1 | 华山出版社 |
|  8 | 太极拳     |        4 |  2 | 少林出版社 |
|  8 | 太极拳     |        4 |  3 | 明教出版社 |
|  8 | 太极拳     |        4 |  4 | 武当出版社 |
|  8 | 太极拳     |        4 |  5 | 丐帮出版社 |
|  9 | 天罡北斗阵 |        4 |  1 | 华山出版社 |
|  9 | 天罡北斗阵 |        4 |  2 | 少林出版社 |
|  9 | 天罡北斗阵 |        4 |  3 | 明教出版社 |
|  9 | 天罡北斗阵 |        4 |  4 | 武当出版社 |
|  9 | 天罡北斗阵 |        4 |  5 | 丐帮出版社 |
| 10 | 吸星大法   |     NULL |  1 | 华山出版社 |
| 10 | 吸星大法   |     NULL |  2 | 少林出版社 |
| 10 | 吸星大法   |     NULL |  3 | 明教出版社 |
| 10 | 吸星大法   |     NULL |  4 | 武当出版社 |
| 10 | 吸星大法   |     NULL |  5 | 丐帮出版社 |
+----+------------+----------+----+------------+

# 咱们只想取到两表有关系的记录怎么办呢,有同学会说,我们加一个where条件。
mysql> select * from book,press where book.press_id=press.id;
+----+------------+----------+----+------------+
| id | name       | press_id | id | name       |
+----+------------+----------+----+------------+
|  1 | 独孤九剑   |        1 |  1 | 华山出版社 |
|  2 | 华山剑法   |        1 |  1 | 华山出版社 |
|  3 | 九阳神功   |        2 |  2 | 少林出版社 |
|  4 | 九阴真经   |        2 |  2 | 少林出版社 |
|  5 | 易筋经     |        2 |  2 | 少林出版社 |
|  6 | 葵花宝典   |        3 |  3 | 明教出版社 |
|  7 | 乾坤大挪移 |        3 |  3 | 明教出版社 |
|  8 | 太极拳     |        4 |  4 | 武当出版社 |
|  9 | 天罡北斗阵 |        4 |  4 | 武当出版社 |
+----+------------+----------+----+------------+
# 确实可以取到,但是不用这样自己去写where条件,SQL语句有对应的语法来实现。咱们接下来就详细说一下。

  3、内连接(inner join)

    内连接,只取两表的共同部分。

mysql> select * from book inner join press on book.press_id = press.id;
+----+------------+----------+----+------------+
| id | name       | press_id | id | name       |
+----+------------+----------+----+------------+
|  1 | 独孤九剑   |        1 |  1 | 华山出版社 |
|  2 | 华山剑法   |        1 |  1 | 华山出版社 |
|  3 | 九阳神功   |        2 |  2 | 少林出版社 |
|  4 | 九阴真经   |        2 |  2 | 少林出版社 |
|  5 | 易筋经     |        2 |  2 | 少林出版社 |
|  6 | 葵花宝典   |        3 |  3 | 明教出版社 |
|  7 | 乾坤大挪移 |        3 |  3 | 明教出版社 |
|  8 | 太极拳     |        4 |  4 | 武当出版社 |
|  9 | 天罡北斗阵 |        4 |  4 | 武当出版社 |
+----+------------+----------+----+------------+
# 此查询结果是不是跟上面的一样啊,说明上面的where条件就是内连接。

  4、左连接(left join)

    左连接,在内连接的基础上保留左边的记录。

mysql> select * from book left join press on book.press_id = press.id;
+----+------------+----------+------+------------+
| id | name       | press_id | id   | name       |
+----+------------+----------+------+------------+
|  1 | 独孤九剑   |        1 |    1 | 华山出版社 |
|  2 | 华山剑法   |        1 |    1 | 华山出版社 |
|  3 | 九阳神功   |        2 |    2 | 少林出版社 |
|  4 | 九阴真经   |        2 |    2 | 少林出版社 |
|  5 | 易筋经     |        2 |    2 | 少林出版社 |
|  6 | 葵花宝典   |        3 |    3 | 明教出版社 |
|  7 | 乾坤大挪移 |        3 |    3 | 明教出版社 |
|  8 | 太极拳     |        4 |    4 | 武当出版社 |
|  9 | 天罡北斗阵 |        4 |    4 | 武当出版社 |
| 10 | 吸星大法   |     NULL | NULL | NULL       |
+----+------------+----------+------+------------+

  5、右连接(right join)

    右连接,在内连接的基础上保留右边的记录。

mysql> select * from book right join press on book.press_id = press.id;
+------+------------+----------+----+------------+
| id   | name       | press_id | id | name       |
+------+------------+----------+----+------------+
|    1 | 独孤九剑   |        1 |  1 | 华山出版社 |
|    2 | 华山剑法   |        1 |  1 | 华山出版社 |
|    3 | 九阳神功   |        2 |  2 | 少林出版社 |
|    4 | 九阴真经   |        2 |  2 | 少林出版社 |
|    5 | 易筋经     |        2 |  2 | 少林出版社 |
|    6 | 葵花宝典   |        3 |  3 | 明教出版社 |
|    7 | 乾坤大挪移 |        3 |  3 | 明教出版社 |
|    8 | 太极拳     |        4 |  4 | 武当出版社 |
|    9 | 天罡北斗阵 |        4 |  4 | 武当出版社 |
| NULL | NULL       |     NULL |  5 | 丐帮出版社 |
+------+------------+----------+----+------------+

  6、全外连接

    全外连接,在内连接的基础上保留左右两表没有对应关系的记录。

# 注意:mysql不支持全外连接 full JOIN
mysql> select * from book full join press on book.press_id = press.id;
ERROR 1054 (42S22): Unknown column 'book.press_id' in 'on clause'

# 强调:mysql可以使用此种方式间接实现全外连接(自己实现全外连接的效果)
# union 连接两个以上的 SELECT 语句的结果组合到一个结果集合中。多个 SELECT 语句会删除重复的数据。
mysql> select * from book left join press on book.press_id = press.id
    -> union
    -> select * from book right join press on book.press_id = press.id;
+------+------------+----------+------+------------+
| id   | name       | press_id | id   | name       |
+------+------------+----------+------+------------+
|    1 | 独孤九剑   |        1 |    1 | 华山出版社 |
|    2 | 华山剑法   |        1 |    1 | 华山出版社 |
|    3 | 九阳神功   |        2 |    2 | 少林出版社 |
|    4 | 九阴真经   |        2 |    2 | 少林出版社 |
|    5 | 易筋经     |        2 |    2 | 少林出版社 |
|    6 | 葵花宝典   |        3 |    3 | 明教出版社 |
|    7 | 乾坤大挪移 |        3 |    3 | 明教出版社 |
|    8 | 太极拳     |        4 |    4 | 武当出版社 |
|    9 | 天罡北斗阵 |        4 |    4 | 武当出版社 |
|   10 | 吸星大法   |     NULL | NULL | NULL       |
| NULL | NULL       |     NULL |    5 | 丐帮出版社 |
+------+------------+----------+------+------------+

三、符合条件连接查询

  1、准备数据

# 咱们为book表新增单价字段
mysql> alter table book add price decimal(5,2) not null;
# 查看表结构
mysql> desc book;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| name     | varchar(32)  | YES  |     | NULL    |                |
| press_id | int(11)      | YES  | MUL | NULL    |                |
| price    | decimal(5,2) | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
# 新增的字段,默认为0
mysql> select * from book;
+----+------------+----------+-------+
| id | name       | press_id | price |
+----+------------+----------+-------+
|  1 | 独孤九剑   |        1 | 60.00 |
|  2 | 华山剑法   |        1 |  0.00 |
|  3 | 九阳神功   |        2 |  0.00 |
|  4 | 九阴真经   |        2 |  0.00 |
|  5 | 易筋经     |        2 |  0.00 |
|  6 | 葵花宝典   |        3 |  0.00 |
|  7 | 乾坤大挪移 |        3 |  0.00 |
|  8 | 太极拳     |        4 |  0.00 |
|  9 | 天罡北斗阵 |        4 |  0.00 |
| 10 | 吸星大法   |     NULL |  0.00 |
+----+------------+----------+-------+
# 更新新增字段的值
update book set price=60 where id=1;
update book set price=30 where id=2;
update book set price=70 where id=3;
update book set price=40 where id=4;
update book set price=80 where id=5;
update book set price=40 where id=6;
update book set price=50 where id=7;
update book set price=80 where id=8;
update book set price=30 where id=9;
update book set price=40 where id=10;

  2、查询操作

# 示例1:
# 以内连接的方式查询book和press表,并且book表中的price字段值必须大于50,即找出单价大于50元的书籍以及书籍的出版社名称
mysql> select book.name,press.name from book inner join press on book.press_id =
 press.id where price > 50;
+----------+------------+
| name     | name       |
+----------+------------+
| 独孤九剑 | 华山出版社 |
| 九阳神功 | 少林出版社 |
| 易筋经   | 少林出版社 |
| 太极拳   | 武当出版社 |
+----------+------------+


#示例2:
# 以内连接的方式查询book和press表,并且以price字段的降序方式显示书籍名称、价格和出版社名称。
mysql> select book.name,book.price,press.name from book inner join press on book
.press_id = press.id order by price desc;
+------------+-------+------------+
| name       | price | name       |
+------------+-------+------------+
| 太极拳     | 80.00 | 武当出版社 |
| 易筋经     | 80.00 | 少林出版社 |
| 九阳神功   | 70.00 | 少林出版社 |
| 独孤九剑   | 60.00 | 华山出版社 |
| 乾坤大挪移 | 50.00 | 明教出版社 |
| 九阴真经   | 40.00 | 少林出版社 |
| 葵花宝典   | 40.00 | 明教出版社 |
| 华山剑法   | 30.00 | 华山出版社 |
| 天罡北斗阵 | 30.00 | 武当出版社 |
+------------+-------+-----------

四、子查询

#1:子查询是将一个查询语句嵌套在另一个查询语句中。
#2:内层查询语句的查询结果,可以为外层查询语句提供查询条件。
#3:子查询中可以包含:INNOT INANYALLEXISTSNOT EXISTS等关键字
#4:还可以包含比较运算符:=!=><

  1、带in关键字的子查询

# 1、查询书籍平均价格在45元以上的出版社的id和名称
# 子查询
select id,name from press
    where id in
    (select press_id from book group by press_id having avg(price) > 45);

# 连表查询
select press.id,press.name from press inner join book on press.id = book.press_id group by book.press_id having avg(book.price) > 45;


# 2、查看少林出版社的所有书籍名称
# 子查询
select name from book 
    where press_id in
    (select id from press where name = "少林出版社");

# 连表查询
select book.name from book inner join press on book.press_id = press.id where press.name = '少林出版社';


# 3、查询出版书籍数大于2的出版社名称
# 子查询
select name from press
    where id in
    (select press_id from book group by press_id having count(press_id) > 2);

# 连表查询
select press.name from press inner join book on press.id = book.press_id group by book.press_id having count(book.id) > 2;

  2、带比较运算符的子查询

# 比较运算符:=!=>>=<<=
# 查询大于所有书籍平均价格的书名与单价
select name,price from book
    where price >
    (select avg(price) from book);


# 查询大于出版社里平均价格的书名与单价
select name,price from book t1
    inner join 
    (select press_id,avg(price) avg_price from book group by press_id) t2
    on t1.press_id = t2.press_id
    where t1.price > t2.avg_price;

  3、带exists关键字的子查询

# EXISTS关字键字表示存在。在使用EXISTS关键字时,内层查询语句不返回查询的记录。
# 而是返回一个真假值。True或False
# 当返回True时,外层查询语句将进行查询;当返回值为False时,外层查询语句不进行查询

# press表中存在name="少林出版社",Ture
mysql> select * from book
    ->  where exists
    ->  (select * from press where name="少林出版社");
+----+------------+----------+-------+
| id | name       | press_id | price |
+----+------------+----------+-------+
|  1 | 独孤九剑   |        1 | 60.00 |
|  2 | 华山剑法   |        1 | 30.00 |
|  3 | 九阳神功   |        2 | 70.00 |
|  4 | 九阴真经   |        2 | 40.00 |
|  5 | 易筋经     |        2 | 80.00 |
|  6 | 葵花宝典   |        3 | 40.00 |
|  7 | 乾坤大挪移 |        3 | 50.00 |
|  8 | 太极拳     |        4 | 80.00 |
|  9 | 天罡北斗阵 |        4 | 30.00 |
| 10 | 吸星大法   |     NULL | 40.00 |
+----+------------+----------+-------+

# press表中存在name="峨眉出版社",False
mysql> select * from book
    ->  where exists
    ->  (select * from press where name="峨眉出版社");
Empty set (0.00 sec)

五、综合练习(作业)

  1、准备数据

    表结构:

# 1、创建表
# 创建班级表
create table class(
cid int primary key auto_increment,
caption varchar(32) not null
);

# 创建学生表
create table student(
sid int primary key auto_increment,
gender char(1) not null,
class_id int not null,
sname varchar(32) not null,
foreign key(class_id) references class(cid) on delete cascade on update cascade
);

# 创建老师表
create table teacher(
tid int primary key auto_increment,
tname varchar(32) not null
);

# 创建课程表
create table course(
cid int primary key auto_increment,
cname varchar(32) not null,
teacher_id int not null,
foreign key(teacher_id) references teacher(tid) on delete cascade on update cascade
);

# 创建成绩表
create table score(
sid int primary key auto_increment,
student_id int not null,
course_id int not null,
num int not null,
foreign key(student_id) references student(sid) on delete cascade on update cascade,
foreign key(course_id) references course(cid) on delete cascade on update cascade
);


# 2、插入记录
# 班级表插入记录
insert into class values
('1', '三年二班'), 
('2', '三年三班'), 
('3', '一年二班'), 
('4', '二年一班');

# 学生表插入记录
insert into student values
('1', '', '1', '理解'), 
('2', '', '1', '钢蛋'), 
('3', '', '1', '张三'), 
('4', '', '1', '张一'), 
('5', '', '1', '张二'), 
('6', '', '1', '张四'), 
('7', '', '2', '铁锤'),
('8', '', '2', '李三'), 
('9', '', '2', '李一'), 
('10', '', '2', '李二'), 
('11', '', '2', '李四'), 
('12', '', '3', '如花'), 
('13', '', '3', '刘三'), 
('14', '', '3', '刘一'), 
('15', '', '3', '刘二'), 
('16', '', '3', '刘四');

# 老师表插入记录
insert into teacher values
('1', '张磊'), 
('2', '李平'), 
('3', '刘海燕'), 
('4', '朱云海'), 
('5', '李春秋');

# 课程表插入记录
insert into course values
('1', '生物', '1'), 
('2', '物理', '2'), 
('3', '体育', '3'), 
('4', '美术', '2');

# 成绩表插入记录
insert into score values
('1', '1', '1', '10'), 
('2', '1', '2', '9'), 
('3', '1', '3', '76'),
('5', '1', '4', '66'), 
('6', '2', '1', '8'), 
('8', '2', '3', '68'), 
('9', '2', '4', '99'), 
('10', '3', '1', '77'), 
('11', '3', '2', '66'), 
('12', '3', '3', '87'), 
('13', '3', '4', '99'), 
('14', '4', '1', '79'), 
('15', '4', '2', '11'), 
('16', '4', '3', '67'), 
('17', '4', '4', '100'), 
('18', '5', '1', '79'), 
('19', '5', '2', '11'), 
('20', '5', '3', '67'), 
('21', '5', '4', '100'), 
('22', '6', '1', '9'), 
('23', '6', '2', '100'), 
('24', '6', '3', '67'), 
('25', '6', '4', '100'), 
('26', '7', '1', '9'), 
('27', '7', '2', '100'), 
('28', '7', '3', '67'), 
('29', '7', '4', '88'), 
('30', '8', '1', '9'), 
('31', '8', '2', '100'), 
('32', '8', '3', '67'),
('33', '8', '4', '88'), 
('34', '9', '1', '91'), 
('35', '9', '2', '88'), 
('36', '9', '3', '67'), 
('37', '9', '4', '22'), 
('38', '10', '1', '90'), 
('39', '10', '2', '77'), 
('40', '10', '3', '43'), 
('41', '10', '4', '87'), 
('42', '11', '1', '90'), 
('43', '11', '2', '77'), 
('44', '11', '3', '43'), 
('45', '11', '4', '87'), 
('46', '12', '1', '90'), 
('47', '12', '2', '77'), 
('48', '12', '3', '43'), 
('49', '12', '4', '87'), 
('52', '13', '3', '87');

  2、练习题目

1、查询所有的课程的名称以及对应的任课老师姓名

2、查询学生表中男女生各有多少人

3、查询物理成绩等于100的学生的姓名

4、查询平均成绩大于八十分的同学的姓名和平均成绩

5、查询所有学生的学号,姓名,选课数,总成绩

6、 查询姓李老师的个数

7、 查询没有报李平老师课的学生姓名

8、 查询物理课程的分数比生物课程的分数高的学生的学号

9、 查询没有同时选修物理课程和体育课程的学生姓名

10、查询挂科超过两门(包括两门)的学生姓名和班级

11、查询选修了所有课程的学生姓名

12、查询李平老师教的课程的所有成绩记录

13、查询全部学生都选修了的课程号和课程名

14、查询每门课程被选修的次数

15、查询只选修了一门课程的学生学号和姓名

16、查询所有学生考出的成绩并按从高到低排序(成绩去重)

17、查询平均成绩大于85的学生姓名和平均成绩

18、查询生物成绩不及格的学生姓名和对应生物分数

19、查询在所有选修了李平老师课程的学生中,这些课程(李平老师的课程,不是所有课程)平均成绩最高的学生姓名

20查询每门课程成绩最好的课程id、学生姓名和分数

21、查询不同课程但成绩相同的课程号、学生号、成绩 

22、查询没学过“李平”老师课程的学生姓名以及选修的课程名称

23、查询所有选修了学号为2的同学选修过的一门或者多门课程的同学学号和姓名

24、任课最多的老师中学生单科成绩最高的课程id、学生姓名和分数

六、pymysql模块

  1、介绍

    之前我们都是通过MySQL自带的命令行客户端工具mysql来操作数据库,那如何在python程序中操作数据库呢?这就需要用到了pymysql模块,该模块本质就是一个套接字客户端软件,使用前需要事先安装一下。

pip3 install pymysql

  2、基本使用

import pymysql

user = input("user>: ").strip()
pwd = input("password>: ").strip()

# 建立连接
conn = pymysql.connect(
    host='127.0.0.1',  # 如果是远程的数据库,输入远程IP 
    port=3306,
    user='root',
    password='root123',
    db='db1',
    charset='utf8'
)

# 注意:如果连接远程报1130错误,是远程连接权限问题引起的。
# 解决方法:
  # 允许任何IP地址(下面的 % 就是任何的意思)的电脑使用root用户和密码来访问这个MySQL Server 。
  # grant all on *.* to 'root'@'%' identified by 'root123';
  # 及时生效需执行
  # flush privileges;
  
# 拿到游标
cursor = conn.cursor()

# 执行SQL语句
sql = 'select * from userinfo where username = "%s" and password = "%s";' % (user, pwd)
rows = cursor.execute(sql)

cursor.close()  # 关闭游标
conn.close()  # 关闭连接

# 进行判断
if rows:
    print("登录成功!")
else:
    print("登录失败!")

  3、SQL注入

    这是数据库的userinfo表的数据。

    如图输入:用户名" -- xxxx,不用输入密码也能登录成功。

    有没有绕过用户名和密码的方法,也能SQL注入成功呢?答案是有,请看下图。

    为了解决上面的问题,请参考如下代码。

import pymysql

user = input("user>: ").strip()
pwd = input("password>: ").strip()

# 建立连接
conn = pymysql.connect(
    host='127.0.0.1',  # 如果是远程的数据库,输入远程IP 
    port=3306,
    user='root',
    password='root123',
    db='db1',
    charset='utf8'
)
  
# 拿到游标
cursor = conn.cursor()

# 执行SQL语句
# 去掉里面%s的双引号,不用自己字符串拼接。
sql = 'select * from userinfo where username = %s and password = %s;'
# pymysql模块自动帮我们解决sql注入的问题,只要我们将参数传入execute方法里,如下所示。
rows = cursor.execute(sql, (user, pwd))

cursor.close()  # 关闭游标
conn.close()  # 关闭连接

# 进行判断
if rows:
    print("登录成功!")
else:
    print("登录失败!")

  4、增、删、改(conn.commit())

import pymysql

# 建立连接
conn = pymysql.connect(
    host='127.0.0.1',  # 如果是远程的数据库,输入远程IP 
    port=3306,
    user='root',
    password='root123',
    db='db1',
    charset='utf8'
)
  
# 拿到游标
cursor = conn.cursor
# 执行SQL语句
# 增
# sql = 'insert into userinfo(username, password) values(%s, %s)'

# 删
# sql = 'delete from userinfo where username = %s and password = %s'

# 改
sql = 'update userinfo set password = %s where username = %s'

# 操作一条记录,用execute方法
# rows = cursor.execute(sql, (456, "alex"))

# 操作多条记录,用executemany方法
rows = cursor.executemany(sql, [(222, "egon"), (333, "michael")])

print(rows)  # 被影响的行数

conn.commit()  # 注意一定要commit,提交同步到数据库。

cursor.close()  # 关闭游标
conn.close()  # 关闭连接

  5、查(fetchone、fetchmany、fetchall)

import pymysql

# 建立连接
conn = pymysql.connect(
    host='127.0.0.1',  # 如果是远程的数据库,输入远程IP 
    port=3306,
    user='root',
    password='root123',
    db='db1',
    charset='utf8'
)

# 拿到游标
# 这样取默认是元组的形式。如果字段比较多,不方便取值,我们可以通过设置游标,以字典的形式取值
# cursor = conn.cursor()
cursor = conn.cursor(pymysql.cursors.DictCursor)

# 执行SQL语句
sql = 'select * from userinfo;'
rows = cursor.execute(sql)

print(rows)  # 被影响的行数

# 取一条记录
# cursor.scroll(2, mode='absolute')  # 相对绝对位置移动
print(cursor.fetchone())  # 取出一条记录,默认是从第一条取。上面设置后,这条记录id应为3

# cursor.scroll(1, mode='relative')  # 相对当前位置移动,往后移动一条。
print(cursor.fetchone())  # 取出一条记录,上面设置后,这条记录id应为5
# print(cursor.fetchone())  # 超出范围不会报错,会显示None

# 取多条记录
# print(cursor.fetchmany(2))

# 取全部记录
# print(cursor.fetchall())

cursor.close()  # 关闭游标
conn.close()  # 关闭连接

  6、新增时获取最后一条数据的自增ID

import pymysql

# 建立连接
conn = pymysql.connect(
    host='127.0.0.1',  # 如果是远程的数据库,输入远程IP 
    port=3306,
    user='root',
    password='root123',
    db='db1',
    charset='utf8'
)

# 拿到游标
cursor = conn.cursor()

# 执行SQL语句
# 增
sql = 'insert into userinfo(username, password) values(%s, %s)'

# 操作一条记录,用execute方法
rows = cursor.execute(sql, ("bobo", 123))

print(cursor.lastrowid)  # 在插入语句后查看,当前插入的第一条自增id号

print(rows)  # 被影响的行数

conn.commit()  # 注意一定要commit,提交同步到数据库。

cursor.close()  # 关闭游标
conn.close()  # 关闭连接

 

 

posted @ 2019-04-30 11:32  Michael--chen  阅读(1009)  评论(0编辑  收藏  举报