MySQL数据库

mysql服务端安装

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
------------- mysql服务端安装:-----------------
    sudo apt-get install mysql-server
查看apt是否安装MySQL服务端:
    apt-cache show mysql-server
查看MySQL服务端是否在运行:
    ps -aux | grep mysql
    * ps 查看当前系统中的进程
    * -a 表示所有用户
    * -u 表示显示用户名
    * -x 表示显示所有的执行程序
查看MySQL服务状态:
    sudo service mysql status
停止MySQL服务:
    sudo service mysql stop
启动MySQL服务:
    sudo service mysql start
重启MySQL服务:
    sudo service mysql restart

MySQL配置文件路径: /etc/mysql/mysql.conf.d/mysqld.cnf
    port表示端口号,默认为3306
    bind-address表示服务器绑定的ip,默认为127.0.0.1
    datadir表示数据库保存路径,默认为/var/lib/mysql
    log_error表示错误日志,默认为/var/log/mysql/error.log

------------- mysql客户端安装:-----------------
客户端是程序员或者dba使用的软件,通过socket方式与服务端程序通信
- 命令行 MySQL
    命令行客户端mysql的安装,在Ubuntu中打开终端,输入下面的命令:
    sudo apt-get install mysql-client
    查看是否安装客户端:apt-cache show mysql-client
    MySQL客户端连接服务端:mysql -uroot -p
- navicat:https://www.navicat.com.cn/download/navicat-for-mysql
    tar zxvf navicat112_mysql_cs_x64.tar.gz
    ./start_navicat
# 过期重新试用
    当过期后,删除用户主目录下的.navicat64目录,继续再使用14天。
    cd ~
    rm -r .navicat64

mysql使用帮助:mysql -help

创建桌面快捷方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
chmod +x navicat15-mysql-cs.AppImage
./navicat15-mysql-cs.AppImage

图标可以自定义,网上下载一个即可
主要是Exec执行的文件要对

$ vim /usr/share/applications/navicat15-mysql-cs.desktop
[Desktop Entry]
Version=1.0
Type=Application
Name=navicat15-mysql-cs
Icon=/opt/navicat/navicat_mysql.png
Exec="/opt/navicat/navicat15-mysql-cs" %f
Comment=navicat15-mysql-cs
Categories=mysql;
Terminal=false
StartupWMClass=navicat15-mysql-cs

安装的MySQL没有密码

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
我在Ubuntu 18.10上全新安装了mysql-server,但无法使用默认密码登录。然后只有我知道默认情况下,使用auth_socket对root用户进行身份验证。因此,当答案更改为mysql_native_password时,我们可以使用mysql默认密码
$ sudo cat /etc/mysql/debian.cnf
# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost
user     = debian-sys-maint
password = TMvZ8TVGoVZglsvP
socket   = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
host     = localhost
user     = debian-sys-maint
password = TMvZ8TVGoVZglsvP
socket   = /var/run/mysqld/mysqld.sock

$ mysql -u debian-sys-maint -p
Enter password:
输入来自debian.cnf的密码
mysql> USE mysql
mysql> SELECT User, Host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| User             | Host      | plugin                |
+------------------+-----------+-----------------------+
| debian-sys-maint | localhost | caching_sha2_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | auth_socket           |
+------------------+-----------+-----------------------+
5 rows in set (0.00 sec)

要么:
mysql> UPDATE user SET plugin='mysql_native_password' WHERE User='root';
mysql> COMMIT;

要么:
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';

要么:
//对于MySQL 5.7+
mysql>UPDATE mysql.user SET authentication_string=PASSWORD('new_password') where user='root';

要么:
alter user root@localhost identified with mysql_native_password by '新密码';

有时您需要重新启动mysql服务器。
sudo service mysql restart
要么
sudo systemctl restart mysql 

开启MySQL的远程访问

1
2
3
4
5
6
7
为了能够在远程登录和使用MySQL,需要编辑文件 
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
找到这句 bind-address = 127.0.0.1:,将它注销掉即可
#bind-address = 127.0.0.1:
保存退出,完了之后开启MySQL服务

