MySQL-注释-Navicat基本使用-复杂查询练习题-解题思路-pymysql操作数据库-SQL注入-05

目录

mysql语句注释

利用注释备注信息(注释用的6 也可以造成SQL注入

-- 这是行注释,这一行从这里开始的SQL语句都不会被执行

# 这也是行注释,这一行从这里开始的SQL语句都不会被执行

/*
	这是块注释,写在里面的SQL语句都不会被执行
*/

navicat 的基本使用

我们使用 navicat 这个数据库图形化界面管理软件来简化数据库操作,提高开发效率

特色(个人总结)

就现阶段用到而言

  • 把许多数据库基本操作语句用 鼠标点点点 的方式完成,并且可以看到操作对应的 sql 语句
  • 语法提示
  • 关键字颜色标识,不容易打错字单词
  • 模型功能(设计、分析数据关系十分便利)
  • 导入导出功能十分强大
  • 支持多种数据库服务器(可以保存连接,点击登录)

navicat 官方使用手册 写的还是挺详细的,图文结合,想要详细了解可以戳过去看看

此部分仅作为个人 navicat 使用快速查阅区

有些部分比较简单,就只写了一个标题,表示必须步骤

可以支持很多种数据库,可视化客户端界面

与数据服务器建立连接

创建&打开数据库、表

创建

选中上一级对象右键新建...

数据库连接 > 数据库 > 数据表 > 表记录 (忽略其他对象)

打开

直接双击数据库、表即可(像windows操作文件目录一样)

修改操作表结构

修改表结构

查询修改操作表数据

基本语句对应的操作

忘了这里是打算写什么了,先空着吧...

模型 ***** 特色功能

从数据库建立模型

模型页面基本操作

常用功能个人小结

  • 鼠标放在表上可以到与之关联的表
  • 鼠标放在线上可关联关系(外键)
  • 双击表可以改表结构
  • 右键重命名可以改表名
  • 右下角有历史记录区,可回退
  • 如图标注移动与整理格式视图功能
  • 与库表结构之间的相互转换

用模型设计数据库并导出

打开模型视图

在工作区创建表

创建表字段关联(外键)

将模型导出

可以导出数据库,也可导出表

结构、数据导入导出

导出

注意对象(库、表)

导入

一般导出的sql 文件中都有drop ... if exisists .... ,用之前慎重

附属小功能

刷新小按钮

点击刷新小图标 1566475669734 或者选中对应对象右键刷新

查看操作对应sql语句

很多地方都有这个SQL预览

执行时间查看

可以对自己的 sql 语句性能有个粗略评估

手动筛选数据

练习

习题引自 多表查询练习题

数据准备

create database day38;

create table test(
	id int primary key auto_increment,
    name varchar(32),
    sex enum('male', 'female', 'others') default 'male' not null
);

insert into test values(''), ('');

使用SQL语句导入表结构与数据

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `class`
-- ----------------------------
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
  `cid` int(11) NOT NULL AUTO_INCREMENT,
  `caption` varchar(32) NOT NULL,
  PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `class`
-- ----------------------------
BEGIN;
INSERT INTO `class` VALUES ('1', '三年二班'), ('2', '三年三班'), ('3', '一年二班'), ('4', '二年九班');
COMMIT;

-- ----------------------------
--  Table structure for `course`
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
  `cid` int(11) NOT NULL AUTO_INCREMENT,
  `cname` varchar(32) NOT NULL,
  `teacher_id` int(11) NOT NULL,
  PRIMARY KEY (`cid`),
  KEY `fk_course_teacher` (`teacher_id`),
  CONSTRAINT `fk_course_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `course`
-- ----------------------------
BEGIN;
INSERT INTO `course` VALUES ('1', '生物', '1'), ('2', '物理', '2'), ('3', '体育', '3'), ('4', '美术', '2');
COMMIT;

-- ----------------------------
--  Table structure for `score`
-- ----------------------------
DROP TABLE IF EXISTS `score`;
CREATE TABLE `score` (
  `sid` int(11) NOT NULL AUTO_INCREMENT,
  `student_id` int(11) NOT NULL,
  `course_id` int(11) NOT NULL,
  `num` int(11) NOT NULL,
  PRIMARY KEY (`sid`),
  KEY `fk_score_student` (`student_id`),
  KEY `fk_score_course` (`course_id`),
  CONSTRAINT `fk_score_course` FOREIGN KEY (`course_id`) REFERENCES `course` (`cid`),
  CONSTRAINT `fk_score_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `score`
-- ----------------------------
BEGIN;
INSERT INTO `score` VALUES ('1', '1', '1', '10'), ('2', '1', '2', '9'), ('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');
COMMIT;

-- ----------------------------
--  Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `sid` int(11) NOT NULL AUTO_INCREMENT,
  `gender` char(1) NOT NULL,
  `class_id` int(11) NOT NULL,
  `sname` varchar(32) NOT NULL,
  PRIMARY KEY (`sid`),
  KEY `fk_class` (`class_id`),
  CONSTRAINT `fk_class` FOREIGN KEY (`class_id`) REFERENCES `class` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `student`
-- ----------------------------
BEGIN;
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', '刘四');
COMMIT;

-- ----------------------------
--  Table structure for `teacher`
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
  `tid` int(11) NOT NULL AUTO_INCREMENT,
  `tname` varchar(32) NOT NULL,
  PRIMARY KEY (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `teacher`
-- ----------------------------
BEGIN;
INSERT INTO `teacher` VALUES ('1', '张磊老师'), ('2', '李平老师'), ('3', '刘海燕老师'), ('4', '朱云海老师'), ('5', '李杰老师');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

如何验证答案是否正确

语句写出来不一定是对的,此时需要举一两个例子去验证

题目

选几道做做掌握方法即可

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

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

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

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

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

# 6、 查询姓李老师的个数

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

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

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

# 10、查询挂科超过两门(包括两门)的学生姓名和班级、查询选修了所有课程的学生姓名

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

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

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

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

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

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

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

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

# 20、查询每门课程成绩最好的前两名学生姓名

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

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

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

# 24、任课最多的老师中学生单科成绩最高的学生姓名

部分参考答案(只放了两题的)

# 1、查询所有的课程的名称以及对应的任课老师姓名
SELECT course.cname, teacher.tname FROM course INNER JOIN teacher ON course.teacher_id=teacher.tid;

# 2、查询学生表中男女生各有多少人  # 利用group分组与聚合函数 count计数
-- SELECT * FROM student;
SELECT gender, COUNT(sid) FROM student GROUP BY gender;

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

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

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

# 6、 查询姓李老师的个数

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

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

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

# 10、查询挂科超过两门(包括两门)的学生姓名和班级、查询选修了所有课程的学生姓名

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

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

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

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

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

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

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

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

# 20、查询每门课程成绩最好的前两名学生姓名

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

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

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

# 24、任课最多的老师中学生单科成绩最高的学生姓名

少数题推导歩鄹(记录思路)

-- 1、查询所有的课程的名称以及对应的任课老师姓名
# 所有课程信息
-- SELECT * FROM course;
# 老师表的信息
-- SELECT * FROM teacher;
# 两张表是一一对应的关系,直接根据 老师id 用内连接连起来即可
# 与老师信息对应起来
SELECT course.cname, teacher.tname FROM course INNER JOIN teacher ON course.teacher_id=teacher.tid;


# 3、查询物理成绩等于100的学生的姓名
# 审题: 涉及到模型中的 课程表、成绩表、学生表
# 先看看课程表
SELECT * FROM course;  # 有 cid 跟成绩表关联
# 看看成绩表
SELECT * FROM score;  # 有学生 id 和 分数
# 看看学生表
SELECT * FROM student;  # 有学生名还有 学生 id

# 先查出课程表中物理课的 cid
SELECT course.cid FROM course WHERE cname='物理';
# 将上一步的查询结果cid 与成绩表 course_id关联
SELECT * FROM score WHERE score.course_id=(SELECT course.cid FROM course WHERE cname='物理');
# 物理成绩等于100 分的学生 sid  ---> sid 和 student_id 引起歧义了。。。。 千万不要搞错了
SELECT score.student_id FROM score WHERE num = 100 and score.course_id=(SELECT course.cid FROM course WHERE cname='物理');
# 用上一步的结果 sid 与学生表 sid 关联,查出学生姓名
SELECT * FROM student WHERE student.sid in (SELECT score.student_id FROM score WHERE num = 100 and score.course_id=(SELECT course.cid FROM course WHERE cname='物理'));


-- 4、查询平均成绩大于八十分的同学的姓名和平均成绩
-- 先查平均成绩大于80分的
-- 有错
-- SELECT student.sname, tmp.avg_num FROM student
-- LEFT JOIN
-- (SELECT score.student_id, AVG(score.num) as avg_num FROM score GROUP BY score.student_id HAVING AVG(score.num) > 80) AS tmp
-- ON
-- student.sid=tmp.student_id;

-- 7、 查询没有报李平老师课的学生姓名
-- SELECT * FROM teacher LEFT JOIN course ON teacher.tid=course.teacher_id and teacher.tname='李平老师';

-- SELECT teacher.tid FROM teacher WHERE teacher.tname='李平老师';
-- SELECT * FROM course WHERE course.teacher_id=(SELECT teacher.tid FROM teacher WHERE teacher.tname='李平老师')
-- SELECT course.cid FROM course WHERE course.teacher_id=(SELECT teacher.tid FROM teacher WHERE teacher.tname='李平老师')

-- 8、 查询没有同时选修物理课程和体育课程的学生姓名(规定必须选了)
-- SELECT course.cid FROM course WHERE course.cname='物理' or course.cname='体育';  # 2, 3
-- SELECT * FROM score WHERE score.course_id IN (
-- SELECT course.cid FROM course WHERE course.cname='物理' or course.cname='体育'
-- );
# 只有一门的学生
-- SELECT score.student_id FROM score WHERE score.course_id IN (
-- SELECT course.cid FROM course WHERE course.cname='物理' or course.cname='体育'
-- ) GROUP BY score.student_id HAVING COUNT(score.student_id)=1;
-- SELECT student.sname FROM student WHERE student.sid IN (
-- SELECT score.student_id FROM score WHERE score.course_id IN (
-- SELECT course.cid FROM course WHERE course.cname='物理' or course.cname='体育'
-- ) GROUP BY score.student_id HAVING COUNT(score.student_id)=1
-- );
-- # 可以把 course.cname='物理' or course.cname='体育' 改成 in ('体育', '物理')


-- 9、 查询挂科超过两门(包括两门)的学生姓名和班级
-- SELECT * FROM score WHERE score.num < 60;
-- SELECT score.student_id FROM score WHERE score.num < 60
-- GROUP BY score.student_id
-- HAVING COUNT(score.student_id) >= 2
-- ;
-- SELECT * FROM student WHERE student.sid IN (
-- SELECT score.student_id FROM score WHERE score.num < 60
-- GROUP BY score.student_id
-- HAVING COUNT(score.student_id) >= 2)
-- ;
SELECT class.caption, student.sname FROM student
INNER JOIN class ON class.cid=student.class_id
WHERE student.sid IN (
SELECT score.student_id FROM score WHERE score.num < 60
GROUP BY score.student_id
HAVING COUNT(score.student_id) >= 2)
;

亦可处理好缩进 选择部分代码执行 推导每步执行结果

select student.sname from student where sid not  in(
	select distinct student_id from score where course_id in(
		select course.cid from course inner join teacher on course.teacher_id = teacher.tid where tid = (
			select tid from teacher where tname = '李平老师'
		)
	)
)
;

核心点--解题思路与注意点、小结

个人解题思路与技巧

  • 一定要分步一点点写,写一点查一点

    ​ 虽然慢,但也比写不出来强不要太多呀!

  • 一定要保留上一步的代码,方便回看或者保持好缩进,可以每次选择前几步的结果来查询

    ​ 后面写不下去了,或者哪里突然写错了,方便排查错误

  • 根据哪张表字段关联什么,可以得到什么 来当成查询返回目标字段(select 后面)

    ​ 每一步查出来的结果都是下一步所需的字段,score表有学生id 是我查老师所教学生的所要的条件

    select score.student_id from score where score.course_id in (
        select course.id from course where course.teacher_id in (
            select teacher.id from teacher where teacher.name in ('李平老师', 'tank老师')
            )
        )
    )
    ;
    
    # 采用这样的缩进可以选中不同部分去执行,可以看到查询结果(分号在最后一行,只管套就行了)
    
  • 表的字段都用表名来点(teacher.tid student.name 这样) 后续歩鄹多了很可能会和其他表有冲突,在找问题时也不够清晰,你还要去分析这个id是哪里的id

    ​ 带上表名一眼就知道了

  • 分号直接放在最后一行

    ​ 嵌套逐步推导式不用关心引号了,不然还删了,再在最外层加

小结

  • 两个表合起来一定是 inner join(left join 或者 right join这些一般用不到)

  • 子查询一般拿来做条件且只返回一个字段, id in (select student.id ...)

  • 数据从虚拟表里拿 --> 外层表拿里层表(括号里执行产生虚拟表)里的数据

    ​ 从括号里查询结果中拿取数据

某题详细分析思路(没有写完)

题目

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

分析流程、技巧/注意点

用面向过程的思路来分步解决

看题分析用到哪些表

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

涉及到 课程表、成绩表、学生表

在模型中查看表关联

可以开着navicat的模型关系图,筛选出与题目相关联的表,拉出来,看清楚各个表之间的关联字段,要得到什么字段需要经过哪些表,如何得出。

查看表结构与内容,标注每一步的关联字段

看看课程表 SELECT * FROM course; # 有 cid 跟成绩表关联

# 看看成绩表 SELECT * FROM score; # 有学生 sid 和 分数

# 看看学生表 SELECT * FROM student; # 有学生名还有 学生 id

分步去推导出结果

找一个起点,一点一点的查出想要的数据

.... 后续空闲了再来补充

人工分析验证语句执行结果是否正确

其实校验每一步执行的结果递推也可以确定答案是否正确

从最开始的语句一句一句往下面执行推导结果

分块语句一小块一小块往外面执行

推荐还是从最初那步开始去校对结果

一步一步复杂的去验证,全部都没问题,那就没问题(或者考虑没有的情况,选课,没选课的呢?)

题目:查询物理成绩等于100的学生的姓名

按思路人工查表

如果记录不存在,可能此人工查法结果是错的,如果中间有联表,可能也是错的 --> 应该是没问题的,后面验证下(最近太忙了, emmm)

查出课程表中物理课的 cid

根据上一步查出的 cid 去成绩表查满足条件的 student_id

筛选分数

根据上一步获取到的 student_id 去student表查 sname ---> 即最终结果

将最终结果与语句执行结果相比较

如果有误,校对SQL语句或人工查询是否有误

通过 python 进行 MySQL 操作

先安装 pymysql 模块

打开设置

打开搜索页面

搜索并安装pymysql模块

安装完毕即可

参考数据

teacher表

查询案例

点进去看源码及参数,返回值,可以知道他的用法以及参数,返回值,其他功能

读数据时注意游标的移动,类似于文件的读取等操作

普通查询操作

前提:要事先创建好数据库

conn连接对象.cursor() cursor游标对象.execute() cursor游标对象.fetchone() cursor游标对象.fetchmany() cursor游标对象.fetchall() cursor游标对象.scroll() cursor游标对象.close() conn连接对象.close()

import pymysql

conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='000000',
    database='day38',
    charset='utf8'  # 编码千万不要加 -   --> utf-8 会直接报错
)

