目录:
数据库介绍
mysql 数据库安装使用
mysql管理
mysql 数据类型
常用mysql命令事务索引
创建数据库
外键
增删改查表
权限
python 操作mysql
ORM sqlachemy学习
内容:
1、数据库介绍
什么是数据库?
数据库是按照数据结构来组织、存储和管理数据的仓库,每个数据库都有一个或多个不同的API用于创建、访问、管理、搜索和复制所保存的数据,我们也可以将数据存储在文件中,但是文件读写数据速度相对较慢,所以现在我们使用关系型数据库管理系统RDBMS来存储和管理大数据量。所谓的关系型数据库,是建立在关系模型基础上的数据库,借助于集合代数等数学概念呢和方法来处理数据库中的数据。
RDBMS即关系数据库管理系统的特点:
1、数据以表格的形式出现
2、每行为各种记录名称
3、每列为记录名称所对应的数据域
4、许多的行和列组成了一张表单
5、若干的表单组成database
下图就是一个数据库:
RDBMS术语
在我们开始学习MySQL数据库前,先了解下RDBMS的一些术语:
数据库:数据库是一些关联表的集合
数据表:表是数据的矩阵,在一个数据库中的表看起来像一个简单的电子表格
列:一列数据元素包含了相同的数据,例如邮政编码的数据
行:一行=元组,或者记录 是一组相关的数据,例如一条用户订阅的数据
冗余:存储两倍的数据,冗余可以使系统速度更快,表的规范化程度越高,表与表之间的关系就越多,查询时可能经常需要在多个表之间进行连接查询,而进行连接操作会降低宣传速度,例如学生的信息存在student表中,院系信息存储在department表中,通过student表中的dept_id字段与department表建立关联,如果要查询一个学生所在系的名称,必须从student表中查询学生所在院系的编号dept_id,然后根据这个编号去department查找洗的名称,如果经常需要进行这个操作,链接查询会浪费很多时间,因此可以在student中增加一个冗余字段dept_name,该字段用来查询学生所在院系名称,这样就不用每次都进行连接操作了
主键:主键是稳一的,一个数据表中只能包含一个主键,可以使用主键来查询数据
外键:外键用于关联两个表
复合键:复合键也叫组合键,将多个列作为一个索引键,一般用于复合索引
索引:使用索引可快速访问数据库表中的特定信息,索引是对数据库表中一列活多列的值进行排序的一种结构,类似于书籍的目录
参照完整性:参照的完整性要求关系中不允许引用不存在的实体,与实体完整性关系模型必须满足完整性约束条件,目的是保证数据的一致性
MySQL数据库
MySQL是最流行的关系型数据库管理系统,在web应用方面是MySQL是最好的RDSMS(Relational Database Management System):关系数据库管理系统应用软件之一,由瑞典MySQL AB公司开发,目前属于Oracle公司,MySQL是一种关联数据库管理系统,关联数据库将数据保存再不同表中,而不是将所有的数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
MySQL是开源的,所以不需要支付任何额外的费用
MYSQL支持大型的数据库,可以处理上千万条记录
MySQL使用标准的SQL数据语言形式
MySQL可以允许多个系统上,支持多语言这些编程语言包括C C++ python java perl php eiffel ruby等
MySQL对PHP有很好的支持,PHP是目前最流行的web开发语言
MySQL可以定制,采用GPL协议,可以修改源码来开发自己的系统
2、MySQL的使用安装
Linux/UNIX上安装Mysql
Linux平台上推荐使用RPM包来安装Mysql,MySQL AB提供了以下RPM包的下载地址:
MySQL - MySQL服务器。你需要该选项,除非你只想连接运行在另一台机器上的MySQL服务器。
MySQL-client - MySQL 客户端程序,用于连接并操作Mysql服务器。
MySQL-devel - 库和包含文件,如果你想要编译其它MySQL客户端,例如Perl模块,则需要安装该RPM包。
MySQL-shared - 该软件包包含某些语言和应用程序需要动态装载的共享库(libmysqlclient.so*),使用MySQL。
MySQL-bench - MySQL数据库服务器的基准和性能测试工具。
以下安装Mysql RMP的实例是在SuSE Linux系统上进行,当然该安装步骤也适合应用于其他支持RPM的Linux系统,如:Centos。
安装步骤如下:
使用root用户登陆你的Linux系统。
通过以下命令执行Mysql安装,rpm包为你下载的rpm包:
[root@host]#rpm -i MySQL-5.0.9-0.i386.rpm
以上安装mysql服务器的过程会创建mysql用户,并创建一个mysql配置文件my.cnf。
你可以在/usr/bin和/usr/sbin中找到所有与MySQL相关的二进制文件。所有数据表和数据库将在/var/lib/mysql目录中创建。
以下是一些mysql可选包的安装过程,你可以根据自己的需要来安装:
1 [root@host]#rpm -i MySQL-client-5.0.9-0.i386.rpm
2 [root@host]#rpm -i MySQL-devel-5.0.9-0.i386.rpm
3 [root@host]#rpm -i MySQL-shared-5.0.9-0.i386.rpm
4 [root@host]#rpm -i MySQL-bench-5.0.9-0.i386.rpm
Window上安装Mysql
Window上安装Mysql相对来说会较为简单,你只需要载 MySQL 下载中下载window版本的mysql安装包,并解压安装包。
双击 setup.exe 文件,接下来你只需要安装默认的配置点击"next"即可,默认情况下安装信息会在C:\mysql目录中。
接下来你可以通过"开始" =》在搜索框中输入 " cmd" 命令 =》 在命令提示符上切换到 C:\mysql\bin 目录,并输入一下命令:
1 mysqld.exe --console
如果安装成功以上命令将输出一些mysql启动及InnoDB信息。
验证Mysql安装
在成功安装Mysql后,一些基础表会表初始化,在服务器启动后,你可以通过简单的测试来验证Mysql是否工作正常。
使用 mysqladmin 工具来获取服务器状态:
使用 mysqladmin 命令俩检查服务器的版本,在linux上该二进制文件位于 /usr/bin on linux ,在window上该二进制文件位于C:\mysql\bin
[root@host]#mysqladmin --version
如果以上命令执行后未输入任何信息,说明你的Mysql未安装成功。
使用 MySQL Client(Mysql客户端) 执行简单的SQL命令
你可以在 MySQL Client(Mysql客户端) 使用 mysql 命令连接到Mysql服务器上,默认情况下Mysql服务器的密码为空,所以本实例不需要输入密码。
命令如下:
[root@host]#mysql
Mysql安装后需要做的
Mysql安装成功后,默认的root用户密码为空,你可以使用以下命令来创建root用户的密码:
[root@host]#mysqladmin -u root password "new_password";
注意:在输入密码时,密码是不会显示了,你正确输入即可。
Linux系统启动时启动 MySQL
如果你需要在Linux系统启动时启动 MySQL 服务器,你需要在 /etc/rc.local 文件中添加以下命令:
/etc/init.d/mysqld start
同样,你需要将 mysqld 二进制文件添加到 /etc/init.d/ 目录中。
3、MySQL管理
启动及关闭 MySQL 服务器
首先,我们需要通过以下命令来检查MySQL服务器是否启动:
ps -ef | grep mysqld
如果MySql已经启动,以上命令将输出mysql进程列表, 如果mysql未启动,你可以使用以下命令来启动mysql服务器:
root@host#cd /usr/bin
./mysqld_safe &
如果你想关闭目前运行的 MySQL 服务器, 你可以执行以下命令:
1 root@host#cd /usr/bin
2 ./mysqladmin -u root -p shutdown3 Enter password: ******
MySQL 用户设置
如果你需要添加 MySQL 用户,你只需要在 mysql 数据库中的 user 表添加新用户即可。
以下为添加用户的的实例,用户名为guest,密码为guest123,并授权用户可进行 SELECT, INSERT 和 UPDATE操作权限:
1 root@host#mysql -u root -p
2 Enter password:*******
3 mysql>use mysql;4 Database changed5
6 mysql>INSERT INTO user7 (host, user, password,8 select_priv, insert_priv, update_priv)9 VALUES ('localhost', 'guest',10 PASSWORD('guest123'), 'Y', 'Y', 'Y');11 Query OK, 1 row affected (0.20sec)12
13 mysql>FLUSH PRIVILEGES;14 Query OK, 1 row affected (0.01sec)15
16 mysql> SELECT host, user, password FROM user WHERE user = 'guest';17 +-----------+---------+------------------+
18 | host | user | password |
19 +-----------+---------+------------------+
20 | localhost | guest | 6f8c114b58f2ce9e |
21 +-----------+---------+------------------+
22 1 row in set (0.00 sec)
在添加用户时,请注意使用MySQL提供的 PASSWORD() 函数来对密码进行加密。 你可以在以上实例看到用户密码加密后为: 6f8c114b58f2ce9e.在添加用户时,请注意使用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 。
1 root@host#mysql -u root -p password;
2 Enter password:*******
3 mysql>use mysql;4 Database changed5
6 mysql>GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP7 -> ON TUTORIALS.*
8 -> TO 'zara'@'localhost'
9 -> IDENTIFIED BY 'zara123';
/etc/my.cnf 文件配置
一般情况下,你不需要修改该配置文件,该文件默认配置如下:
1 [mysqld]2 datadir=/var/lib/mysql3 socket=/var/lib/mysql/mysql.sock4
5 [mysql.server]6 user=mysql7 basedir=/var/lib8
9 [safe_mysqld]10 err-log=/var/log/mysqld.log11 pid-file=/var/run/mysqld/mysqld.pid
管理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支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。
数值类型
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的值
小数值
日期和时间类型
表示时间值的日期和时间类型为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
混合日期和时间值,时间戳
字符串类型
字符串类型指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类型,有相同的最大长度和存储需求。
5. mysql 常用命令
MySQL 创建数据表
语法
CREATE TABLE table_name (column_name column_type);
创建一个student表
1 create table student(2 stu_id INT NOT NULL AUTO_INCREMENT,3 name CHAR(32) NOT NULL,4 age INT NOT NULL,5 register_date DATE,6 PRIMARY KEY ( stu_id )7 );
实例解析:
如果你不想字段为 NULL 可以设置字段的属性为 NOT NULL, 在操作数据库时如果输入该字段的数据为NULL ,就会报错。
AUTO_INCREMENT定义列为自增的属性,一般用于主键,数值会自动加1。
PRIMARY KEY关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔。
MySQL 插入数据
语法
INSERT INTO table_name ( field1, field2,...fieldN )
VALUES
( value1, value2,...valueN );
插入数据
mysql> insert into student (name,age,register_date) values ("alex li",22,"2016-03-4")->;
Query OK,1 row affected (0.00sec)
mysql> select * fromstudent;+--------+---------+-----+---------------+
| stu_id | name | age | register_date |
+--------+---------+-----+---------------+
| 1 | alex li | 22 | 2016-03-04 |
+--------+---------+-----+---------------+
1 row in set (0.00 sec)
MySQL 查询数据
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;+--------+---------+-----+---------------+
| stu_id | name | age | register_date |
+--------+---------+-----+---------------+
| 3 | alex li | 24 | 2016-03-04 |
| 4 | alex li | 24 | 2016-03-01 |
| 5 | alex li | 24 | 2016-03-02 |
+--------+---------+-----+---------------+
3 rows in set (0.00sec)
比如这个SQL ,limit后面跟的是3条数据,offset后面是从第3条开始读取
mysql> select * from student limit 3 ,1;+--------+---------+-----+---------------+
| stu_id | name | age | register_date |
+--------+---------+-----+---------------+
| 4 | alex li | 24 | 2016-03-01 |
+--------+---------+-----+---------------+
1 row in set (0.00sec)
而这个SQL,limit后面是从第3条开始读,读取1条信息。
MySQL where 子句
语法
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 子句的条件查询是非常快速的。
select * from student where register_date > '2016-03-04';
MySQL UPDATE 查询
语法
UPDATE table_name SET field1=new-value1, field2=new-value2
[WHERE Clause]1update student set age=22 ,name="Alex Li" where stu_id>3;
MySQL DELETE 语句
语法
DELETE FROM table_name [WHERE Clause]
delete from student where stu_id=5;
MySQL LIKE 子句
语法
SELECT field1, field2,...fieldN table_name1, table_name2...
WHERE field1 LIKE condition1 [AND [OR]] filed2= 'somevalue'select*from student where name binary like "%Li";
select*from student where name binary like binary "%Li"; #只匹配大写
MySQL GROUP BY 语句
SELECT column_name, function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name;
示例
mysql> SELECT *FROM employee_tbl;+----+--------+---------------------+--------+
| id | name | date | singin |
+----+--------+---------------------+--------+
| 1 | 小明 | 2016-04-22 15:25:33 | 1 |
| 2 | 小王 | 2016-04-20 15:25:47 | 3 |
| 3 | 小丽 | 2016-04-19 15:26:02 | 2 |
| 4 | 小王 | 2016-04-07 15:26:14 | 4 |
| 5 | 小明 | 2016-04-11 15:26:40 | 4 |
| 6 | 小明 | 2016-04-04 15:26:54 | 2 |
+----+--------+---------------------+--------+接下来我们使用 GROUP BY 语句 将数据表按名字进行分组,并统计每个人有多少条记录:
mysql> SELECT name, COUNT(*) FROM employee_tbl GROUP BY name;+--------+----------+
| name | COUNT(*) |
+--------+----------+
| 小丽 | 1 |
| 小明 | 3 |
| 小王 | 2 |
+--------+----------+
3 rows in set (0.01sec)
使用 WITH ROLLUP
mysql>SELECT name, SUM(singin) as singin_count FROM employee_tbl GROUP BY name WITH ROLLUP;+--------+--------------+
| name | singin_count |
+--------+--------------+
| 小丽 | 2 |
| 小明 | 7 |
| 小王 | 7 |
| NULL | 16 |
+--------+--------------+
4 rows in set (0.00sec)
其中记录 NULL 表示所有人的登录次数。
我们可以使用 coalesce 来设置一个可以取代 NUll 的名称,coalesce 语法:
mysql> SELECT coalesce(name, '总数'), SUM(singin) as singin_count FROM employee_tbl GROUP BY name WITH ROLLUP;
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 i j BIGINT;
mysql> ALTER TABLE testalter_tbl CHANGE j j INT;
ALTER TABLE 对 Null 值和默认值的影响
当你修改字段时,你可以指定是否包含只或者是否设置默认值。
以下实例,指定字段 j 为 NOT NULL 且默认值为100 。
mysql>ALTER TABLE testalter_tbl-> MODIFY j BIGINT NOT NULL DEFAULT 100;
修改表名
mysql> ALTER TABLE testalter_tbl RENAME TO alter_tbl;
关于主键
外键,一个特殊的索引,用于关键2个表,只能是指定内容
mysql> create table class(-> id int notnull primary key,-> name char(16));
Query OK, 0 rows affected (0.02sec)
CREATE TABLE `student2` (
`id` int(11) NOT NULL,
`name` char(16) NOT NULL,
`class_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_class_key` (`class_id`),
CONSTRAINT `fk_class_key` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`)
)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16此时如果class 表中不存在id1,student表也插入不了,这就叫外键约束
mysql> insert into student2(id,name,class_id) values(1,'alex', 1);
ERROR1452 (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 class(id,name) values(1,"linux");
Query OK,1 row affected (0.01sec)
mysql> insert into student2(id,name,class_id) values(1,'alex', 1);
Query OK,1 row affected (0.00sec)
#如果有student表中跟这个class表有关联的数据,你是不能删除class表中与其关联的纪录的
mysql> delete from class where id =1;
ERROR1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`testdb`.`student2`, CONSTR
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运算符。
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 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。
Suppose you have two tables, with a single column each, and data as follows:
A B- -
1 3
2 4
3 5
4 6
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.
select * from a INNER JOIN b on a.a =b.b;
select a.*,b.* from a,b where a.a =b.b;
a|b--+--
3 | 3
4 | 4
其实就是只显示2个表的交集
Left join
A left join will give all rows in A, plus any common rows in B.
select * from a LEFT JOIN b on a.a =b.b;
a|b--+-----
1 |null2 |null3 | 3
4 | 4
Right join
A right join will give all rows in B, plus any common rows in A.
select * from a RIGHT JOIN b on a.a =b.b;
a|b-----+----
3 | 3
4 | 4null| 5null| 6
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
select * from a FULL JOIN b on a.a =b.b;
a|b-----+-----
1 |null2 |null3 | 3
4 | 4null| 6null| 5
mysql 并不直接支持full join,but 总是难不到我们
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 |
+------+------+
| 3 | 3 |
| 4 | 4 |
| 1 | NULL |
| 2 | NULL |
| NULL | 5 |
| NULL | 6 |
+------+------+
6 rows in set (0.00 sec)
6. 事务
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; #开始一个事务
mysql> insert into a (a) values(555);
mysql>rollback; 回滚 , 这样数据是不会写入的
当然如果上面的数据没问题,就输入commit提交命令就行;
7.索引
MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。
打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。
索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索包含多个列。
创建索引时,你需要确保该索引是应用在 SQL 查询语句的条件(一般作为 WHERE 子句的条件)。
实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。
上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。建立索引会占用磁盘空间的索引文件。
普通索引
创建索引
这是最基本的索引,它没有任何限制。它有以下几种创建方式:
CREATE INDEX indexName ON mytable(username(length));
如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length。
修改表结构
ALTER mytable ADD INDEX [indexName] ON (username(length))
创建表的时候直接指定
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);
删除索引的语法
DROP INDEX [indexName] ON mytable;
唯一索引
它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
创建索引
创建索引
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))
);
使用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);
使用 ALTER 命令添加和删除主键
主键只能作用于一个列上,添加主键索引时,你需要确保该主键默认不为空(NOT NULL)。实例如下:
mysql>ALTER TABLE testalter_tbl MODIFY i INT NOT NULL;
mysql>ALTER TABLE testalter_tbl ADD PRIMARY KEY (i);
你也可以使用 ALTER 命令删除主键:
mysql>ALTER TABLE testalter_tbl DROP PRIMARY KEY;
删除指定时只需指定PRIMARY KEY,但在删除索引时,你必须知道索引名。
显示索引信息
mysql> SHOW INDEX FROM table_name\G
mysql练习题 http://www.cnblogs.com/wupeiqi/articles/5729934.html
更多mysql知识,请看http://www.cnblogs.com/wupeiqi/articles/5713323.html
8. python 操作mysql
pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同。
下载安装
pip3 install pymysql
使用操作
1、执行SQL
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 importpymysql4
5 #创建连接
6 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')7 #创建游标
8 cursor =conn.cursor()9
10 #执行SQL,并返回收影响行数
11 effect_row = cursor.execute("update hosts set host = '1.1.1.2'")12
13 #执行SQL,并返回受影响行数
14 #effect_row = cursor.execute("update hosts set host = '1.1.1.2' where nid > %s", (1,))
15
16 #执行SQL,并返回受影响行数
17 #effect_row = cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
18
19
20 #提交,不然无法保存新建或者修改的数据
21 conn.commit()22
23 #关闭游标
24 cursor.close()25 #关闭连接
26 conn.close()
View Code
2、获取新创建数据自增ID
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 importpymysql4
5 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')6 cursor =conn.cursor()7 cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])8 conn.commit()9 cursor.close()10 conn.close()11
12 #获取最新自增ID
13 new_id = cursor.lastrowid
View Code
3、获取查询数据
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 importpymysql4
5 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')6 cursor =conn.cursor()7 cursor.execute("select * from hosts")8
9 #获取第一行数据
10 row_1 =cursor.fetchone()11
12 #获取前n行数据
13 #row_2 = cursor.fetchmany(3)
14 #获取所有数据
15 #row_3 = cursor.fetchall()
16
17 conn.commit()18 cursor.close()19 conn.close()
View Code
注:在fetch数据时按照顺序进行,可以使用cursor.scroll(num,mode)来移动游标位置,如:
cursor.scroll(1,mode='relative') # 相对当前位置移动
cursor.scroll(2,mode='absolute') # 相对绝对位置移动
4、fetch数据类型
关于默认获取的数据是元祖类型,如果想要或者字典类型的数据,即:
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 importpymysql4
5 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')6
7 #游标设置为字典类型
8 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)9 r = cursor.execute("call p1()")10
11 result =cursor.fetchone()12
13 conn.commit()14 cursor.close()15 conn.close()
View Code
9. ORM sqlachemy学习
ORM介绍
orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言
orm优点:
1、隐藏了数据访问细节,封装的通用数据库交互,ORM的核心,使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语言,快速开发由此而来
2、ORM使我们构造固化数据结构变得简单易行
缺点:
无可避免的,自动化意味着映射和关系管理,代价就是牺牲性能(早期这是所有不喜欢ORM的人的共同点),现在各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad, Cache),效果还是很显著的
2. sqlalchemy安装
在python中,最有名的ORM框架就是SQLAlchemy,用户包括openstack/Dropbox等知名公司和应用,主要用户列表
http://www.sqlalchemy.org/organizations.html#openstack
Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
1 MySQL-Python2 mysql+mysqldb://:@[:]/
3
4 pymysql5 mysql+pymysql://:@/[?]6
7 MySQL-Connector8 mysql+mysqlconnector://:@[:]/
9
10 cx_Oracle11 oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]12
13 更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html
安装sqlalchemy
1 pip install SQLAlchemy2 pip install pymysql #由于mysqldb依然不支持py3,所以这里我们用pymysql与sqlalchemy交互
3.sqlalchemy基本使用
下面开始使用sqlchemy,首先使用sql创建一个表:
1 CREATE TABLE user(2 id INTEGER NOT NULLAUTO_INCREMENT,3 name VARCHAR(32),4 password VARCHAR(64),5 PRIMARY KEY(id)6 )
这只是简单的sql表,如果再加上外键关联什么的,一般程序员脑容量是记不住这些sql语句的于是有了orm,实现上面同样的功能,代码如下:
1 importsqlalchemy2 from sqlalchemy importcreate_engine3 from sqlalchemy.ext.declarative importdeclarative_base4 from sqlalchemy importColumn, Integer, String5
6 engine = create_engine("mysql+pymysql://root:alex3714@localhost/testdb",7 encoding='utf-8', echo=True)8
9
10 Base = declarative_base() #生成orm基类
11
12 classUser(Base):13 __tablename__ = 'user' #表名
14 id = Column(Integer, primary_key=True)15 name = Column(String(32))16 password = Column(String(64))17
18 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.
创建好表以后,开始用orm创建一条数据试试
1 from sqlalchemy.orm importsessionmaker2 Session_class = sessionmaker(bind=engine)#创建与数据库的会话session class,注意,这里返回给session的是个class,不是实例
3 #要把实例与engine绑定
4 Session = Session_class()#生成session实例 session就是cursor
5
6 user_obj1 = User(name='alex',password='123') #生成要创建的数据对象
7 user_obj2 = User(name='tom',password='456')8 print(user_obj1.name,user_obj1.password,user_obj1.id)#此时还没有创建对象,可以打印一下id发现还是None
9
10 Session.add(user_obj1)#把腰创建的数据对象添加到这个session中,一会统一创建
11 Session.add(user_obj2) #add只能分开写,没法一起写
12 print(user_obj1.name,user_obj1.password,user_obj1.id)#此时仍然没有创建
13
14 Session.commit()#统一提交创建数据
查询
1 importsqlalchemy2 from sqlalchemy import create_engine #create_engine就是连接数据库的
3 from sqlalchemy.ext.declarative importdeclarative_base4 from sqlalchemy importColumn,String,Integer5 from sqlalchemy.orm importsessionmaker6 engine = create_engine("mysql+pymysql://gavin:[email protected]/s14day11",encoding='utf-8',echo=False)#sqlalchemy底层就是使用pymysql mysql-db,mysql-connetc,oracle之类,语法为定义连接什么数据库,使用哪个底层调用
7 #echo=True是打开类似debug功能
8
9 Base = declarative_base() #生产orm基类
10
11
12 classUser(Base):13 __tablename__ = 'user' #表名
14 id = Column(Integer, primary_key=True)15 name = Column(String(32))16 password = Column(String(64))17
18 def __repr__(self):#如果想在Session.query(User).filter_by().all()的情况下直接print()data_obj直接显示内容,就需要加这个函数
19 return "" %(self.id,self.name,self.password)20
21 Base.metadata.create_all(engine) #创建表结构,通过调用engine这个socket,Base父类调用儿子来创建表
22
23 Session_class = sessionmaker(bind=engine)#创建与数据库的会话session class,注意,这里返回给session的是个class,不是实例
24 #要把实例与engine绑定
25 Session = Session_class()#生成session实例 session就是cursor
26 '''
27 user_obj1 = User(name='alex',password='123') #生成要创建的数据对象28 user_obj2 = User(name='tom',password='456')29 print(user_obj1.name,user_obj1.password,user_obj1.id)#此时还没有创建对象,可以打印一下id发现还是None30
31 Session.add(user_obj1)#把腰创建的数据对象添加到这个session中,一会统一创建32 Session.add(user_obj2) #add只能分开写,没法一起写33 print(user_obj1.name,user_obj1.password,user_obj1.id)#此时仍然没有创建34 Session.commit()#统一提交创建数据35 '''
36 #查询
37 data = Session.query(User).filter_by(name='alex') #fiter_by查询出来只是个查询语句
38 data_obj = Session.query(User).filter_by(name='alex').all()39 #print(data_obj) data_obj是个对象
40 print(data_obj[0].name,data_obj[0].password)41
42 data_obj = Session.query(User).filter_by().all() #如果fiter_by里面不写具体内容,会返回表中所有数据,如果不在User类里添加__repr__函数,就只会显示对象,而不显示具体内容
43 print(data_obj)44
45 data_obj = Session.query(User).filter_by().first() #使用first的话,只会显示第一条
46 print(data_obj)47
48 data_obj = Session.query(User).filter(User.id==2).all() #fiter可以写 >号 也可以写==号
49 print(data_obj)50 data_obj = Session.query(User).filter_by(id=2).all() #fiter_by可以使用=表示等于
51 print(data_obj)52
53 #多条件查询
54 data_obj = Session.query(User).filter(User.id>2).filter(User.id <4).all() #多条件是连续多个fiter组合
55 print(data_obj)
修改
1 #修改
2 data_obj = Session.query(User).filter(User.id>2).filter(User.id <4).first() #如果要修改数据,必须使用first(),只能修改一个数据,不能批量修改
3 print(data_obj)4
5 data_obj.name = 'Jack Liu'
6 data_obj.password = 'password'
7 Session.commit()#修改需要提交
回滚
1 #回滚
2 fake_user = User(name='Rain', password='222')3 Session.add(fake_user)4
5 print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all())6 Session.rollback()7 print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all())8 Session.commit()
分组统计
1 #普通统计
2 print(Session.query(User).filter(User.name.in_(['Jack Liu','alex'])).count())3
4 #分组统计
5 from sqlalchemy importfunc6
7 print(Session.query(User.name,func.count(User.name)).group_by(User.name).all()) #按照User.name来统计,func.count与User.name位置可以互换
删除
1 print(Session.query(User).filter(User.name=='n3').all())2 Session.query(User).filter(User.name=='n3').delete()3 print(Session.query(User).filter(User.name=='n3').all())
连表
1 print(Session.query(User,Student).filter(User.id==Student.id).all())2 print(Session.query(User).join(Student).all()) #如果有foreign key才可以使用
3 print(Session.query(User).join(Student,isouter=True).all())#如果有foreign key才可以使用
外键关联
from sqlalchemy importcreate_enginefrom sqlalchemy.ext.declarative importdeclarative_basefrom sqlalchemy importColumn,DATE,Integer,String,ForeignKeyfrom sqlalchemy.orm importsessionmaker,relationship
engine= create_engine('mysql+pymysql://gavin:[email protected]/s14day11',encoding='utf-8')
Base=declarative_base()classStudent(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 '' %(self.id,self.name,self.register_date)classStudyRecord(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'))
student= relationship('Student',backref='my_study_record')#在StudyRecord表中通过student字段可以查看Student里所有内容,也可以让Student这个表
#通过my_study_record这个字段反查Studyrecord内容
#student是存在在内容中,是orm自身的,和mysql无关
def __repr__(self):return '' %(self.student.name,self.stu_id,self.day,self.status) #可以在这里将student表中学员名字显示出来
Base.metadata.create_all(engine)
Session_class= sessionmaker(bind=engine)
Session=Session_class()'''s1 = Student(name='Alex',register_date='2016-06-12')
s2 = Student(name='Jack',register_date='2015-01-14')
s3 = Student(name='Rain',register_date='2012-11-13')
s4 = Student(name='Eric',register_date='2017-07-25')
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=='alex').first()print(stu_obj.my_study_record) #在student中查看alex然后查看alex在study_record中出勤率
study_obj= Session.query(StudyRecord).filter(StudyRecord.id==1).first()print(study_obj.student)
常用查询语法
Common Filter Operators
Here’s a rundown of some of the most common operators used in filter():
equals:
query.filter(User.name == 'ed')
not equals:
query.filter(User.name != 'ed')
LIKE:
query.filter(User.name.like('%ed%'))
IN:
NOT IN:
query.filter(~User.name.in_(['ed', 'wendy', 'jack']))
IS NULL:
IS NOT NULL:
AND:
2.1. ObjectRelationalTutorial 17
query.filter(User.name.in_(['ed', 'wendy', 'jack']))
# works with query objects too:
query.filter(User.name.in_( session.query(User.name).filter(User.name.like('%ed%'))
))
query.filter(User.name == None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.is_(None))
query.filter(User.name != None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.isnot(None))
SQLAlchemy Documentation, Release 1.1.0b1
# use and_()
from sqlalchemy import and_
query.filter(and_(User.name == 'ed', User.fullname == 'Ed Jones'))
# or send multiple expressions to .filter()
query.filter(User.name == 'ed', User.fullname == 'Ed Jones')
# or chain multiple filter()/filter_by() calls
query.filter(User.name == 'ed').filter(User.fullname == 'Ed Jones')
Note: Makesureyouuseand_()andnotthePythonandoperator! • OR:
Note: Makesureyouuseor_()andnotthePythonoroperator! • MATCH:
query.filter(User.name.match('wendy'))
Note: match() uses a database-specific MATCH or CONTAINS f
多外键关联
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表
备注:在实际编程中,表结构与增删改查操作是在不同的文件中存放的
1 from sqlalchemy importInteger,ForeignKey,String,Column2 from sqlalchemy.ext.declarative importdeclarative_base3 from sqlalchemy.orm importrelationship4 from sqlalchemy importcreate_engine5 engine = create_engine("mysql+pymysql://gavin:[email protected]/s14day11",encoding='utf-8',echo=False)6 Base =declarative_base()7
8 classCustomer(Base):9 __tablename__ = 'customer'
10 id = Column(Integer,primary_key=True)11 name = Column(String(64))12
13 billing_address_id = Column(Integer,ForeignKey('address.id'))14 shipping_address_id = Column(Integer,ForeignKey('address.id'))15
16 billing_address = relationship('Address',foreign_keys=[billing_address_id]) #需要添加foreign_keys=[billing_address_id]才能在操作时知道
17 shipping_address = relationship('Address',foreign_keys=[shipping_address_id])18
19 def __repr__(self):20 return '' %( self.id,self.name,self.billing_address_id,self.shipping_address_id )21
22 classAddress(Base):23 __tablename__ = 'address'
24 id = Column(Integer,primary_key=True)25 street = Column(String(64))26 city = Column(String(64))27 state = Column(String(64))28
29 def __repr__(self):30 return '' %(self.id,self.street,self.city,self.state)31
32
33 Base.metadata.create_all(engine)
orm_manfk.py
1 importorm_manyfk2
3 from sqlalchemy.orm importsessionmaker4
5
6 Session_class = sessionmaker(bind=orm_manyfk.engine)7 session =Session_class()8 '''
9 addr1 = orm_manyfk.Address(street='tiantongyuan',city='Chanping',state='BJ')10 addr2 = orm_manyfk.Address(street='wudaokou',city='Haidian',state='BJ')11 addr3 = orm_manyfk.Address(street='yanjiao',city='Langfang',state='HB')12
13 session.add_all([addr1,addr2,addr3])14 c1 = orm_manyfk.Customer(name='Alex',billing_address=addr1,shipping_address=addr2)15 c2 = orm_manyfk.Customer(name='Jack',billing_address=addr3,shipping_address=addr3)16 c3 = orm_manyfk.Customer(name='Tom',billing_address=addr2,shipping_address=addr1)17 c4 = orm_manyfk.Customer(name='Jarry',billing_address=addr2,shipping_address=addr1)18 session.add_all([c1,c2,c3,c4])19
20 session.commit()21 '''
22
23
24
25
26
27 obj = session.query(orm_manyfk.Customer).filter(orm_manyfk.Customer.name=='Alex').first()28 print(obj.name,obj.billing_address,obj.shipping_address)
orm_api.py
多对多关系
现在来设计一个能描述“图书”与“作者”的关系的表结构,需求是
一本书可以有好几个作者一起出版
一个作者可以写好几本书
此时你会发现,用之前学的外键好像没办法实现上面的需求了,因为
当然你更不可以像下面这样干,因为这样就你就相当于有多条书的记录了,太low了,改书名还得都改。。。
那怎么办呢? 此时,我们可以再搞出一张中间表,就可以了
这样就相当于通过book_m2m_author表完成了book表和author表之前的多对多关联
用orm如何表示呢?
1 from sqlalchemy importTable,Column,Integer,String,DATE,ForeignKey2 from sqlalchemy.ext.declarative importdeclarative_base3 from sqlalchemy.orm importrelationship4 from sqlalchemy importcreate_engine5 from sqlalchemy.orm importsessionmaker6
7 Base =declarative_base()8
9 book_m2m_author =Table(10 'book_m2m_author',Base.metadata,11 Column('book_id',Integer,ForeignKey('books.id')),12 Column('author_id',Integer,ForeignKey('authors.id')),13 )14
15 classBook(Base):16 __tablename__ = 'books'
17 id = Column(Integer,primary_key=True)18 name = Column(String(64))19 pub_date =Column(DATE)20 authors = relationship('Author',secondary=book_m2m_author,backref='books')21
22 def __repr__(self):23 returnself.name24
25
26
27 classAuthor(Base):28 __tablename__ = 'authors'
29 id = Column(Integer,primary_key=True)30 name = Column(String(32))31
32 def __repr__(self):33 returnself.name34
35
36 engine = create_engine('mysql+pymysql://gavin:[email protected]/s14day11?charset=utf8',encoding='utf-8')#在db后面加?charset=utf8才能支持中文
37
38 Base.metadata.create_all(engine)
View Code
接下来创建几本书和作者
1 Session_class = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
2 s = Session_class() #生成session实例
3
4 b1 = Book(name="跟Alex学Python")5 b2 = Book(name="跟Alex学把妹")6 b3 = Book(name="跟Alex学装逼")7 b4 = Book(name="跟Alex学开车")8
9 a1 = Author(name="Alex")10 a2 = Author(name="Jack")11 a3 = Author(name="Rain")12
13 b1.authors =[a1,a2]14 b2.authors =[a1,a2,a3]15
16 s.add_all([b1,b2,b3,b4,a1,a2,a3])17
18 s.commit()
此时,手动连上mysql,分别查看这3张表,你会发现,book_m2m_author中自动创建了多条纪录用来连接book和author表
1 mysql> select * frombooks;2 +----+------------------+----------+
3 | id | name | pub_date |
4 +----+------------------+----------+
5 | 1 | 跟Alex学Python | NULL |
6 | 2 | 跟Alex学把妹 | NULL |
7 | 3 | 跟Alex学装逼 | NULL |
8 | 4 | 跟Alex学开车 | NULL |
9 +----+------------------+----------+
10 4 rows in set (0.00sec)11
12 mysql> select * fromauthors;13 +----+------+
14 | id | name |
15 +----+------+
16 | 10 | Alex |
17 | 11 | Jack |
18 | 12 | Rain |
19 +----+------+
20 3 rows in set (0.00sec)21
22 mysql> select * frombook_m2m_author;23 +---------+-----------+
24 | book_id | author_id |
25 +---------+-----------+
26 | 2 | 10 |
27 | 2 | 11 |
28 | 2 | 12 |
29 | 1 | 10 |
30 | 1 | 11 |
31 +---------+-----------+
32 5 rows in set (0.00 sec)
此时,我们去用orm查一下数据
1 print('--------通过书表查关联的作者---------')2
3 book_obj = s.query(Book).filter_by(name="跟Alex学Python").first()4 print(book_obj.name, book_obj.authors)5
6 print('--------通过作者表查关联的书---------')7 author_obj =s.query(Author).filter_by(name="Alex").first()8 print(author_obj.name , author_obj.books)9 s.commit()
输出如下
1 --------通过书表查关联的作者---------
2 跟Alex学Python [Alex, Jack]3 --------通过作者表查关联的书---------
4 Alex [跟Alex学把妹, 跟Alex学Python]
多对多删除
删除数据时不用管boo_m2m_authors , sqlalchemy会自动帮你把对应的数据删除
通过书删除作者
1 author_obj =s.query(Author).filter_by(name="Jack").first()2
3 book_obj = s.query(Book).filter_by(name="跟Alex学把妹").first()4
5 book_obj.authors.remove(author_obj) #从一本书里删除一个作者
6 s.commit()
直接删除作者
删除作者时,会把这个作者跟所有书的关联关系数据也自动删除
1 author_obj = session.query(orm_m2m.Author).filter(orm_m2m.Author.name=='Alex').first()2 session.delete(author_obj)3 session.commit()
处理中文
sqlalchemy设置编码字符集一定要在数据库访问的URL上增加charset=utf8,否则数据库的连接就不是utf8的编码格式
eng = create_engine('mysql://root:root@localhost:3306/test2?charset=utf8',echo=True)