sudo mysql -uroot -p此处直接加上你的mysql的密码 

ubuntu上navicat无法连接mysql

1
2
3
> 2002 cannot connect to local mysql server through socket /var/lib/mysql/mysql.sock(13权限不够)

主机名改为127.0.0.1就可以连接了,不清楚是什么原因

image-20230927182350650

基本使用

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
登录数据库:mysql -uroot -p

# 登录成功后:
显示当前时间:select now();
登出(退出)数据库:quit 或 exit 或 ctrl + d

# 数据库操作
1. 查看所有数据库:show databases;
2. 创建数据库:create database 数据库名 charset=utf8;
3. 使用数据库:use 数据库名;
4. 查看当前使用的数据库:select database();
5. 删除数据库-慎重:drop database 数据库名;
6. 查看MySQL数据库支持的表的存储引擎:show engines;
7. 修改表的存储引擎使用: alter table 表名 engine = 引擎类型(InnoDB/MyISAM);
    比如: alter table students engine = 'MyISAM';

# 表结构操作
1. 查看当前数据库中所有表
    show tables;
    \G 可以竖着看信息
2. 创建表
create table students(
    id int unsigned primary key auto_increment not null,
    name varchar(20) not null,
    age tinyint unsigned default 0,
    height decimal(5,2),
    gender enum('男','女','人妖','保密')
);
说明:
create table 表名(
    字段名称 数据类型  可选的约束条件,
    column1 datatype contrai,
    ...
);
3. 修改表-添加字段
    alter table 表名 add 列名 类型 约束;
    例:
    alter table students add birthday datetime;
4. 修改表-修改字段类型
    alter table 表名 modify 列名 类型 约束;
    例:
    alter table students modify birthday date not null;
    说明:
        * modify: 只能修改字段类型或者约束,不能修改字段名
5. 修改表-修改字段名和字段类型
    alter table 表名 change 原名 新名 类型及约束;
    例:
    alter table students change birthday birth datetime not null;
    说明:
        * change: 既能对字段重命名又能修改字段类型还能修改约束
    -- 可以同时改多个
    alter table goods change cate_name cate_id int not null, change brand_name brand_id int not null;
6. 修改表-删除字段
    alter table 表名 drop 列名;
    例:
    alter table students drop birthday;
7. 查看创表SQL语句
    show create table 表名;
    例:
    show create table students;
8. 查看创库SQL语句
    show create database 数据库名;
    例:
    show create database mytest;
    -- 查看表结构
    desc goods;
9.删除表
    drop table 表名;
    例:
    drop table students;

# 表数据操作
0. 执行sql文件给areas表导入数据:
    source areas.sql;
1. 查询数据
    -- 1. 查询所有列select * from 表名;
        例:select * from students;
    -- 2. 查询指定列select 列1,列2,... from 表名;
        例:select id,name from students;
2. 添加数据
    -- 1. 全列插入:值的顺序与表结构字段的顺序完全一一对应
        insert into 表名 values (...)
        例:insert into students values(0, 'xx', default, default, '男');
    -- 2. 部分列插入:值的顺序与给出的列顺序对应
        insert into 表名 (列1,...) values(值1,...)
        例:insert into students(name, age) values('王二小', 15);
    -- 3. 全列多行插入
        insert into 表名 values(...),(...)...;
        例:insert into students values(0, '张飞', 55, 1.75, '男'),
        (0, '关羽', 58, 1.85, '男');
    -- 4. 部分列多行插入
        insert into 表名(列1,...) values(值1,...),(值1,...)...;
        例:insert into students(name, height) values('刘备', 1.75),
        ('曹操', 1.6);
    说明:
    * 主键列是自动增长,但是在全列插入时需要占位,通常使用空值(0或者null或者default)
    * 在全列插入时,如果字段列有默认值可以使用 default 来占位,插入后的数据就是之前设置的默认值
3. 修改数据
    update 表名 set1=1,列2=2... where 条件
    例:update students set age = 18, gender = '女' where id = 6;