# cursor = conn.cursor()  # 产生一个游标对象(闪烁的输入 _)
# sql = 'select * from teacher'
# res = cursor.execute(sql)  # 执行传入的sql 语句
# # 5
# print(res)  # res其实是执行语句返回的数据条数
# print(cursor.fetchone())
# # (1, '张磊老师')
# print(cursor.fetchall())  # 下面从第二条开始了。。。 取了就没了(队列,文件光标移动)
# # ((2, '李平老师'), (3, '刘海燕老师'), (4, '朱云海老师'), (5, '李杰老师'))
# print(cursor.fetchmany())
# # ()


# ---------------------------------------------------
# conn.cursor() 产生一个游标对象
#   不写参数    默认将每条数据以元组返回
#   pymysql.cursors.DictCursor  以字典的形式返回每条记录
# ---------------------------------------------------
cursor = conn.cursor(pymysql.cursors.DictCursor)  # 产生一个游标对象(闪烁的输入 _) 以字典的形式返回查询出来的数据, 键是表的字段,值是...
sql = 'select * from teacher'

# ---------------------------------------------------
# cursor.execute() 拼接执行sql语句
# ---------------------------------------------------
res = cursor.execute(sql)  # 执行传入的sql 语句
# 5
print(res)  # res其实是执行语句返回的数据条数


