上节回顾:
1. RabbitMQ
a. 平均分发
b. perfetch = 1
c. durable 队列持久化 deliver_mode = 2 消息持久化
d. 1对多广播 exchange
本节内容
- 数据库介绍
- mysql 数据库安装使用
- mysql管理
- mysql 数据类型
- 常用mysql命令
- 创建数据库
- 外键
- 增删改查表
- 权限
- 事务
- 索引
- python 操作mysql
- ORM sqlachemy学习
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
一、数据库介绍
1.数据库介绍
数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据。我们也可以将数据存储在文件中,但是在文件中读写数据速度相对较慢。所以,现在我们使用关系型数据库管理系统(RDBMS)来存储和管理的大数据量。所谓的关系型数据库,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。
RDBMS即关系数据库管理系统(Relational Database Management System)的特点:
1.数据以表格的形式出现
2.每行为各种记录名称
3.每列为记录名称所对应的数据域
4.许多的行和列组成一张表单
5.若干的表单组成database
2.RDBMS 术语
在我们开始学习MySQL 数据库前,让我们先了解下RDBMS的一些术语:
- 数据库: 数据库是一些关联表的集合。.
- 数据表: 表是数据的矩阵。在一个数据库中的表看起来像一个简单的电子表格。
- 列: 一列(数据元素) 包含了相同的数据, 例如邮政编码的数据。
- 行:一行(=元组,或记录)是一组相关的数据,例如一条用户订阅的数据。
- 冗余:存储两倍数据,冗余可以使系统速度更快。(表的规范化程度越高,表与表之间的关系就越多;查询时可能经常需要在多个表之间进行连接查询;而进行连接操作会降低查询速度。例如,学生的信息存储在student表中,院系信息存储在department表中。通过student表中的dept_id字段与department表建立关联关系。如果要查询一个学生所在系的名称,必须从student表中查找学生所在院系的编号(dept_id),然后根据这个编号去department查找系的名称。如果经常需要进行这个操作时,连接查询会浪费很多的时间。因此可以在student表中增加一个冗余字段dept_name,该字段用来存储学生所在院系的名称。这样就不用每次都进行连接操作了。)
- 主键:主键是唯一的。一个数据表中只能包含一个主键。你可以使用主键来查询数据。
- 外键:外键用于关联两个表。
- 复合键:复合键(组合键)将多个列作为一个索引键,一般用于复合索引。
- 索引:使用索引可快速访问数据库表中的特定信息。索引是对数据库表中一列或多列的值进行排序的一种结构。类似于书籍的目录。
- 参照完整性: 参照的完整性要求关系中不允许引用不存在的实体。与实体完整性是关系模型必须满足的完整性约束条件,目的是保证数据的一致性。
3.MySQL数据库
Mysql是最流行的关系型数据库管理系统,在WEB应用方面MySQL是最好的RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。由瑞典MySQL AB公司开发,目前属于Oracle公司。MySQL是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
- Mysql是开源的,所以你不需要支付额外的费用。
- Mysql支持大型的数据库。可以处理拥有上千万条记录的大型数据库。
- MySQL使用标准的SQL数据语言形式。
- Mysql可以允许于多个系统上,并且支持多种语言。这些编程语言包括C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等。
- Mysql对PHP有很好的支持,PHP是目前最流行的Web开发语言。
- MySQL支持大型数据库,支持5000万条记录的数据仓库,32位系统表文件最大可支持4GB,64位系统支持最大的表文件为8TB。
- Mysql是可以定制的,采用了GPL协议,你可以修改源码来开发自己的MySQL系统。
二、MySQL数据库安装及使用
1.MySQL安装(略)
2.MySQL用户管理
如果你需要添加 MySQL 用户,你只需要在 mysql 数据库中的 user 表添加新用户即可。
以下为添加用户的的实例,用户名为guest,密码为guest123,并授权用户可进行 SELECT, INSERT 和 UPDATE操作权限:
root@host# mysql -u root -p
Enter password:*******
mysql> use mysql;
Database changed
mysql> INSERT INTO user
(host, user, password,
select_priv, insert_priv, update_priv)
VALUES ('localhost', 'guest',
PASSWORD('guest123'), 'Y', 'Y', 'Y');
Query OK, 1 row affected (0.20 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 1 row affected (0.01 sec)
mysql> SELECT host, user, password FROM user WHERE user = 'guest';
+-----------+---------+------------------+
| host | user | password |
+-----------+---------+------------------+
| localhost | guest | 6f8c114b58f2ce9e |
+-----------+---------+------------------+
1 row in set (0.00 sec)
在添加用户时,请注意使用MySQL提供的 PASSWORD() 函数来对密码进行加密。 你可以在以上实例看到用户密码加密后为6f8c114b58f2ce9e.
注意:在 MySQL5.7 中 user 表的 password 已换成了authentication_string。
注意:在注意需要执行 FLUSH PRIVILEGES 语句。 这个命令执行后会重新载入授权表。
如果你不使用该命令,你就无法使用新创建的用户来连接mysql服务器,除非你重启mysql服务器。
你可以在创建用户时,为用户指定权限,在对应的权限列中,在插入语句中设置为 'Y' 即可,用户权限列表如下:
Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Reload_priv,Shutdown_priv,Process_priv,File_priv,Grant_priv,References_priv,Index_priv,Alter_priv
另外一种添加用户的方法为通过SQL的 GRANT 命令,你下命令会给指定数据库TUTORIALS添加用户 zara ,密码为 zara123
mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON TUTORIALS.* TO 'zara'@'localhost' IDENTIFIED BY 'zara123';
3.MySQL管理命令
以下列出了使用Mysql数据库过程中常用的命令:
- USE 数据库名 :选择要操作的Mysql数据库,使用该命令后所有Mysql命令都只针对该数据库。
- SHOW DATABASES: 列出 MySQL 数据库管理系统的数据库列表。
- SHOW TABLES: #显示指定数据库的所有表,使用该命令前需要使用 use命令来选择要操作的数据库。
- SHOW COLUMNS FROM 数据表: #显示数据表的属性,属性类型,主键信息 ,是否为 NULL,默认值等其他信息。
- create database testdb charset "utf8"; #创建一个叫testdb的数据库,且让其支持中文
- drop database testdb; #删除数据库
- SHOW INDEX FROM 数据表:显示数据表的详细索引信息,包括PRIMARY KEY(主键)。
4. MySQL数据类型
MySQL中定义数据字段的类型对你数据库的优化是非常重要的。
MySQL支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。
a.数值类型
MySQL支持所有标准SQL数值数据类型。这些类型包括严格数值数据类型(INTEGER、SMALLINT、DECIMAL和NUMERIC),以及近似数值数据类型(FLOAT、REAL和DOUBLE PRECISION)。
关键字INT是INTEGER的同义词,关键字DEC是DECIMAL的同义词。
BIT数据类型保存位字段值,并且支持MyISAM、MEMORY、InnoDB和BDB表。
作为SQL标准的扩展,MySQL也支持整数类型TINYINT、MEDIUMINT和BIGINT。下面的表显示了需要的每个整数类型的存储和范围。
类型 | 大小 | 范围(有符号) | 范围(无符号) | 用途 |
TINYINT | 1 字节 | (-128,127) | (0,255) | 小整数值 |
SMALLINT | 2 字节 | (-32 768,32 767) | (0,65 535) | 大整数值 |
MEDIUMINT | 3 字节 | (-8 388 608,8 388 607) | (0,16 777 215) | 大整数值 |
INT或INTEGER | 4 字节 | (-2 147 483 648,2 147 483 647) | (0,4 294 967 295) | 大整数值 |
BIGINT | 8 字节 | (-9 233 372 036 854 775 808,9 223 372 036 854 775 807) | (0,18 446 744 073 709 551 615) | 极大整数值 |
FLOAT | 4 字节 | (-3.402 823 466 E+38,1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) | 0,(1.175 494 351 E-38,3.402 823 466 E+38) | 单精度 浮点数值 |
DOUBLE | 8 字节 | (1.797 693 134 862 315 7 E+308,2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 双精度 浮点数值 |
DECIMAL | 对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2 | 依赖于M和D的值 | 依赖于M和D的值 | 小数值 |
b.日期和时间类型
表示时间值的日期和时间类型为DATETIME、DATE、TIMESTAMP、TIME和YEAR。
每个时间类型有一个有效值范围和一个"零"值,当指定不合法的MySQL不能表示的值时使用"零"值。
TIMESTAMP类型有专有的自动更新特性,将在后面描述。
类型 | 大小 (字节) | 范围 | 格式 | 用途 |
DATE | 3 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | '-838:59:59'/'838:59:59' | HH:MM:SS | 时间值或持续时间 |
YEAR | 1 | 1901/2155 | YYYY | 年份值 |
DATETIME | 8 | 1000-01-01 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP | 4 | 1970-01-01 00:00:00/2037 年某时 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
c.字符串类型
字符串类型指CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET。该节描述了这些类型如何工作以及如何在查询中使用这些类型。
类型 | 大小 | 用途 |
CHAR | 0-255字节 | 定长字符串 |
VARCHAR | 0-65535 字节 | 变长字符串 |
TINYBLOB | 0-255字节 | 不超过 255 个字符的二进制字符串 |
TINYTEXT | 0-255字节 | 短文本字符串 |
BLOB | 0-65 535字节 | 二进制形式的长文本数据 |
TEXT | 0-65 535字节 | 长文本数据 |
MEDIUMBLOB | 0-16 777 215字节 | 二进制形式的中等长度文本数据 |
MEDIUMTEXT | 0-16 777 215字节 | 中等长度文本数据 |
LONGBLOB | 0-4 294 967 295字节 | 二进制形式的极大文本数据 |
LONGTEXT | 0-4 294 967 295字节 | 极大文本数据 |
CHAR和VARCHAR类型类似,但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。在存储或检索过程中不进行大小写转换。
BINARY和VARBINARY类类似于CHAR和VARCHAR,不同的是它们包含二进制字符串而不要非二进制字符串。也就是说,它们包含字节字符串而不是字符字符串。这说明它们没有字符集,并且排序和比较基于列值字节的数值值。
BLOB是一个二进制大对象,可以容纳可变数量的数据。有4种BLOB类型:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB。它们只是可容纳值的最大长度不同。
有4种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。这些对应4种BLOB类型,有相同的最大长度和存储需求。
三、MySQL常用命令
1.MySQL创建表 create
a.语法
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
b.创建一个student表
create table student(
id int auto_increment,
name char(32) not null,
age int not null,
register_date date not null,
primary key (id));
实例解析:
- 如果你不想字段为 NULL 可以设置字段的属性为 NOT NULL, 在操作数据库时如果输入该字段的数据为NULL ,就会报错。
- AUTO_INCREMENT定义列为自增的属性,一般用于主键,数值会自动加1。
- PRIMARY KEY关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔。
2.MySQL插入数据 insert
a.语法
INSERT INTO table_name ( field1, field2,...fieldN )
VALUES
( value1, value2,...valueN );
b.插入数据
mysql> insert into student (name ,age ,register_date) values("zhangyang",21,"1995-12-26");
mysql> insert into student (name ,age ,register_date) values("GaoRui",4,"2012-05-03");
mysql> insert into student (name ,age ,register_date) values("GaoShuai",1,"2016-10-22");
mysql> insert into student (name ,age ,register_date) values("YoungCheung",21,"1995-12-26");
mysql> select * from student;
+----+-------------+-----+---------------+
| id | name | age | register_date |
+----+-------------+-----+---------------+
| 1 | zhangyang | 21 | 1995-12-26 |
| 2 | GaoRui | 4 | 2012-05-03 |
| 3 | GaoShuai | 1 | 2016-10-22 |
| 4 | YoungCheung | 21 | 1995-12-26 |
+----+-------------+-----+---------------+
4 rows in set (0.00 sec)
3.查询数据select
a.语法
SELECT column_name,column_name
FROM table_name
[WHERE Clause]
[OFFSET M ][LIMIT N]
- 查询语句中你可以使用一个或者多个表,表之间使用逗号(,)分割,并使用WHERE语句来设定查询条件。
- SELECT 命令可以读取一条或者多条记录。
- 你可以使用星号(*)来代替其他字段,SELECT语句会返回表的所有字段数据
- 你可以使用 WHERE 语句来包含任何条件。
- 你可以通过OFFSET指定SELECT语句开始查询的数据偏移量。默认情况下偏移量为0。
- 你可以使用 LIMIT 属性来设定返回的记录数。
mysql> select * from student limit 3 offset 2;
+----+----------+-----+---------------+
| id | name | age | register_date |
+----+----------+-----+---------------+
| 3 | GaoShuai | 1 | 2016-10-22 |
+----+----------+-----+---------------+
1 row in set (0.00 sec)
比如这个SQL ,limit后面跟的是3条数据,offset后面是从第3条开始读取
mysql> select * from student limit 3 ,1;
+----+-------------+-----+---------------+
| id | name | age | register_date |
+----+-------------+-----+---------------+
| 4 | YoungCheung | 21 | 1995-12-26 |
+----+-------------+-----+---------------+
1 row in set (0.00 sec)
而这个SQL,limit后面是从第3条开始读,读取1条信息。
4、MySQL where语句
a.语法
SELECT field1, field2,...fieldN FROM table_name1, table_name2...
[WHERE condition1 [AND [OR]] condition2.....
以下为操作符列表,可用于 WHERE 子句中。
下表中实例假定 A为10 B为20
操作符 | 描述 | 实例 |
= | 等号,检测两个值是否相等,如果相等返回true | (A = B) 返回false。 |
<>, != | 不等于,检测两个值是否相等,如果不相等返回true | (A != B) 返回 true。 |
> | 大于号,检测左边的值是否大于右边的值, 如果左边的值大于右边的值返回true | (A > B) 返回false。 |
< | 小于号,检测左边的值是否小于右边的值, 如果左边的值小于右边的值返回true | (A < B) 返回 true。 |
>= | 大于等于号,检测左边的值是否大于或等于右边的值, 如果左边的值大于或等于右边的值返回true | (A >= B) 返回false。 |
<= | 小于等于号,检测左边的值是否小于于或等于右边的值, 如果左边的值小于或等于右边的值返回true | (A <= B) 返回 true。 |
使用主键来作为 WHERE 子句的条件查询是非常快速的。
mysql> select * from student where register_date > '2016-03-04';
+----+----------+-----+---------------+
| id | name | age | register_date |
+----+----------+-----+---------------+
| 3 | GaoShuai | 1 | 2016-10-22 |
+----+----------+-----+---------------+
1 row in set (0.00 sec)
5、MySQL update语句
a.语法
UPDATE table_name SET field1=new-value1, field2=new-value2 [WHERE Clause]
b.范例更新id大于3的改为Jerry
mysql> update student set age=22 ,name="Jerry" where id>3;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from student;
+----+-----------+-----+---------------+
| id | name | age | register_date |
+----+-----------+-----+---------------+
| 1 | zhangyang | 21 | 1995-12-26 |
| 2 | GaoRui | 4 | 2012-05-03 |
| 3 | GaoShuai | 1 | 2016-10-22 |
| 4 | Jerry | 22 | 1995-12-26 |
+----+-----------+-----+---------------+
6.MySQL delete
a.语法
DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name
[PARTITION (partition_name,...)]
[WHERE where_condition]
[ORDER BY ...]
[LIMIT row_count]
b.删除id为4的列
mysql> delete from student where id =4;
Query OK, 1 row affected (0.01 sec)
mysql>
mysql> select * from student;
+----+-----------+-----+---------------+
| id | name | age | register_date |
+----+-----------+-----+---------------+
| 1 | zhangyang | 21 | 1995-12-26 |
| 2 | GaoRui | 4 | 2012-05-03 |
| 3 | GaoShuai | 1 | 2016-10-22 |
+----+-----------+-----+---------------+
3 rows in set (0.00 sec)
7.MySQL like语句
a.语法
SELECT field1, field2,...fieldN table_name1, table_name2...WHERE field1 LIKE condition1 [AND [OR]] filed2 = 'somevalue'
b.查询姓名中含有G的
mysql> select * from student where name like "G%";
+----+----------+-----+---------------+
| id | name | age | register_date |
+----+----------+-----+---------------+
| 2 | GaoRui | 4 | 2012-05-03 |
| 3 | GaoShuai | 1 | 2016-10-22 |
+----+----------+-----+---------------+
2 rows in set (0.00 sec)
8.MySQL 排序
a.语法
SELECT field1, field2,...fieldN table_name1, table_name2... ORDER BY field1, [field2...] [ASC [DESC]]
使用 ASC 或 DESC 关键字来设置查询结果是按升序或降序排列。 默认情况下,它是按升序排列。
b.排序
mysql> select * from student order by name;
+----+-----------+-----+---------------+
| id | name | age | register_date |
+----+-----------+-----+---------------+
| 2 | GaoRui | 4 | 2012-05-03 |
| 3 | GaoShuai | 1 | 2016-10-22 |
| 1 | zhangyang | 21 | 1995-12-26 |
+----+-----------+-----+---------------+
3 rows in set (0.00 sec)
9.MySQL group by
a.语法
SELECT column_name, function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;
b.示例
mysql> select * from student;
+----+-------------+-----+---------------+
| id | name | age | register_date |
+----+-------------+-----+---------------+
| 1 | zhangyang | 21 | 1995-12-26 |
| 2 | GaoRui | 4 | 2012-05-03 |
| 3 | GaoShuai | 1 | 2016-10-22 |
| 5 | YoungCheung | 21 | 1995-12-26 |
+----+-------------+-----+---------------+
4 rows in set (0.00 sec)
接下来我们使用 GROUP BY 语句 将数据表按名字进行分组,并统计每个人有多少条记录:
mysql> select name,COUNT(*) from student GROUP BY name;
+-------------+----------+
| name | COUNT(*) |
+-------------+----------+
| GaoRui | 1 |
| GaoShuai | 1 |
| YoungCheung | 1 |
| zhangyang | 1 |
+-------------+----------+
4 rows in set (0.01 sec)
使用
WITH
ROLLUP
mysql> select name,sum(age) from student group by name with rollup;
+-------------+----------+
| name | sum(age) |
+-------------+----------+
| CRH | 36 |
| GaoRui | 4 |
| GaoShuai | 1 |
| WangSen | 22 |
| YoungCheung | 21 |
| zhangyang | 21 |
| NULL | 105 |
+-------------+----------+
其中记录 NULL 表示所有人的登录次数。<br>
我们可以使用 coalesce 来设置一个可以取代 NUll 的名称,coalesce 语法:
mysql> SELECT coalesce(name, '总数'), SUM(singin) as singin_count FROM employee_tbl GROUP BY name WITH ROLLUP;
mysql> select coalesce(name,"Total Age"),sum(age) from student group by name with rollup;
+----------------------------+----------+
| coalesce(name,"Total Age") | sum(age) |
+----------------------------+----------+
| CRH | 36 |
| GaoRui | 4 |
| GaoShuai | 1 |
| WangSen | 22 |
| YoungCheung | 21 |
| zhangyang | 21 |
| Total Age | 105 |
+----------------------------+----------+
7 rows in set (0.17 sec)
10.MySQL alter
我们需要修改数据表名或者修改数据表字段时,就需要使用到MySQL ALTER命令。
删除,添加或修改表字段
alter table student drop register_date;
#从student表删除register_date 字段
alter table student add phone int(11) not null;
#添加phone字段
修改字段类型及名称
如果需要修改字段类型及名称, 你可以在ALTER命令中使用 MODIFY 或 CHANGE 子句 。
例如,把字段 c 的类型从 CHAR(1) 改为 CHAR(10),可以执行以下命令:
mysql> ALTER TABLE testalter_tbl MODIFY c CHAR(10);
使用 CHANGE 子句, 语法有很大的不同。 在 CHANGE 关键字之后,紧跟着的是你要修改的字段名,然后指定新字段名及类型。尝试如下实例:
mysql> ALTER TABLE testalter_tbl CHANGE old new BIGINT;
ALTER TABLE 对 Null 值和默认值的影响
当你修改字段时,你可以指定是否包含只或者是否设置默认值。
以下实例,指定字段 j 为 NOT NULL 且默认值为100 。
mysql> ALTER TABLE testalter_tbl
-> MODIFY j BIGINT NOT NULL DEFAULT 100;
修改表名
ALTER TABLE testalter_tbl RENAME TO alter_tbl;
11.关于外键
外键,一个特殊的索引,用于关键2个表,只能是指定内容
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` char(32) NOT NULL,
`age` int(11) NOT NULL,
`register_date` date NOT NULL,
`sex` enum('M','F') DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
CREATE TABLE `study_record` (
`id` int(11) NOT NULL,
`day` int(11) NOT NULL,
`status` char(32) NOT NULL,
`stu_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_student_key` (`stu_id`),
CONSTRAINT `fk_student_key` FOREIGN KEY (`stu_id`) REFERENCES `student` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
此时如果study_record表中不存在id 1,student表也插入不了,这就叫外键约束
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`testdb`.`student2`, CONSTRAINT `fk_class_key` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))
mysql> insert into study_record (day,status,stu_id) values(1,'Yes',2);
Query OK, 1 row affected (0.01 sec)
#如果有student表中跟这个class表有关联的数据,你是不能删除class表中与其关联的纪录的
mysql> delete from student where id =1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`oldboydb`.`study_record`, CONSTRAINT `fk_student_key` FOREIGN KEY (`stu_id`)
12 、MySQL null 处理
我们已经知道MySQL使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作。
为了处理这种情况,MySQL提供了三大运算符:
IS NULL: 当列的值是NULL,此运算符返回true。
IS NOT NULL: 当列的值不为NULL, 运算符返回true。
<=>: 比较操作符(不同于=运算符),当比较的的两个值为NULL时返回true。
关于 NULL 的条件比较运算是比较特殊的。你不能使用 = NULL 或 != NULL 在列中查找 NULL 值 。
在MySQL中,NULL值与任何其它值的比较(即使是NULL)永远返回false,即 NULL = NULL 返回false 。
MySQL中处理NULL使用IS NULL和IS NOT NULL运算符。
13. MySQL 连接(left join, right join, inner join ,full join)
我们已经学会了如果在一张表中读取数据,这是相对简单的,但是在真正的应用中经常需要从多个数据表中读取数据。
本章节我们将向大家介绍如何使用 MySQL 的 JOIN 在两个或多个表中查询数据。
你可以在SELECT, UPDATE 和 DELETE 语句中使用 Mysql 的 JOIN 来联合多表查询。
JOIN 按照功能大致分为如下三类:
- INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。
- LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
- RIGHT JOIN(右连接): 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。
mysql> create table A (a int not null );
mysql> create table B (b int not null );
Suppose you have two tables, with a single column each, and data as follows:
mysql> select * from A ;
+---+
| a |
+---+
| 1 |
| 2 |
| 3 |
| 4 |
+---+
4 rows in set (0.00 sec)
mysql> select * from B ;
+---+
| b |
+---+
| 4 |
| 3 |
| 5 |
| 6 |
| 7 |
+---+
5 rows in set (0.00 sec)
a.Inner join
An inner join using either of the equivalent queries gives the intersection of the two tables, i.e. the two rows they have in common.
mysql> select * from A INNER JOIN B on A.a = B.b ;
+---+---+
| a | b |
+---+---+
| 4 | 4 |
| 3 | 3 |
+---+---+
2 rows in set (0.00 sec)
mysql> select A.* ,B.* from A,B where A.a=B.b;
+---+---+
| a | b |
+---+---+
| 4 | 4 |
| 3 | 3 |
+---+---+
2 rows in set (0.00 sec)
其实就是只显示2个表的交集
2.Left join
A left join will give all rows in A, plus any common rows in B.
mysql> select * from A LEFT JOIN B on A.a = B.b;
+---+------+
| a | b |
+---+------+
| 4 | 4 |
| 3 | 3 |
| 1 | NULL |
| 2 | NULL |
+---+------+
4 rows in set (0.00 sec)
3.Right join
A right join will give all rows in B, plus any common rows in A.
mysql> select * from A RIGHT JOIN B on A.a = B.b;
+------+---+
| a | b |
+------+---+
| 3 | 3 |
| 4 | 4 |
| NULL | 5 |
| NULL | 6 |
| NULL | 7 |
+------+---+
5 rows in set (0.00 sec)
4.Full join
A full outer join will give you the union of A and B, i.e. all the rows in A and all the rows in B. If something in A doesn't have a corresponding datum in B, then the B portion is null, and vice versa
mysql> select * from A FULL JOIN B on A.a = B.b;
ERROR 1054 (42S22): Unknown column 'A.a' in 'on clause'
mysql 并不直接支持full join,but 总是难不到我们
mysql> select * from A left join B on A.a = B.b UNION select * from A right join B on A.a = B.b;
+------+------+
| a | b |
+------+------+
| 4 | 4 |
| 3 | 3 |
| 1 | NULL |
| 2 | NULL |
| NULL | 5 |
| NULL | 6 |
| NULL | 7 |
+------+------+
7 rows in set (0.00 sec)
13.事物
MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!
- 在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务
- 事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行
- 事务用来管理insert,update,delete语句
一般来说,事务是必须满足4个条件(ACID): Atomicity(原子性)、Consistency(稳定性)、Isolation(隔离性)、Durability(可靠性)
- 1、事务的原子性:一组事务,要么成功;要么撤回。
- 2、稳定性 : 有非法数据(外键约束之类),事务撤回。
- 3、隔离性:事务独立运行。一个事务处理后的结果,影响了其他事务,那么其他事务会撤回。事务的100%隔离,需要牺牲速度。
- 4、可靠性:软、硬件崩溃后,InnoDB数据表驱动会利用日志文件重构修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit选项 决定什么时候吧事务保存到日志里。
在Mysql控制台使用事务来操作
mysql> begin; 开始一个事务
Query OK, 0 rows affected (0.00 sec)
mysql> insert into student(name,age,register_date,sex) values('GaoShuai',1,"2016-10-22",'F');
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+----+--------------+-----+---------------+------+
| id | name | age | register_date | sex |
+----+--------------+-----+---------------+------+
| 1 | zhangyang | 21 | 1995-12-26 | M |
| 2 | GaoRui | 4 | 2012-05-03 | F |
| 3 | GaoShuai | 1 | 2016-10-2 2 | M |
+----+--------------+-----+---------------+------+
3 rows in set (0.00 sec)
mysql> rollback; 回滚,这样数据不会写入
Query OK, 0 rows affected (0.01 sec)
mysql> select * from student;
+----+-----------+-----+---------------+------+
| id | name | age | register_date | sex |
+----+-----------+-----+---------------+------+
| 1 | zhangyang | 21 | 1995-12-26 | M |
| 2 | GaoRui | 4 | 2012-05-03 | F |
+----+-----------+-----+---------------+------+
2 rows in set (0.00 sec)
当然如果上面的数据没问题,就输入commit提交命令就行;
14.索引
MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。
打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。
索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索包含多个列。
创建索引时,你需要确保该索引是应用在 SQL 查询语句的条件(一般作为 WHERE 子句的条件)。
实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。
上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。建立索引会占用磁盘空间的索引文件。
普通索引
a.创建索引
这是最基本的索引,它没有任何限制。它有以下几种创建方式:
CREATE INDEX indexName ON mytable(username(length));
mysql> CREATE INDEX index_name on student(name(32));
如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length。
b.修改表结构
ALTER mytable ADD INDEX [indexName] ON (username(length))
c.创建表的时候直接指定
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);
d.删除索引的方法
DROP INDEX [indexName] ON mytable;
唯一索引
它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
a.创建索引
创建索引
CREATE UNIQUE INDEX indexName ON mytable(username(length))
修改表结构
ALTER mytable ADD UNIQUE [indexName] ON (username(length))
创建表的时候直接指定
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
UNIQUE [indexName] (username(length))
b. 使用ALTER 命令添加和删除索引
有四种方式来添加数据表的索引:
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。
ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出现多次。
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):该语句指定了索引为 FULLTEXT ,用于全文索引。
以下实例为在表中添加索引。
mysql> ALTER TABLE testalter_tbl ADD INDEX (c);
你还可以在 ALTER 命令中使用 DROP 子句来删除索引。尝试以下实例删除索引:
mysql> ALTER TABLE testalter_tbl DROP INDEX (c);
View Code
c.显示索引信息
SHOW INDEX FROM table_name\G
SHOW INDEX FROM table_name;
mysql练习题
更多mysql知识,请看
14. python 操作mysql
python-mysqldb :
pymysql :
对于Python操作MySQL主要使用两种方式:
- 原生模块 pymsql
- ORM框架 SQLAchemy
pymysql
1.安装
pip install pymysql
2.使用操作
a.执行SQL
#!/usr/bin/env python
# -*- conding:utf-8 -*-
__Author__ = "YoungCheung"
import pymysql
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='oldboydb')
# 创建游标http://idea.lanyus.com
cursor = conn.cursor()
# 执行SQL,并返回收影响行数
#effect_row = cursor.execute("select * from student")
data = [
("N1",12,'2015-05-22','M'),
("N2",13,'2015-05-23','M'),
("N3",14,'2015-05-25','F')
]
cursor.executemany("insert into student (name,age,register_date,sex) values (%s,%s,%s,%s)",data)
# 提交,不然无法保存新建或者修改的数据
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
#打印1个内容
# print(cursor.fetchone())
# print(cursor.fetchone())
#打印所有内容
# print(cursor.fetchall())
# 获取前n行数据
# row_2 =
cursor
.fetchmany(3)
15. ORM sqlachemy学习
- ORM介绍
- sqlalchemy安装
- sqlalchemy基本使用
- 多外键关联
- 多对多关系
- 表结构设计作业
1.ORM介绍
orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。
orm的优缺点
优点:
- 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
- ORM使我们构造固化数据结构变得简单易行。
缺点:
- 无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)。现在的各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。
2.sqlalchemy安装
在Python中,最有名的ORM框架是SQLAlchemy。用户包括openstack\Dropbox等知名公司或应用,主要用户列表http://www.sqlalchemy.org/organizations.html#openstack
Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
MySQL-Python
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
pymysql
mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
MySQL-Connector
mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
cx_Oracle
oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html
安装sqlalchemy:
pip install SQLAlchemy
pip install pymysql
#由于mysqldb依然不支持py3,所以这里我们用pymysql与sqlalchemy交互
3.sqlalchemy基本使用
之前我们创建一个表是这样的
CREATE TABLE user (
id INTEGER NOT NULL AUTO_INCREMENT,
name VARCHAR(32),
password VARCHAR(64),
PRIMARY KEY (id)
)
这只是最简单的sql表,如果再加上外键关联什么的,一般程序员的脑容量是记不住那些sql语句的,于是有了orm,实现上面同样的功能,代码如下
#!/usr/bin/env python
# -*- conding:utf-8 -*-
__Author__ = "YoungCheung"
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboydb",
encoding = 'utf-8',echo=True) #echo = Ture 打印创建过程
Base = declarative_base() #生成orm基类
class User(Base):
__tablename__ = 'user' #表名
id = Column(Integer, primary_key=True)
name = Column(String(32))
password = Column(String(64))
Base.metadata.create_all(engine) #创建表结构
说,娘那个腚的,并没有感觉代码量变少啊,呵呵, 孩子莫猴急,好戏在后面
Lazy Connecting
The Engine, when first returned by create_engine(), has not actually tried to connect to the database yet; that happens only the first time it is asked to perform a task against the database.
除上面的创建之外,还有一种创建表的方式,虽不学用,但还是看看吧
#!/usr/bin/env python
# -*- conding:utf-8 -*-
__Author__ = "YoungCheung"
from sqlalchemy import Table, MetaData, Column, Integer, String, ForeignKey
from sqlalchemy.orm import mapper
metadata = MetaData()
user = Table('user', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
Column('fullname', String(50)),
Column('password', String(12))
)
class User(object):
def __init__(self, name, fullname, password):
self.name = name
self.fullname = fullname
self.password = password
mapper(User, user) #the table metadata is created separately with the Table construct, then associated with the User class via the mapper() function
最基本的表我们创建好了,那我们开始用orm创建一条数据试试
#!/usr/bin/env python
# -*- conding:utf-8 -*-
__Author__ = "YoungCheung"
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboydb",
encoding='utf-8', echo=True)
Base = declarative_base() #生成orm基类
class User(Base):
__tablename__ = 'user' #表名
id = Column(Integer, primary_key=True)
name = Column(String(32))
password = Column(String(64))
Base.metadata.create_all(engine) #创建表结构
Session_class = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
Session = Session_class() #生成session实例
user_obj = User(name="zy",password="123456") #生成你要创建的数据对象
user_obj2 = User(name="jack",password="123456") #生成你要创建的数据对象
print(user_obj.name,user_obj.id) #此时还没创建对象呢,不信你打印一下id发现还是None
Session.add(user_obj) #把要创建的数据对象添加到这个session里, 一会统一创建
Session.add(user_obj2) #把要创建的数据对象添加到这个session里, 一会统一创建
print(user_obj.name,user_obj.id) #此时也依然还没创建
Session.commit() #现此才统一提交,创建数据
我擦,写这么多代码才创建一条数据,你表示太tm的费劲了,正要转身离开,我拉住你的手不放开,高潮还没到。。
a.查询
#!/usr/bin/env python
# -*- conding:utf-8 -*-
__Author__ = "YoungCheung"
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboydb",
encoding='utf-8')
Base = declarative_base() #生成orm基类
class User(Base):
__tablename__ = 'user' #表名
id = Column(Integer, primary_key=True)
name = Column(String(32))
password = Column(String(64))
def __repr__(self):
return '<%s name:%s >' %(self.id,self.name)
Base.metadata.create_all(engine) #创建表结构
Session_class = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
Session = Session_class() #生成session实例
#查询
#data = Session.query(User).filter_by(name='jack').all()
data = Session.query(User).filter_by(name='jack').first()
data = Session.query(User).filter(User.id>0).all() #
#print(data[0].name,data[0].password) print(data)
b.修改
data = Session.query(User).filter_by(name='zy').first()
data.name = "root"
data.password = "123456"
Session.commit()
c.回滚
data = Session.query(User).filter_by(id = 1).first()
#data.name = "YoungCheung"
fake_user = User(name = "Rain",password='654321')
Session.add(fake_user)
print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() )
Session.rollback() #此时你rollback一下
print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) #再查就发现刚才添加的数据没有了。
d.获取所有数据
print(Session.query(User.name,User.id).all() )
e.多条件查询
objs = Session.query(User).filter(User.id>0).filter(User.id<7).all()
f.统计和分组
#统计和分组
#统计
# objs = Session.query(User).filter(User.id>0).filter(User.id<7).count()
# print(objs)
#分组
print(Session.query(func.count(User.name),User.name).group_by(User.name).all() ) #注意导入模块 from sqlalchemy import func
相当于:
SELECT count(user.name) AS count_1, user.name AS user_name
FROM user GROUP BY user.name
输出:
[(1, 'jack'), (1, 'root')]
f.连表查询
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Enum,DATE,Integer, String
from sqlalchemy.orm import sessionmaker
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboydb",
encoding = 'utf-8') #echo = Ture 打印创建过程
Base = declarative_base() # 生成orm基类
class User(Base):
__tablename__ = 'user' # 表名
id = Column(Integer, primary_key=True)
name = Column(String(32))
password = Column(String(64))
def __repr__(self):
return "<%s name:%s>" % (self.id,self.name)
class Student(Base):
__tablename__ = "student"
id = Column(Integer, primary_key=True)
age = Column(Integer,nullable=False)
name = Column(String(32),nullable=False)
register_date = Column(DATE,nullable=False)
sex = Column(String(32),nullable=False)
def __repr__(self):
return "<%s name:%s>" % (self.id, self.name)
Base.metadata.create_all(engine) # 创建表结构
Session_class = sessionmaker(bind=engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
Session = Session_class() # 生成session实例 #cursor
print(Session.query(User,Student).filter(User.id==Student.id).all())
# print(Session.query(User).join(Student,isouter=True).all())
外键关联
我们创建一个addresses表,跟user表关联
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
email_address = Column(String(32), nullable=False)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship("User", backref="addresses") #这个nb,允许你在user表里通过backref字段反向查出所有它在addresses表里的关联项
def __repr__(self):
return "<Address(email_address='%s')>" % self.email_address
The relationship.back_populates parameter is a newer version of a very common SQLAlchemy feature calledrelationship.backref. The relationship.backref parameter hasn’t gone anywhere and will always remain available! The relationship.back_populates is the same thing, except a little more verbose and easier to manipulate. For an overview of the entire topic, see the section Linking Relationships with Backref.
实例
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Enum,DATE,Integer, String,ForeignKey
from sqlalchemy.orm import sessionmaker,relationship
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboydb",
encoding = 'utf-8') #echo = Ture 打印创建过程
Base = declarative_base() # 生成orm基类
class Student(Base):
__tablename__ = "student"
id = Column(Integer, primary_key=True)
name = Column(String(32),nullable=False)
register_date = Column(DATE,nullable=False)
def __repr__(self):
return "<%s name:%s>" % (self.id, self.name)
class StudyRecord(Base):
__tablename__ = "study_record"
id = Column(Integer, primary_key=True)
day = Column(Integer,nullable=False)
status = Column(String(32),nullable=False)
stu_id = Column(Integer,ForeignKey("student.id"))
#study_obj = query(id=1)
#student = query(Student).filter(Student.id == stu_obj.stu_id).first()
student = relationship("Student", backref="my_study_record") #这个nb,允许你在user表里通过backref字段反向查出所有它在addresses表里的关联项
def __repr__(self):
return "<%s day:%s status:%s>" % (self.student.name, self.day,self.status)
Base.metadata.create_all(engine) # 创建表结构
Session_class = sessionmaker(bind=engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
session = Session_class() # 生成session实例 #cursor
# #创建学员
# s1 = Student(name="Alex",register_date="2014-05-21")
# s2 = Student(name="Jack",register_date="2014-03-21")
# s3 = Student(name="Rain",register_date="2014-02-21")
# s4 = Student(name="Eric",register_date="2013-01-21")
# #添加记录
# study_obj1 = StudyRecord(day=1,status="YES", stu_id=1)
# study_obj2 = StudyRecord(day=2,status="NO", stu_id=1)
# study_obj3 = StudyRecord(day=3,status="YES", stu_id=1)
# study_obj4 = StudyRecord(day=1,status="YES", stu_id=2)
# #
# session.add_all([s1,s2,s3,s4,study_obj1,study_obj2,study_obj3,study_obj4])
# session.commit()
stu_obj = session.query(Student).filter(Student.name=="Jack").first()
print(stu_obj.my_study_record)
4.多外键关联
One of the most common situations to deal with is when there are more than one foreign key path between two tables.
Consider a Customer
class that contains two foreign keys to an Address
class:
下表中,Customer表有2个字段都关联了Address表
from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class Customer(Base):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True)
name = Column(String(64))
billing_address_id = Column(Integer, ForeignKey("address.id"))
shipping_address_id = Column(Integer, ForeignKey("address.id"))
billing_address = relationship("Address", foreign_keys=[billing_address_id])
shipping_address = relationship("Address", foreign_keys=[shipping_address_id])
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
street = Column(String(64))
city = Column(String(64))
state = Column(String(64))
def __repr__(self):
return self.street
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboydb",
encoding = 'utf-8') #echo = Ture 打印创建过程
Base.metadata.create_all(engine) # 创建表结构
API
import orm_many_fk
from sqlalchemy.orm import sessionmaker
Session_class = sessionmaker(bind=orm_many_fk.engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
session = Session_class() # 生成session实例 #cursor
'''
addr1 = orm_many_fk.Address(street="TiYuGuan", city="ChaoYang", state="BJ")
addr2 = orm_many_fk.Address(street="ChangNingXian", city="YB", state="SC")
addr3 = orm_many_fk.Address(street="Yanjiao", city="LangFang", state="HB")
session.add_all([addr1,addr2,addr3])
c1 = orm_many_fk.Customer(name="Jerry", billing_address= addr1,shipping_address=addr2)
c2 = orm_many_fk.Customer(name="Jack", billing_address= addr3,shipping_address=addr3)
session.add_all([c1,c2])
'''
obj = session.query(orm_many_fk.Customer).filter(orm_many_fk.Customer.name=="jerry").first()
print(obj.name,obj.billing_address,obj.shipping_address)
session.commit()
5.多对多关系
现在来设计一个能描述“图书”与“作者”的关系的表结构,需求是
- 一本书可以有好几个作者一起出版
- 一个作者可以写好几本书
此时你会发现,用之前学的外键好像没办法实现上面的需求了,因为
当然你更不可以像下面这样干,因为这样就你就相当于有多条书的记录了,太low b了,改书名还得都改。。。
那怎么办呢? 此时,我们可以再搞出一张中间表,就可以了
这样就相当于通过book_m2m_author表完成了book表和author表之前的多对多关联
用orm如何表示呢?
from sqlalchemy import Table, Column, Integer,String,DATE, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
book_m2m_author = Table('book_m2m_author', Base.metadata,
Column('book_id',Integer,ForeignKey('books.id')),
Column('author_id',Integer,ForeignKey('authors.id')),
)
class Book(Base):
__tablename__ = 'books'
id = Column(Integer,primary_key=True)
name = Column(String(64))
pub_date = Column(DATE)
authors = relationship('Author',secondary=book_m2m_author,backref='books')
def __repr__(self):
return self.name
class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
name = Column(String(32))
def __repr__(self):
return self.name
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboydb?charset=utf8",
)
Base.metadata.create_all(engine) # 创建表结构
orm_m2m.py
import orm_m2m
from sqlalchemy.orm import sessionmaker
Session_class = sessionmaker(bind=orm_m2m.engine) # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
session = Session_class() # 生成session实例 #cursor
'''
#创建书
b1 = orm_m2m.Book(name="learn python with Alex",pub_date="2014-05-2")
b2= orm_m2m.Book(name="learn Zhuangbility with Alex",pub_date="2015-05-2")
b3 = orm_m2m.Book(name="跟Alex去泰国",pub_date="2016-05-2")
#创建作者
a1 = orm_m2m.Author(name="Alex")
a2 = orm_m2m.Author(name="Jack")
a3 = orm_m2m.Author(name="Rain")
#关联
b1.authors = [a1,a3]
b2.authors = [a2,a3]
b3.authors = [a1,a2,a3]
session.add_all([b1,b2,b3,a1,a2,a3])
session.commit()
'''
#查询
author_obj = session.query(orm_m2m.Author).filter(orm_m2m.Author.name=="alex").first()
print(author_obj.books[1].pub_date)
book_obj = session.query(orm_m2m.Book).filter(orm_m2m.Book.id==2).first()
print(book_obj.authors)
orm_m2m_api
多对多删除
删除数据时不用管boo_m2m_authors , sqlalchemy会自动帮你把对应的数据删除
通过书删除作者:
book_obj.authors.remove(author_obj)
session.commit()
直接删除作者
删除作者时,会把这个作者跟所有书的关联关系数据也自动删除
author_obj =s.query(Author).filter_by(name="Alex").first()
# print(author_obj.name , author_obj.books)
s.delete(author_obj)
s.commit()
6.中文讲解
engine = create_engine("mysql+pymysql://root:123456@localhost/oldboydb?charset=utf8",
)
源代码
``dialect[+driver]://user:password@host/dbname[?key=value..]``, where
``dialect`` is a database name such as ``mysql``, ``oracle``,
``postgresql``, etc., and ``driver`` the name of a DBAPI, such as
``psycopg2``, ``pyodbc``, ``cx_oracle``, etc. Alternatively,
the URL can be an instance of :class:`~sqlalchemy.engine.url.URL`.
源代码
7.本节作业
主题:学员管理系统
需求:
- 用户角色,讲师\学员, 用户登陆后根据角色不同,能做的事情不同,分别如下
- 讲师视图
- 管理班级,可创建班级,根据学员qq号把学员加入班级
- 可创建指定班级的上课纪录,注意一节上课纪录对应多条学员的上课纪录, 即每节课都有整班学员上, 为了纪录每位学员的学习成绩,需在创建每节上课纪录是,同时 为这个班的每位学员创建一条上课纪录
- 为学员批改成绩, 一条一条的手动修改成绩
- 学员视图
- 提交作业
- 查看作业成绩
- 一个学员可以同时属于多个班级,就像报了Linux的同时也可以报名Python一样, 所以提交作业时需先选择班级,再选择具体上课的节数
- 附加:学员可以查看自己的班级成绩排名
-