4. 删除数据
    delete from 表名 where 条件
    例:delete from students where id=5;
    问题:
        上面的操作称之为物理删除,一旦删除就不容易恢复,我们可以使用逻辑删除的方式来解决这个问题。
        -- 添加删除表示字段,0表示未删除 1表示删除
        alter table students add isdelete bit default 0;
        -- 逻辑删除数据
        update students set isdelete = 1 where id = 8;
        说明:
            * 逻辑删除,本质就是修改操作
# 插入查询结果
    insert into .. select .. 表示: 把查询结果插入到指定表中,也就是表复制。
    create table .. select 列名 .. 表示创建表并插入数据
# 关联表更新
    连接更新表中数据使用: update .. join .. 语句
    -- 查看goods表中的商品分类名称对应的商品分类id
    select * from goods inner join good_cates on goods.cate_name = good_cates.name;
    -- 把该语句中from 后的语句理解为一张虚表  
    update goods g inner join good_cates gc on g.cate_name=gc.name set g.cate_name=gc.id;

# 一些关键字用法
* as 关键字可以给表中字段 或者 表名起别名
* distinct 关键字可以去除重复数据行。
* 常见的比较运算符有 >,<,>=,<=,!=
* 逻辑运算符and表示多个条件同时成立则为真,or表示多个条件有一个成立则为真,not表示对条件取反
* like%结合使用表示任意多个任意字符,like和_结合使用表示一个任意字符
* between-and限制连续性范围 in限制非连续性范围
* 判断为空使用: is null
* 判断非空使用: is not null
* 排序使用 order by 关键字
* asc 表示升序
* desc 表示降序
* 使用 limit 关键字可以限制数据显示数量,通过 limit 关键可以完成分页查询
* limit 关键字后面的第一个参数是开始行索引(默认是0,不写就是0),第二个参数是查询条数

# 聚合函数
默认忽略字段为null的记录 要想列值为null的记录也参与计算,必须使用ifnull函数对null值做替换。
* count(col): 表示求指定列的总行数
* max(col): 表示求指定列的最大值
* min(col): 表示求指定列的最小值
* sum(col): 表示求指定列的和
* avg(col): 表示求指定列的平均值

# 分组
GROUP BY 列名 [HAVING 条件表达式] [WITH ROLLUP]
说明:
* 列名: 是指按照指定字段的值进行分组。
* HAVING 条件表达式: 用来过滤分组后的数据。
* WITH ROLLUP:在所有记录的最后加上一条记录,显示select查询时聚合函数的统计和计算结果

group by + group_concat()
统计每个分组指定字段的信息集合,每个信息之间使用逗号进行分割
    -- 根据gender字段进行分组, 查询gender字段和分组的name字段信息select         gender,group_concat(name) from students group by gender;
group by + with rollup
    -- 根据gender字段进行分组,汇总总人数
    select gender,count(*) from students group by gender with rollup;
    -- 根据gender字段进行分组,汇总所有人的年龄
    select gender,group_concat(age) from students group by gender with rollup;

内连接:inner join .. on .., on
左连接:left join .. on .., on
    以左表为主根据条件查询右表数据,右表数据不存在使用null值填充。
右连接:right join .. on .., on
    以右表为主根据条件查询左表数据,左表数据不存在使用null值填充。
自连接:
    一张表模拟成左右两张表,接的表还是本身这张表,必须对表起别名

# 子查询
1. 查询大于平均年龄的学生:
select * from students where age > (select avg(age) from students);
2. 查询学生在班的所有班级名字:
select name from classes where id in (select cls_id from students where cls_id is not null);
3. 查找年龄最大,身高最高的学生:
select * from students where (age, height) =  (select max(age), max(height) from students);

三范式

1
第一范式(1NF): 强调的是列的原子性,即列不能够再分成其他几列。

image-20230927182358952

1
    这种表结构设计就没有达到 1NF,要符合 1NF 我们只需把列拆分,即:把 contact 字段拆分成 name 、tel、addr 等字段。