# ---------------------------------------------------
# cursor.fetchone() 从当前游标位置开始查询一个记录
#   游标会自动后移一位
# ---------------------------------------------------
print(cursor.fetchone())
# {'tid': 1, 'tname': '张磊老师'}


# ---------------------------------------------------
# cursor.fetchmany() 从当前游标位置开始往后读取n条数据
#   游标会自动后移n行
# ---------------------------------------------------
print(cursor.fetchmany(2))
# [{'tid': 2, 'tname': '李平老师'}, {'tid': 3, 'tname': '刘海燕老师'}]

# ---------------------------------------------------
# cursor.fetchall() 从当前游标位置开始读取全部数据
#   游标会自动后移至末尾
# ---------------------------------------------------
print(cursor.fetchall())  # 下面从第二条开始了。。。 取了就没了(队列,文件光标移动)
# [{'tid': 4, 'tname': '朱云海老师'}, {'tid': 5, 'tname': '李杰老师'}]


# ---------------------------------------------------
# cursor.scroll() 移动游标
#   absolute 直接移到第n 行
#   relative 根据当前位置将游标往后移动n 行
# ---------------------------------------------------
cursor.scroll(0, 'absolute')  # 数字 absolute 相对于起始位置往后移动几位
print(cursor.fetchall())
# [{'tid': 1, 'tname': '张磊老师'}, {'tid': 2, 'tname': '李平老师'}, {'tid': 3, 'tname': '刘海燕老师'}, {'tid': 4, 'tname': '朱云海老师'}, {'tid': 5, 'tname': '李杰老师'}]

