事务(DTL)
begin; commit; rollback;
事务的四个特性(ACID)
A 原子性
C 一致性
I 隔离性 (Isolation)
四个级别:
读未提交(read uncommitted)、
读已提交 (read committed)、
可重复读(repeatable read)
串行化(serializable)
查看当前会话session(连接)的隔离性:
select @@tx_isolation;
设置当前会话的隔离级别:
set session transaction isolation level read committed;
约束
查询表中的约束
show create table 表名;
select constraint_name, constraint_type
from information_schema.table_constraints
where table_schema='数据库名'
and table_name= '表名';
添加约束
create table 表名(
...,
constraint [约束名] constraint_type (约束的字段1 [, 约束的字段2] )
);
alter table 表名
add constraint [约束名] constraint_type (约束的字段1 [, 约束的字段2] );
删除约束
alter table 表名
drop constraint_type [约束名];
外键约束
-- 普通的外键约束
alter table card
add constraint user_id_fk foreign key (user_id) references user(id);
-- 级联的外键约束
alter table card
add constraint user_id_fk foreign key (user_id) references user(id)
on delete cascade|set null;
连表查询
等值条件查询
select a.*, b.*
from a, b
where a.id = b.aid; -- 外键与主键的连接
如果有n张表,应该有n-1等值条件连接。
内连接是两个表之间都具有的数据连接,连接字段的值有交集的部分。
使用 join on 关键字:
-- 查看所有用户在所有银行中的开户信息: 姓名、身份证号、银行卡号、银行名称、余额
-- user(id, number,name, phone)
-- back(id, name, address)
-- card(id, number, money, passwd, user_id, back_id)
select u.name as user_name,
u.number as user_number,
replace(c.number,' ', '') as card_number,
b.name as back_name,
c.money
from card c
join user u on (c.user_id=u.id)
join back b on (c.back_id=b.id);
user和back两张表是没有直接关系, 都是通过card表进行关联。from 关键字后 表名应该是连接三张表中最核心的表。
左外连接是指定除内连接的数据之外,增加左边表中的其它的数据。即左表选择全部数据,右表选择交集(都存在的)的数据。
使用的关键字: left join on
-- 查看所有用户的银行卡及余额信息
-- user(id, number,name, phone)
-- card(id, number, money, passwd, user_id, back_id)
select name, c.number as card_number, money
from user u
left join card c on (c.user_id = u.id);
如何选择左表, 应该根据查询的要求。 上面的要求是查询"所有用户", 选择user表作为左表是最合适的。
同左外连接相反,除内连接的数据之外,额外选择右表中的其它数据。
使用关键字: right join on
-- 查看用户的所有头像信息: 用户名、头像url
-- user(id, number,name, phone)
-- user_photo(photo_id, url, user_id)
select name, url, photo_id
from user u
right join user_photo p on (u.id=p.user_id);
将内连接、左外连接、右外连接的全部数据选择出来。
使用的关键字: full [outer] join on
-- 全部的用户信息和头像信息
select name, url, photo_id
from user u
full join user_photo p on (u.id=p.user_id);
注意: MySQL中不支持全外连接。
可以尝试使用union的合并查询关键字,将左外和右外查询的结果合并到一块:
select name, url, photo_id
from user u
left join user_photo p on (u.id=p.user_id)
union
select name, url, photo_id
from user u
right join user_photo p on (u.id=p.user_id);
union查询,要求两个查询的列名保持一致。对于重复的行会自动去重。
生成样本数据
-- 教师表 teacher
create table teacher(
id varchar(20) primary key,
name varchar(20) not null,
tel varchar(11)
) default charset=utf8;
insert into teacher values
('120001','张之梅','15611789900'),
('120002','刘小庆','17881918181'),
('120003','沈小阳','19929109991');
-- 班级表
create table cls(
id integer primary key auto_increment,
name varchar(20) unique,
teacher_id varchar(20),
constraint teacher_id_fk foreign key(teacher_id) references teacher(id)
on delete set null
) default charset=utf8;
insert into cls(name, teacher_id)
values
('1901','120001'),
('1902','120002'),
('1903','120001');
-- 学生表
create table student(
stu_id varchar(20) primary key,
stu_name varchar(50) not null,
sex varchar(1) default '男',
city varchar(10) default '西安',
cls_id integer comment '班级ID',
constraint cls_id_fk foreign key (cls_id) references cls(id)
on delete cascade
) default charset=utf8;
insert into student(stu_id,stu_name,cls_id,sex,city)
values
('1001','jack',1,'男','西安'),
('1002','disen',1,'男','西安'),
('1003','rose',2,'女', '咸阳'),
('1004', 'lucy', 1,'女','咸阳'),
('1005', 'jerry', 2, '女', '开封'),
('1006', 'pety',3,'男' ,'开封'),
('1007', 'judy',1 ,'女', '洛阳'),
('1008', 'mack',3, '男', '洛阳');
-- sum() 求合
-- avg() 平均值
-- min() 最小值
-- max() 最大值
-- count() 统计数量
以上聚合函数, 除了count()用于统计数量的,其它函数都用于数值计算。
-- 查看最富有的用户
-- card 表
select max(money) from card;
+------------+
| max(money) |
+------------+
| 40000 |
+------------+
select name, c.number as card_number, money
from user u
join card c on (c.user_id = u.id)
where money = ( select max(money) from card );
+--------+---------------------+-------+
| name | card_number | money |
+--------+---------------------+-------+
| 李成 | 8765 2134 4455 6719 | 40000 |
+--------+---------------------+-------+
如果聚合查询的结果只有一个,可以作为其它查询的where条件。另外,子查询放在一个小括号中。
-- 统计已开户的银行卡总数
select count(id) from card;
-- 查看最小、平均、最大的银行卡的余额值和银行卡的数量
select min(money) as min_money,
round(avg(money),2) as avg_money,
max(money) as max_money,
count(id) as card_cnt
from card;
+-----------+-----------+-----------+----------+
| min_money | avg_money | max_money | card_cnt |
+-----------+-----------+-----------+----------+
| 0 | 12857.14 | 40000 | 7 |
+-----------+-----------+-----------+----------+
关键字: group by
-- 统计学生表的男生和女生的人数
-- stu.student表
select sex, count(*)
from student;
ERROR 1140 (42000): In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'stu.student.sex'; this is incompatible with sql_mode=only_full_group_by
错误的说明, sex列不是聚合列, 需要将sex字段放在group by子句中。
select sex, count(*)
from student
group by sex;
+------+----------+
| sex | count(*) |
+------+----------+
| 女 | 4 |
| 男 | 4 |
+------+----------+
-- 查看每个城市的学生人数
select city, count(stu_id)
from student
group by city;
+--------+---------------+
| city | count(stu_id) |
+--------+---------------+
| 咸阳 | 2 |
| 开封 | 2 |
| 洛阳 | 2 |
| 西安 | 2 |
+--------+---------------+
-- 查看每个城市不同性别的学生人数
select city, sex, count(*)
from student
group by city, sex;
group by的子句中可以有多个分组的字段。
-- 查看每个班级的人数
select cls.name, count(*)
from student s
join cls on (cls.id = s.cls_id)
group by cls.name;
一般在连接join on之后,使用group by分组语句。
-- 查询带班人数最多的教师的信息: 教师的名称、教师的电话和带班的班级名和人数
select t.name,t.tel,c.name,A.stu_cnt
from teacher t
join cls c on (c.teacher_id = t.id)
join (
select cls.id as cls_id, count(*) as stu_cnt
from student s
join cls on (cls.id = s.cls_id)
group by cls.id
) A on (A.cls_id =c.id)
where A.stu_cnt = (
select max(stu_cnt) max_stu_cnt
from
( select cls.id as cls_id, count(*) as stu_cnt
from student s
join cls on (cls.id = s.cls_id)
group by cls.id ) B
);
-- 创建统计班级人数的视图
-- 视图不是一张表,而是保存查询的SQL语句。
-- 视图的查询同表的查询是一样的
create view stu_cnt_view
AS
select cls_id, count(*) as stu_cnt
from student s
group by s.cls_id;
select max(stu_cnt) max_stu_cnt
from stu_cnt_view; -- 视图在查询SQL中同表的用法一样
-- 优化查询语句
-- 查询带班人数最多的教师的信息: 教师的名称、教师的电话和带班的班级名和人数
select t.name,t.tel,c.name,A.stu_cnt
from teacher t
join cls c on (c.teacher_id = t.id)
join stu_cnt_view A on(A.cls_id=c.id)
where A.stu_cnt = (
select max(stu_cnt)
from stu_cnt_view
);
-- 查询班级人数小于4人的班级名称和带课老师的姓名
select c.name as cls_name,
t.name as teacher_name
from cls c
join teacher t on (t.id=c.teacher_id)
where c.id in (
select cls_id
from student
group by cls_id
having count(stu_id) < 4 -- 聚合的结果作为条件使用时,必须放在having中
);
having 子句是在聚合函数的结果作为查询条件时,不能将条件放在where中,只能在having中。
分页查询是解决数据量大不易全部加载和客户端异步请求方式读取数据的情况。
语法: LIMIT offset, rows
offset是起始位置,offset从0开始的, offset的公式: (page-1)*size
rows 一次性读取的行数, 通过size表示一页的数据大小或个数。
-- 分页查询: 每一页显示2条记录, 查询第1页
select * from student
limit 0, 2;
-- 查询第4页
select * from student
limit 6, 2;
项目的名称 dbs_mysql
存放在 项目目录中, Window: d:/codes/
Python环境的名称 dbs
存放的位置: window: d:/venvs
python环境的完整路径: d:/venvs/dbs
激活Python环境
在PyCharm低版本中的Terminal终端里显示的路径默认没有激活Python环境
d:\codes\dbs_mysql > d:\venvs\dbs\Scripts\activate.bat
(dbs) d:\codes\dbs_mysql > pip install pymysql
如果发现安装很慢,或超时,可以通过 -i 参数指定安装源,如阿里的源:
pip install pymysql -i https://mirrors.aliyun.com/pypi/simple
pymysql 主要提供了mysql数据库连接池,用于连接数据库和执行SQL语句。pymysql还支持事务,如在执行DML语句后,需要提交事务,如果不提交事务,则不会影响数据库的数据的。
import pymysql
conn = pymysql.Connect(host, port=3306, user, password, db, charset)
print('Connected!')
对于pymysql.Connect()的参数,通常 ***config方式,即config是一个dict字典,将host/port/user/password/db/charset等关键参数作为dict的key和value封装起来,使用的通过**config 将dict转成关键参数的结构传入Connect()方法中。
import pymysql
from pymysql.cursors import Cursor, DictCursor
config = {
'host': '10.36.174.19', # 可以更成域名 mysql903
'port': 3306,
'user': 'root',
'password': 'root', # 可以写成passwd
'db': 'back', # 可以写成database
'charset': 'utf8',
'connect_timeout': 10, # 默认10秒
'read_timeout': 10,
'write_timeout': 10,
'cursorclass': Cursor # 指定操作数据库的游标类,类似于文件流对象
}
conn = pymysql.Connect(**config)
print('Connected!')
必须先连接数据库,然后再打开cursor对象,通过cursor对象的execute()方法来执行查询SQL语句。
cursor = conn.cursor()
sql = 'select number,money from card'
cursor.execute(sql) # 执行sql语句
再通过cursor对象的fetchone()或fetchall()获取 一条或所有条的查条记录。
results = cursor.fetchall()
print(results) # (('191992029', 8000), ...)
返回是可迭代的元组,内容又是的元组数据(按select中选择字段的顺序)
cursor = conn.cursor(cursor=DictCursor)
results = cursor.execute(sql)
print(results) # [{'number': '', 'money':}, ...]
如果cursorclass指定的DictCursor, 则list中包含是每条记录的dict格式的数据。
在execute(sql, args)方法执行时,如果sql中使用 %s或%(key)s占位符号,则args必须指定相应的参数,如果sql使用是%s,则args指定是tuple类型, 如果sql中使用%(key)s,则args指定是dict类型。
-- sql 中使用 %s
sql = 'select * from card where user_id=%s'
cursor.execute(sql, args=(1,))
results = cursor.fetchall()
print(results)
sql = 'select * from card where user_id=%(uid)s'
cursor.execute(sql, args={'uid': 1})
results = cursor.fetchall()
print(results)
当execute()执行了DML语句时,需要提交事务, 可以通过cursor.rowcount 属性获取DML语句影响的行数。
sql = 'insert into user(name, number, phone) values' \
'(%(name)s , %(number)s, %(phone)s)'
user_data = {
'name': '刘呈迁',
'number': '2999992999292',
'phone': '17766655252'
}
cursor.execute(sql, args=user_data)
if cursor.rowcount >= 1:
print('添加成功')
else:
print('添加失败')
conn.commit() # 提交事务
Python中支持上下文环境的应用,主要使用with关键字,任何实现了__enter__()和__exit__()这两个函数的对象都可以在with关键字中使用。当对象进入上下文时,会调用对象__enter__(),当退出上下文时,会调用对象的__exit__()__
class A:
def __enter__(self):
return [1,2,3]
def __exit__(self, exc_type, exc_value, exc_tb):
print('---释放资源--')
if exc_type:
print('Error:', exc_value)
# True 如果exc_type不为None,则表示有异常但不继续抛出, False会抛出异常
return False
a = A()
with a as nums: # nums接收__enter__()返回的结果
print(nums)
raise Exception('我的异常,我做主!')
print('可以正常输出吗?')
pymysql的conn连接对象已经实现了上下文相关的两个函数。意味着conn可以在with中使用。
with conn as c:
c.execute('select * from user')
print(c.fetchall())
如果conn在with中使用,则进入上下文时,调用conn的__enter__(),这个方法会返回cursor对象,当退出上下文时,检查 是否有异常,如果没有异步则提交事务,反之回滚事务。
Connect类的源码如下:
class Connection:
....
def __enter__(self):
"""Context manager that returns a Cursor"""
warnings.warn(
"Context manager API of Connection object is deprecated; Use conn.begin()",
DeprecationWarning)
return self.cursor()
...
def __exit__(self, exc, value, traceback):
"""On successful exit, commit. On exception, rollback"""
if exc:
self.rollback()
else:
self.commit()
关键字: order by 字段名 [ DESC|ASC ]
DESC 表示字段的值按从大到小的顺序排列的; 降序
ASC 表示字段的值从小到大的顺序 排列的; 升序, 默认
-- order by 语句是在select查询语句的最后位置使用
-- 仅比 limit 语句高一层
-- order by 中可以使用 列的别名
-- 查询用户的银行存款信息,且按升序排列
select u.name, money
from user u
join card c on (c.user_id=u.id)
order by money asc;
-- 查看用户预取款5000元之后的银行存款信息,且按降序排列
select u.name, money-5000 as y_money
from user u
join card c on (c.user_id=u.id)
order by y_money desc;
-- 查看最富有的前3位用户
select u.name, money
from user u
join card c on (c.user_id=u.id)
order by money DESC
limit 0, 3;