1
第二范式(2NF): 满足 1NF,另外包含两部分内容,一是表必须有一个主键;二是非主键字段 必须完全依赖于主键,而不能只依赖于主键的一部分。

image-20230927182411081

1
2
    这种表结构设计就没有达到 2NF,因为 Discount(折扣),Quantity(数量)完全依赖于主键(OrderID),而 UnitPrice单价,ProductName产品名称 只依赖于 ProductID, 所以 OrderDetail 表不符合 2NF。
    我们可以把【OrderDetail】表拆分为【OrderDetail】(OrderID,ProductID,Discount,Quantity)和【Product】(ProductID,UnitPrice,ProductName)这样就符合第二范式了。
1
第三范式(3NF): 满足 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。

image-20230927182421242

1
2
    这种表结构设计就没有达到 3NF,因为 OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity 等非主键列都完全依赖于主键(OrderID),所以符合 2NF。不过问题是 CustomerName,CustomerAddr,CustomerCity 直接依赖的是 CustomerID(非主键列),而不是直接依赖于主键,它是通过传递才依赖于主键,所以不符合 3NF。
    我们可以把【Order】表拆分为【Order】(OrderID,OrderDate,CustomerID)和【Customer】(CustomerID,CustomerName,CustomerAddr,CustomerCity)从而达到 3NF。

E-R模型

1
2
3
4
5
6
7
power designer,db desinger来画出实体-关系模型
* 实体: 用矩形表示,并标注实体名称
* 属性: 用椭圆表示,并标注属性名称,
* 关系: 用菱形表示,并标注关系名称
    * 一对一
    * 一对多
    * 多对多

外键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
外键约束:对外键字段的值进行更新和插入时会和引用表中字段的数据进行验证,数据如果不合法则更新和插入会失败,保证数据的有效性

1. 对于已经存在的字段添加外键约束
    -- 为cls_id字段添加外键约束
    alter table students add foreign key(cls_id) references classes(id);
2. 在创建数据表时设置外键约束
    -- 创建学校表
create table school(
    id int not null primary key auto_increment,
    name varchar(10)
);
    -- 创建老师表
create table teacher(
    id int not null primary key auto_increment,
    name varchar(10),
    s_id int not null,
    foreign key(s_id) references school(id)
);
4. 删除外键约束
    -- 需要先获取外键约束名称,该名称系统会自动生成,可以通过查看表创建语句来获取名称
    show create table teacher;

    -- 获取名称之后就可以根据名称来删除外键约束
    alter table teacher drop foreign key 外键名;

事务

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
执行一系列SQL语句的时候,想要保证这些SQL要么都执行,要么都不执行,就要用到事务了。

* 原子性(Atomicity)
    一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性
* 一致性(Consistency)
    数据库总是从一个一致性的状态转换到另一个一致性的状态。(在前面的例子中,一致性确保了,即使在转账过程中系统崩溃,支票账户中也不会损失200美元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。)
* 隔离性(Isolation)
    通常来说,一个事务所做的修改操作在提交事务之前,对于其他事务来说是不可见的。(在前面的例子中,当执行完第三条语句、第四条语句还未开始时,此时有另外的一个账户汇总程序开始运行,则其看到支票帐户的余额并没有被减去200美元。)
* 持久性(Durability)
    一旦事务提交,则其所做的修改会永久保存到数据库。
说明:
    事务能够保证数据的完整性和一致性,让用户的操作更加安全。

# 事务使用前提
    在使用事务之前,先要确保表的存储引擎是 InnoDB 类型, 只有这个类型才可以使用事务,MySQL数据库中表的存储引擎默认是 InnoDB 类型。表的存储引擎就是提供存储数据一种机制,不同表的存储引擎提供不同的存储机制。
    * InnoDB 是支持事务的
    * MyISAM 不支持事务,优势是访问速度快,对事务没有要求或者以select、insert为主的都可以使用该存储引擎来创建表