cursor.scroll(0, 'absolute')  # 移至最开头
cursor.scroll(1, 'relative')  # 数字 relative 相对当前位置往后移动几位
print(cursor.fetchall())
# [{'tid': 2, 'tname': '李平老师'}, {'tid': 3, 'tname': '刘海燕老师'}, {'tid': 4, 'tname': '朱云海老师'}, {'tid': 5, 'tname': '李杰老师'}]

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

前置数据

CREATE TABLE `user`  (
  `id` int(11) PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(255),
  `password` varchar(255)
) ;

insert into user (name, password) values ('jason', '123'), ('tank', '123'), ('jerry', '123');

SQL注入

SQL注入:利用注释等具有特殊意义的符号,来完成一些骚操作

# 利用字符串拼接的特点, 传入了注释,把密码校验屏蔽掉了
# 	即知道用户名就能登录了
select * from user where name = 'jason' -- safgwe and password = 'sanadawgw';

# 利用字符串拼接的特点,跳过用户名与密码校验
select * from user where name = 'sagas' or 1 = 1 -- and password = '123';

select * from user where name = 'jason' -- safgwe and password = 'sanadawgw';

select * from user where name = 'sagas' or 1 = 1 -- and password = '123';

在python中可以print 一下自己的sql 语句,拿出来执行一下或者放在 navicat 中(有颜色的区别)即可看出端倪