# 开启事务
begin;
或者
start transaction;
    开启事务后执行修改命令,变更数据会保存到MySQL服务端的缓存文件中,而不维护到物理表中
    MySQL数据库默认采用自动提交(autocommit)模式,如果没有显示的开启一个事务,那么每条sql语句都会被当作一个事务执行提交的操作
    当设置autocommit=0就是取消了自动提交事务模式,直到显示(手动)的执行commit和rollback表示该事务结束。 
# 提交事务:
将本地缓存文件中的数据提交到物理表中,完成数据的更新。
commit;
# 回滚事务:
放弃本地缓存文件中的缓存数据, 表示回到开始事务前的状态
rollback;

image-20230927182432666

image-20230927182438895

索引

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
查看表中已有索引:
    show index from 表名; # 主键列会自动创建索引
    desc 查询语句,可以查询索引使用情况
# 索引的创建:
    -- alter table 表名 add index 索引名[可选](列名, ..)
    -- 给name字段添加索引
    alter table classes add index my_name (name); # 索引名不指定,默认使用字段名

# 索引的删除:
    -- alter table 表名 drop index 索引名
    -- 如果不知道索引名,可以查看创表sql语句
    show create table classes;
    alter table classes drop index my_name;

# 验证索引性能操作
-- 开启运行时间监测:
    set profiling=1;
-- 查找第1万条数据ha-99999
    select * from test_index where title='ha-99999';
-- 查看执行的时间:
    show profiles;
-- 给title字段创建索引:
    alter table test_index add index (title);
-- 再次执行查询语句
    select * from test_index where title='ha-99999';
-- 再次查看执行的时间
    show profiles;

# 联合索引:联合索引又叫复合索引,即一个索引覆盖表中两个或者多个字段,一般用在多个字段一起查询的时候。
    alter table teacher add index (name,age);
    联合索引的好处:
        减少磁盘空间开销,因为每创建一个索引,其实就是创建了一个索引文件,那么会增加磁盘空间的开销。

# 联合索引的最左原则
    index(name,age)支持 name 、name 和 age 组合查询,而不支持单独 age 查询,因为没有用到创建的联合索引。

-- 下面的查询使用到了联合索引
    select * from stu where name='张三'  # 这里使用了联合索引的name部分
    select * from stu where name='李四' and age=10 # 这里完整的使用联合索引,包括 name 和 age 部分 
-- 下面的查询没有使用到联合索引
    select * from stu where age=10  # 因为联合索引里面没有这个组合,只有 name | name age 这两种组合
    在使用联合索引的查询数据时候一定要保证联合索引的最左侧字段出现在查询条件里面,否则联合索引失效

# 优点和缺点和使用原则
* 优点:
    1. 加快数据的查询速度
* 缺点:
    1. 创建索引会耗费时间和占用磁盘空间,并且随着数据量的增加所耗费的时间也会增加
* 使用原则:
    1. 通过优缺点对比,不是索引越多越好,而是需要自己合理的使用。
    2. 对经常更新的表就避免对其进行过多索引的创建,对经常用于查询的字段应该创建索引,
    3. 数据量小的表最好不要使用索引,因为由于数据较少,可能查询全部数据花费的时间比遍历索引的时间还要短,索引就可能不会产生优化效果。
    4. 在一字段上相同值比较多不要建立索引,比如在学生表的"性别"字段上只有男,女两个不同值。相反的,在一个字段上不同值较多可是建立索引。

pymysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 如何实现将100000条数据插入到MySQL数据库?
安装pymysql第三方包:
    sudo pip3 install pymysql  # install 安装 uninstall卸载
查看pymysql是否安装:
    pip3 show pymysql  # 可以查看安装的路径
pip3 list 查看使用pip命令安装的第三方包列表

# SQL注入
什么是SQL注入?
    用户提交带有恶意的数据与SQL语句进行字符串方式的拼接,从而影响了SQL语句的语义,最终产生数据泄露的现象。
如何防止SQL注入?
    SQL语句参数化
        * SQL语言中的参数使用%s来占位,此处不是python中的字符串格式化操作
        * 将SQL语句中%s占位所需要的参数存在一个列表中,把参数列表传递给execute方法中第二个参数

简单使用

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
# 1.导包
import pymysql
# 2.创建连接对象
'''
* 参数host:连接的mysql主机,如果本机是'localhost'
* 参数port:连接的mysql主机的端口,默认是3306
* 参数user:连接的用户名
* 参数password:连接的密码
* 参数database:数据库的名称
* 参数charset:通信采用的编码方式,推荐使用utf8
'''
# can use under three way to create connection
# Connect = connect = Connection = connections.Connection
conn = pymysql.connect(
    host="localhost",
    port=3306,
    user="root",
    password="root",
    database="python",
    charset="utf8"
)
# 3.获取游标对象
cursor =conn.cursor()
# 4.执行SQL语句
# 查询 SQL 语句
# sql = "select * from students;"
# ---------------------------sql zhuru------------------------------------------
# 非安全的方式
# 输入 ' or 1 = 1 or '   (单引号也要输入)
# sql = "select * from goods where name='%s'" % find_name
# print("""sql===>%s<====""" % sql)
# # 执行select语句,并返回受影响的行数:查询所有数据
# count = cs1.execute(sql)

# 安全的方式
# 构造参数列表
# find_name = input("请输入物品名称:")
# params = [find_name]
# sql = 'select * from goods where name=%s;'
# # 执行select语句,并返回受影响的行数:查询所有数据
# row_count = cursor.execute(sql, params)
# 注意:
#   如果要是有多个参数,需要进行参数化,可以是元组,列表,字典
#   那么params = [数值1, 数值2....],此时sql语句中有多个%s即可
#   %s 不需要带引号
# --------------------------sql 注入-------------------------------------------
try:
    # 添加 SQL 语句
    # sql = "insert into students(name) values('刘璐'), ('王美丽');"
    # 插入10万次数据
    # for i in range(100000):
    #     cursor.execute("insert into test_index values('ha-%d')" % i)
    # 删除 SQL 语句
    # sql = "delete from students where id = 5;"
    # 修改 SQL 语句
    sql = "update students set name = '王铁蛋' where id = 1;"

    # 执行 SQL 语句 返回值就是 SQL 语句在执行过程中影响的行数
    row_count = cursor.execute(sql)
    print("SQL 语句执行影响的行数%d" % row_count)
    # 5.获取查询结果集
    # for line in cursor.fetchall():
    #     print(line)
    # 6.将修改操作提交到数据库 pymysql默认开启事务,所以要手动提交
    conn.commit()
# 7.回滚数据
except Exception as e:
    # 回滚数据, 即撤销刚刚的SQL语句操作
    conn.rollback()
# 8.关闭游标
cursor.close()
# 9.关闭连接
conn.close()

查看MySQL数据库日志

1
2
mysql日志文件默认没有产生,需要做如下配置:
sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

image-20230927182452317

1
2
3
4
5
6
7
把68,69行前面的#去除,然后保存并使用如下命令重启mysql服务。
sudo service mysql restart

使用如下命令打开mysql日志文件。
tail -f /var/log/mysql/mysql.log  # 可以实时查看数据库的日志内容
# 如提示需要sudo权限,执行
# sudo tail -f /var/log/mysql/mysql.log

mini-web框架

闭包和装饰器

闭包

1
2
3
4
5
6
7
    我们前面已经学过了函数,我们知道当函数调用完,函数内定义的变量都销毁了,但是我们有时候需要保存函数内的这个变量,每次在这个变量的基础上完成一些列的操作,比如: 每次在这个变量的基础上和其它数字进行求和计算,那怎么办呢?
我们就可以通过咱们今天学习的闭包来解决这个需求。
闭包的定义:
    在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
    1. 在函数嵌套(函数里面再定义函数)的前提下
    2. 内部函数使用了外部函数的变量(还包括外部函数的参数)
    3. 外部函数返回了内部函数

image-20230927182459236

1
2
3
4
5
通过上面的输出结果可以看出闭包保存了外部函数内的变量num1,每次执行闭包都是在num1 = 1 基础上进行计算。