避免SQL注入

后续写sql 语句,不要手动拼接关键性的数据,而是用 execute 帮你去拼接

方法的使用可以养成习惯,点进去看看,参数返回值都有,而且还会有简单的英文注释

cursor游标对象.mogrify() cursor游标对象.execute()

import pymysql

conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='000000',
    database='day38',
    charset='utf8'  # 编码千万不要加 -   --> utf-8 会直接报错
)

cursor = conn.cursor()


username = input("username>>>: ").strip()
password = input("password>>>: ").strip()
# SQL 注入
# sql = f"select * from user where name = '{username}' and password = '{password}'"
# print(sql)
# res = cursor.execute(sql)
# if res:
#     print(cursor.fetchall())
# else:
#     print("用户名或密码错误")
# # username>>>: jason
# # password>>>: 123
# # select * from user where name = 'jason' and password = '123'
# # ((1, 'jason', '123'),)

# # username>>>: jason' -- sadagas
# # password>>>:
# # 也可以进去
#
# # username>>>: xxx' or 1=1 --sasgsafg
# # password>>>:
# # 也可以进去


# 解决版
sql = "select * from user where name = %s and password = %s"
# print(sql)

# ---------------------------------------------------
# cursor.mogrify() 拼接sql语句
#   可以在 execute前将语句打印出来,便于排错
# ---------------------------------------------------
print("通过 cursor.mogrify 查看 execute 要执行的语句(execute的中间步骤):" + cursor.mogrify(sql, [username, password]))