* 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。
注意点:
* 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存

例子:

image-20230927182504950

1
* 闭包还可以提高代码的可重用性,不需要再手动定义额外的功能函数。
  • nonlocal

在内部函数想使用并修改外部函数的变量,需要用nonlocal声明一下,因为外部函数的变量不是全局的,所以不能用global

image-20230927182511514

装饰器

  • 什么是装饰器
1
2
3
4
5
就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
装饰器的功能特点:
1. 不修改已有函数的源代码
2. 不修改已有函数的调用方式
3. 给已有函数增加额外的功能

image-20230927182517269

image-20230927182523939

1
2
* 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
* 写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。
  • 更简单的装饰器写法(语法糖)
1
2
3
4
5
    如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。
    语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰
    见上图右图
        * @check 等价于 comment = check(comment)
        * 装饰器的执行时间是加载模块时立即执行
  • 一般的使用场景
1
2
3
装饰器的使用场景
1. 函数执行时间的统计
2. 输出日志信息

image-20230927182531565

  • 装饰带有参数的函数

image-20230927182537116

  • 装饰带有返回值的函数

image-20230927182542592

  • 装饰带有不定长参数的函数

image-20230927182548691

  • 通用装饰器

image-20230927182555039

1
一般直接拷贝要装饰函数的参数.作为装饰器的内部函数的参数。有返回值,就return 内部函数
  • 带有参数的装饰器介绍(@装饰器(参数,…))
1
2
装饰器只能接收一个参数,并且还是函数类型。
    在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。

image-20230927182602254

1
    多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程 
  • 类装饰器的使用

image-20230927182610665

1
2
3
4
5
6
7
    @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
    要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
    在call方法里进行对fn函数的装饰,可以添加额外的功能。

执行结果:
    请先登陆...
    发表评论

property

1
2
3
4
5
6
property属性就是负责把一个方法当做属性进行使用,这样做可以简化代码使用。
定义property属性有两种方式
1. 装饰器方式
2. 类属性方式

不然我们调用私有属性可能就是调用set_age之类的方法,而用@preperty后,还是.属性原名。很方便。

image-20230927182616303

1
2
3
* @property 表示把方法当做属性使用, 表示当获取属性时会执行下面修饰的方法
* @方法名.setter 表示把方法当做属性使用,表示当设置属性时会执行下面修饰的方法
* 装饰器方式的property属性修饰的方法名一定要一样。

image-20230927182622421

1
2
3
property的参数说明:
* 第一个参数是获取属性时要执行的方法
* 第二个参数是设置属性时要执行的方法

logging日志

17_logging日志

17_logging日志

怎么记录日志

1
2
3
4
5
6
7
8
9
python中使用logging这个包
日志等级可以分为5个,从低到高分别是:
* DEBUG:程序调试bug时使用
* INFO:程序正常运行时使用
* WARNING:程序未按预期运行时使用,但并不是错误,如:用户登录密码错误
* ERROR:程序出错误时使用,如:IO操作失败
* CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用
* 默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
* 日志等级从低到高的顺序是: DEBUG < INFO < WARNING < ERROR < CRITICAL

输出到控制台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import logging
# 日志信息只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING
# 设置日志等级和输出日志格式
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
'''
* level 表示设置的日志等级
* format 表示日志的输出格式, 参数说明:
    * %(levelname)s: 打印日志级别名称
    * %(filename)s: 打印当前执行程序名
    * %(lineno)d: 打印日志的当前行号
    * %(asctime)s: 打印日志的时间
    * %(message)s: 打印日志信息
'''
logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')

保存到日志文件

1
2
3
4
5
6
7
8
9
10
11
12
import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                    filename="log.txt",
                    filemode="w")

logging.debug('这是一个debug级别的日志信息')
logging.info('这是一个info级别的日志信息')
logging.warning('这是一个warning级别的日志信息')
logging.error('这是一个error级别的日志信息')
logging.critical('这是一个critical级别的日志信息')