# execute 可以自动解决 Bug, 自动识别并拼接语句中的  %s
# ---------------------------------------------------
# cursor.execute() 执行SQL语句,可拼接防注入
#   传入sql语句自动执行,也可识别 %s 用后面参数替换
# ---------------------------------------------------
res = cursor.execute(sql, [username, password])

if res:
    print(cursor.fetchone())
else:
    print("用户名或密码错误")
# username>>>: tanksadas
# password>>>: sagas
# 通过 cursor.mogrify 查看 execute 要执行的语句(execute的中间步骤):select * from user where name = 'tanksadas' and password = 'sagas'
# 用户名或密码错误

cursor.close()
conn.close()

其他操作(增改删)

sql语句还是自己写,写好占位符,通过 execute 来拼接

conn连接对象.commit() 提交增删改任务

增删改操作的会涉及到数据安全,所以需要写上 conn.commit() 来“确认”提交

亦或是实例化连接时指定参数 autocommit=True

import pymysql

conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='000000',
    database='day38',
    charset='utf8',  # 编码千万不要加 -   --> utf-8 会直接报错
    autocommit=True,  # 这个参数配置完后,增删改操作都不需要手动加 conn.commit 了
)

cursor = conn.cursor()

# # sql = "insert into user values(name, password) values('jerry', '666')"
# sql = "update user set name='jason' where id = 1"
#
# res = cursor.execute(sql)
# conn.commit()  # 增删改必须有这一句才能完成   pymysql.connect(.....) 里写了这就不用写了

sql = "insert into user (name, password) values (%s, %s)"
# ---------------------------------------------------
# cursor.executemany() 自动拼接sql 语句并执行
#   自动识别 %s 替换成后面的参数,插入多个数据
# ---------------------------------------------------
res = cursor.executemany(sql, [('jerry', '123'), ('tank', '123'), ('jason', '123')])

# ---------------------------------------------------
# conn.commit() 提交 新增、修改、删除操作 必须写上才有效
# ---------------------------------------------------
conn.commit()

cursor.close()
conn.close()

防注入的登录功能

import pymysql

conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='000000',
    database='day38',
    charset='utf8'  # 编码千万不要加 -   --> utf-8 会直接报错
)

cursor = conn.cursor(pymysql.cursors.DictCursor)


username = input("username>>>: ").strip()
password = input("password>>>: ").strip

sql = "select * from user where name = %s and password = %s"
# print(sql)

res = cursor.execute(sql, [username, password])

if res:
    print(cursor.fetchone())
else:
    print("用户名或密码错误")

cursor.close()
conn.close()

扩展点 utf8mb4

utf8mb4 这个字符编码可以存表情,后期微信小程序可能会弄到

posted @ 2019-08-25 18:49  suwanbin  阅读(796)  评论(0编辑  收藏  举报