python3_关系型数据库mysql

注: 多为老师分享内容, 亦有部分网上资料和个人总结

关系型数据库基础

DBMS 管理数据库的系统软件,它实现数据库系统的各种功能,RDBMS关系型数据库管理软件
DBA database administrator负责数据库的规划,设计,协调,维护和管理工作
数据库系统的框架:
1)单机构架; 2)大型主机/终端构架; 3)主从式构架; 4)分布式构架
关系型数据库只有这一种数据结构,NoSQL还有其他数据结构
关系: 关系就是二维表,表中的行列次序并不重要
行row:表中的每一行,又称为一条记录
列column:表中的每一列,称为属性,字段

事务transaction

多个操作被当做一个整体对待,要么全部完成,要么都不做(rollback),具有ACID原则:
A:原子性Atomic,多个操作不可拆分
C:一致性Consistent,数据库的约束,级联和触发机制Trigger都必须满足事务的一致性,
事务一旦完成,所有关联数据不可避免的发生改变,并行处理的结果串行处理的结果一致
I:隔离性Isolated,实现并发控制,一个未完成事务不会影响另一个未完成事务,
隔离有不同的级别, 隔离性不好带的问题:

  • 更新丢失,Lost Update, 同时读到数据,处理后数据丢失
  • 脏读,读到未提交数据, ,dirtydata脏数据
  • 不可重复读,Unrepeatable read,A事务查询多次,发现查询结果不一致
  • 幻读,Phantom read,读可重复,但是提交后再读,发现和之前读到的不一致,每一次提交之间读到的不一致.其他事务提交的数据看不到,但是数据库中已存在,再插入相同的数据,可能得不到自己想要的结果.

MySQL隔离级别:

  1. READ UNCOMMITED 可读取未提交的数据,导致脏读
  2. READ COMMITED 读已经提交的数据, ORACLE默认级别
  3. REPEATABLE READ 可重复读, MySQL的默认隔离级别,用户查询的是最近一次提交的镜像,不同事务之间使用不同的镜像.不能解决幻读
  4. SERIALIZABLE 可串行化, 可序列化, 完全隔离, 性能低

D:持久性Durable,一旦一个事务被提交,它应该持久保存,不会因为和其他操作冲突而取消

mysql设置回话级和全局隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL [READ COMMITED]
查询隔离级别
SELECT @@global.tx_isolation;
SELECT @@tx_isolation;
关闭自动提交,SET AUTOCOMMIT=0; 查看变量select @@AUTOCOMMIT;

repeatable read级别,两个事务同时修改一个字段,后修改的事务会阻塞,直到前一个事务提交.
start transaction 开启事务
commit 提交事务
begin end 多行执行sql语句

数据仓库数据库的区别
数据库存储在线交易数据OLTP(联机事务处理,On-Line Transaction Processing)
数据仓库存储历史数据,用于数据分析OLAP(联机分析处理,On-Line Analytical Processing)

主外键,联系类型:

  1. 一对一,外键字段不重复, 一个外键对应一个主键
  2. 一对多,外键字段重复, 一个外键对应多个主键
  3. 多对多,一张表中存在两个外键属性, 且外键字段都重复

数据三要素

  1. 数据的结构,包括两类
    • 与数据类型,内容,性质有关的对象,例如domain,属性
    • 与数据之间联系有关的对象,例如主外键
  2. 数据的操作:
    数据的增删改查 SELECT,INSERT,DELETE,UPDATE
  3. 数据的约束条件:是一组数据完整性规则的集合
    实体完整性Entity Integrity:primary key,确保行唯一
    域完整性Domain Integrity:定义字段范围
    参考完整性Referential Integrity:主外键使用

RDMBS设计范式
第一范式(1NF),第二范式(2NF),第三范式(3NF),巴德斯科范式(BCNF),第四范式(4NF)和第五范式(5NF,又称完美范式),要求依次递增,范式可能会降低效率,范式是用来打破
* 1NF:无重复的列,每一列都是不可分割的基本数据项,同一列中不能有多个值即实体中的某个属性不能有多个值或者不能有重复的属性。 说明:第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的 数据库就不是关系数据库
* 2NF:属性完全依赖于主键,第二范式必须先满足第一范式,要求表中的每一行必须可以被唯一地区分。通常为表加上一个列,以存储各个实例的唯一标识PK,非PK的字段需要与整个PK有直接相关性
* 3NF:属性不依赖于其它非主属性,满足第三范式必须先满足第二范式。第三 范式要求一个数据库表中不包含已在其它表中已包含的非主关键字信息(没有数据冗余),非PK字段间不能有从属关

约束:constraint,表中的数据要遵守的限制

  • 主键:一个或多个字段的组合,填入的数据必须能在本表中唯一标识本行; 必须提供数据,即NOT NULL,一个表只能有一个
  • 惟一键:一个或多个字段的组合,填入的数据必须能在本表中唯一标识本行; 允许为NULL,一个表可以存在多个
  • 外键:一个表中的某字段可填入的数据取决于另一个表的主键或唯一键已有的数据
  • 检查:字段值在一定范围内,域domain

关系运算:
选择selection:挑选出符合条件的行
投影projection:挑选出需要的字段
连接join:表间字段的关联


MySQL

安装包
For a standard server installation you will need to download at least the client, shared, and server RPM files

默认存储引擎
MyISAM ==> InnoDB ==> XtraDB(Percona)
InnoDB:不支持FULLTEXT类型的索引,而且它没有保存表的行数,当SELECT COUNT(*) FROM TABLE时需要扫描全表
MyIASM没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和Innodb不同,MyIASM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。

安全性配置 mysql_secure_installation
客户端程序 mysql, mysqldump, mysqladmin, mysqlimport
服务器端程序 mysql_safe, mysqld, mysql_multi
mysql用户账户由两部分组成: ‘USERNAME’@’HOST’
通配符:%表示任意长度的任意字符,_表示任意单个字符

mysql –help
-A, –no-auto-rehash 禁止补全
-u, –user= 用户名,默认为root
-h, –host= 服务器主机,默认为localhost
-p, –passowrd= 用户密码,建议使用-p,默认为空密码
-P, –port= 服务器端口
-S, –socket= 指定连接socket文件路径
-D, –database= 指定默认数据库
-C, –compress 启用压缩
-e “SQL“ 执行SQL命令
-V, –version 显示版本
-v –verbose 显示详细信息
–print-defaults 获取程序默认使用的配置

默认文件夹 /var/lib/mysql
ip socket: 监听在tcp的3306端口,支持远程通信
unix sock: 监听在sock文件上,仅支持本机通信,
host为localhost,127.0.0.1时自动使用unix sock

配置文件位置/etc/my.cnf,集中式的配置,类ini格式,
_-相同,(1, ON, TRUE)相同, (0, OFF, FALSE)意义相同

[mysqld]
skip-networking=1关闭网络,只侦听本地客户端socket,即/var/lib/mysql/mysql.sock
不对外开放3306端口,本地仍可使用数据库
skip-name-resolve
[mysql]
safe-updates
innodb_file_per_table 5.5以上默认on,表示每一个表有一个文件


SQL: Structure Query Language

结构化查询语言,解释执行,SQL解释器,数据库存储协议,应用层协议C/S
server端,监听socket,接收并处理客户端的应用请求
client端,客户端程序接口:CLI(Command Line Interface),GUI(Graphical User Interface)

语言规范
不区分大小写,建议大写,字符串常量区分大小写
对象包括(数据库、表、索引、视图、用户、存储过程、函数、触发器、事件调度器,)
对象名必须以字母开头,可以包括数字和三个特殊字符#$_
;结尾,空格和缩进没有语义
多行注释: /*注释内容*/,
单行数值: –注释内容
MySql注释: #

SQL语句分类
DDL:Data Defination Language, CREATE,DROP,ALTER
DML:Data Manipulation Language, INSERT,DELETE,UPDATE
DCL:Data Control Language, GRANT,REVOKE
DQL:Data Query Language, SELECT

Keyword组成子句(clause),多条clause组成sql语句

新建仓库

CREATE DATABASE|SCHEMA db_name;
CHARACTER SET character_set_name 设置字符集
COLLATE collate_name 设置排序规则
DROP DATABASE|SCHEMA db_name;
SHOW DATABASES;
SHOW CHARACTER SET;
SHOW COLLATION;

创建表TABLE

CREATE TABLE tbl_name (create_definition) [table_options][partition_options] [select_statement]
CREATE TABLE tbl_name {LIKE old_tbl_name}
字段信息
* col type1
* PRIMARY KEY(col1,…)
* INDEX(col1, …)
* UNIQUE KEY(col1, …)
* FOREIGN KEY [index_name] (index_col_name,…) REFERENCE tbl_name (index_col_name,…)
例: CREATE TABLE students2 (id int UNSIGNED NOT NULL ,name VARCHAR(20) NOT NULL,age tinyint UNSIGNED,PRIMARY KEY(id,name));

SHOW ENGINES; 查看支持的引擎
SHOW TABLES;
DESC tb_name; == SHOW COLUMNS FROM tb_name;
SHOW CREATE {TABLE|DATABASE} tbl_name|db_name;
SHOW TABLE STATUS LIKE tbl_name;
SHOW TABLE STATUS FROM db_name;

数据类型tinyint(m)

整型int,浮点型float,定点数decimal(m,d),字符串char,可变字符串varchar,二进制BLOB,时间和日期date
修饰符:
NULL 数据列可包含NULL值
NOT NULL 数据列不允许包含NULL值
DEFAULT 默认值
CHARACTER SET name 指定一个字符集
数值型修饰符:
AUTO_INCREMENT 自动递增,适用于整数类型
UNSIGNED 无符号

表操作

RENAME TABLE tal_name TO db_new_tbl_name 移动表到另一个数据库并重命名
DROP TABLE tbl_name;
ALTER TABLE tbl_name {add|drop|alter(默认值)|change(字段名)|modify(字段属性)}
ALTER TABLE tbl_name ALTER col_name {SET DEFAULT literal | DROP DEAULT}修改默认值
ADD {col_name type | INDEX(col_name) | UNIQUE KEY(col_name)}

插入数据INSERT

INSERT tbl_name (col1,…) VALUES (val1,..),(val2,…),…;
INSERT tbl_name SET col_name={expr|DEFAULT},…;
INSERT tbl_name [(col_name,…)] SELECT…;
INSERT IGNORE 主键冲突,唯一键冲突,就忽略语句,返回一个警告
INSERT INTO tbl_name (col_name,…) VALUES (val,…) ON DUPLICATE KEY UPDATE
DUPLICATE键冲突则执行update…

更新数据UPDATE

UPDATE table_reference SET col_name1={expr1|DEFAULT},…[WHERE where_condition|LIMIT row_count];limit限制更新条数
where条件一定要添加,不然全部更改
alias mysql=maysql –safe-updates|-U|–i-am-a-dummy
或者 /etc/my.cfg 中添加 [mysql] safe-updates

删除数据DELETE

DELETE FROM tbl_name [WHERE where_condition][ORDER BY …][LIMIT row_count] 可先排序order,再指定删除的行数limit
where条件一定要添加,不然全部更改
TRUNCATE TABLE tbl_name 清空所有行,实际是删除表并重建表定义,更像DDL; 比DELETE效率高,但是不会触发外键约束,不会触发trigger,implicit commit,无法rollback
假删除,添加column deleted 打标记

查询语句SELECT

SELECT子句可以使用别名:col1 AS alias1, col2+10 AS alias2, …
FROM子句也可以使用别名
WHERE子句:指明过滤条件以实现“选择”的功能:
过滤条件:布尔型表达式
算术操作符:+, -, *, /, %
比较操作符:=, !=, <>, >, >=, <, <=
BETWEEN min_num AND max_num [min, max]
IN (element1, element2, …) 避免使用
IS NULL IS NOT NULL
LIKE: WHERE clo_name LIKE ‘exp’, %任意长度任意字符,_任意单字符, 避免使用 like %a% 或a%
RLIKE | REGEXP:可用正则表达式,但是索引失效,不建议使用
逻辑logic操作符: NOT, AND, OR, XOR
SELECT CONCAT(first_name,lastname) 连接 column作为一个column返回
SELECT DISTINCT col_name1, col_name2 去重,将col1和col2合并去重
ORDER BY col_name1 DESC, col_name2 先满足第一个,再满足第二个
慢查询,log记录
GROUP BY:根据指定条件把查询结果进行分组,用于聚合运算,avg(),max(),min(),count(),sum(),count(*)
HAVING where_condition:对分组聚合运算后的结果指定过滤条件
ORDER BY:根据指定字段对查询结果进行排序,
升序:ASC 默认, 降序:DESC
LIMIT [row_count] [OFFSET offset]: 对查询结果进行输出行数限制
FOR UPDATE: select for update,为了修改,表明当前事务将要修改,别的事务不能修改锁定的行row
LOCK IN SHARE MODE:当前事务为了读,并且要求别的事务不能修改,可以读,如果有事务修改了要锁定的行,则阻塞直到事务被提交
row level lock行级锁

子查询:在查询语句嵌套着查询语句,性能较差,基于某语句的查询结果再次进行的查询

  • 用在WHERE子句中的子查询:
    • 用于比较表达式中的子查询;子查询仅能返回单个值 SELECT Name,Age FROM students WHERE Age>(SELECT avg(Age) FROM students);
    • 用于IN中的子查询:子查询应该单键查询并返回一个或多个值从构成列表 SELECT Name,Age FROM students WHERE Age IN (SELECT Age FROM teachers);
  • 用于FROM子句中的子查询 使用格式:SELECT tb_alias.col1,… FROM (SELECT clause) AS tb_alias WHERE Clause;
    示例: SELECT s.aage,s.ClassID FROM (SELECT avg(Age) AS aage,ClassID FROM students WHERE ClassID IS NOT NULL GROUP BY ClassID) AS s WHERE s.aage>30;

联合查询:UNION
SELECT Name,Age FROM students
UNION SELECT Name,Age FROM teachers;
相同的row,会被合并

多表查询:
交叉连接:笛卡尔乘积,一一对应连接,SELECT clo_name FROM tbl1 [CROSS|INNER] JOIN tbl2;
内连接:SELECT clo_name FROM tbl1 [INNER] JOIN tbl2 ON where_condition; 只保留都符合条件的内容
外连接: SELECT clo_name FROM tbl1 {LEFT|RIGHT} [OUTER] JOIN tbl2 ON where_condition; 保留左侧表或右侧表完整内容,对侧没有的以null填充
join存在一对多的情况,自动补全
自然连接NATURAL,会去掉重复列
使用alias, 使

SELECT 语句顺序
FROM(specifies tables that are queried) –>
WHERE(select rows that satisfy the condition) –>
GROUP BY(groups rows on basis of equal values in columns) –>
HAVING(select groups that satisfy the condition) –>
ORDER BY(sorts rows on basis of columns) –>
SELECT(select columns) –>
LIMIT(removes rows on basis of their order number) –>
end result

视图VIEW

虚表,保存实表的查询结果,视图是实时更新,每一次使用,都是根据定义的select语句,重新生成的
创建视图 CREATE VIEW view_name [(column_list)] AS select_statement
查看视图定义: SHOW CREATE VIEW view_name
删除视图: DROP VIEW view_name,[view_name]…[RESTRIC | CASCADE],CASCADE级联删除
修改视图数据,会相应的修改’基表’的数据

函数function

系统函数
自定义函数
创建:(delimiter修改结束符
DELIMITER//
CREATE FUNCTION function_name(parameter_name type,…)
RETURNS type
BEGIN
runtime_body;;
END//
DELIMITER ; # 恢复修改的结束符
查看函数列表: SHOW FUNCTION STATUS;
查看函数定义: SHOW CREATE FUNCTION function_name;
删除函数: DROP FUNCTION funct_name;
调用定义的函数: select function_name(parameter_value,…)
定义函数局部变量: DECLARE val_name,… type [DEFAULT default_value]
变量赋值: 局部变量 SET val_name = value,…; 全局变量 SET @val_name = value
局部变量 SELECT value INTO val_name;

存储过程procedure

保存在mysql.proc表中
创建:
CREATE PROCEDURE sp_name ([proc_parameter],…)
routtime_body
proc_parameter: [IN|OUT|INOUT] parameter_name type
参数可以输入, 输出, 即作为输入又作为输出
查看存储过程列表: SHOW PROCEDURE STATUS
查看存储过程定义: SHOW CREATE PROCEDURE sp_name
调用存储过程: CALL sp_name([proc_parameter,…])
CALL sp_name
删除存储过程: DROP PROCEDURE sp_name
存储过程提高了运行速度,降低了网络数据传输量,
存储过程一般独立执行,函数往往作为其他SQL语句的一部分来使用,
存储过程可以返回多个值,而函数只有一个返回值

流程控制
IF;CASE;LOOP;LEAVE;ITERATE;REPEAT;UNTIL END REPEAT;WHILE

触发器trigger: 定义后,由事件触发执行

创建:
CREATE TRIGGER trigger_name
trigger_time{DEFORE|AFTER} trigger_event{INSERT|UPDATE|DELETE}
ON tbl_name FOR EACH ROW
trigger_body

MySQL用户和权限管理

元数据数据库:mysql
用户账号: ‘USERNAME’@’HOST’: ‘HOST’:主机名;IP地址或Network; 通配符:%, _: 172.16.%.%
用户管理
创建用户:CREATE USER CREATE USER ‘USERNAME’@’HOST’ [IDENTIFIED BY ‘password’];
默认权限:USAGE
用户重命名:RENAME USER old_user_name TO new_user_name
删除用户: DROP USER ‘USERNAME’@’HOST‘ 示例:删除默认的空用户 DROP USER ”@’localhost’;
修改密码:
mysql>SET PASSWORD FOR ‘user’@’host’ = PASSWORD(‘password’);
mysql>UPDATE mysql.user SET password=PASSWORD(‘password’) WHERE clause; 此方法需要执行下面指令才能生效: mysql> FLUSH PRIVILEGES;
mysqladmin -u root –poldpass password ‘newpass‘
忘记管理员密码的解决办法:
启动mysqld进程时,为其使用如下选项: –skip-grant-tables –skip-networking
使用UPDATE命令修改管理员密码
关闭mysqld进程,移除上述两个选项,重启mysqld

授权grant/取消授权revoke
GRANT ALL ON *.* TO ‘username’@’%’ IDENTIFIED BY ‘password’
*.*所有库所有表, ALL表示所有权限
服务器配置
获取运行中的mysql进程使用各服务器参数及其值
mysql> SHOW GLOBAL VARIABLES;
mysql> SHOW [SESSION] VARIABLES;

设置(SET)服务器系统变量三种方法:

在命令行中设置:  shell> ./mysqld_safe --aria\_group_commit="hard“ 
在配置文件my.cnf中设置:  aria\_group_commit = "hard" 
在mysql客户端使用SET命令:  SET GLOBAL aria\_group_commit="hard";

修改服务器变量的值:
mysql> help SET
修改全局变量:仅对修改后新创建的会话有效;对已经建立的会话无效
mysql> SET GLOBAL system_var_name=value;
mysql> SET @@global.system_var_name=value;
修改会话变量:
mysql> SET [SESSION] system_var_name=value;
mysql> SET @@[session.]system_var_name=value;
状态变量(只读):用于保存mysqld运行中的统计数据的变量,不可更改
mysql> SHOW GLOBAL STATUS;
mysql> SHOW [SESSION] STATUS;

索引Index

主键索引,唯一键索引,普通索引

高性能索引策略:
独立地使用索引列:尽量避免其参与运算,在where条件中,始终将索引列单独放在比较符号的一侧
左前缀索引:构建指定索引字段的左侧的字符数,要通过索引选择性来评估
索引选择性:不重复的索引值和数据表的记录总数的比值
多列索引:AND操作时更适合使用多列索引,而非为每个列创建单独的索引
选择合适的索引列顺序:无排序和分组时,将选择性最高放左侧,即不重复数多的列
冗余和重复索引:(A),(A,B)即为冗余索引,不好的索引使用策略,建议扩展索引,而非冗余

索引优化建议:
只要列中含有NULL值,就最好不要在此例设置索引,复合索引如果有NULL值, 此列在使用时也不会使用索引
尽量使用短索引,如果可以,应该制定一个前缀长度
对于经常在where子句使用的列,最好设置索引
对于有多个列where或者ordered by子句条件,应该建立复合索引
对于like语句,以%或者‘-’开头的不会使用索引,以%结尾会使用索引
尽量不要在列上进行运算(函数操作和表达式操作)
尽量不要使用not in和><操作,范围太大

创建索引: CREATE INDEX index_name ON tbl_name(index_col_name,…)
删除索引: DROP INDEX index_name ON tbl_name
查看索引: SHOW INDEXES FROM [db_name.]tbl_name
优化表空间: OPTIMIZE TABLE tb_name

日志

复制

相关概念

去IOE IBM小型机,Oracle数据库,EMC存储设备
SQLite, DB2(IBM)
NoSQL: MongoDB(Document),Redis(Memory)
Search engine: Elasticsearch,Solr
Wide column store: HBase, Cassandra
Hive 数据仓库工具, PostgreSQL 对象关系型数据库管理系统(ORDBMS)


pymysql

mysql的驱动

  • mysqldb,不再支持python3
  • Mysql 官方Connector
  • pymysql 使用的接口是python自己的库

编程步骤

  1. 建立connection
    conn = pymysql.connect(host, user, password, database, port) # post默认3306
    conn.ping(False) 测试与数据库连接,抛异常,说明连接不正常
  2. 获得游标Cursor对象
    cursor = conn.cursor(cursor=None) # cursor=DictCursor返回的记录是字典
  3. 执行事务
    cursor.execute(sql_statement: str)
  4. 提交事务
    conn.commit() 如果发生异常conn.rollback()
  5. 释放资源
    cursor.close()
    conn.close()
    如果是查询语句,通过操作cursor,获得结果,不用提交commit,记得关闭
    cursor.fetchone() 取一条记录
    cursor.fetchmany(2) 取若干条记录,元组套元组
    cursor.fetchall() 取所有记录,类似生成器,fetch完,只能返回空元组
    cursor.rownumber() 返回当前行数,
    cursor.rowcount() 返回总行数,

connection 和 cursor都支持上下文
connection __enter__方法返回一个游标cursor,__exit__方法执行发生异常时roollback,没有异常就提交commit,不会关闭conneciton,需要手动关闭
cursor __enter__防护返回自己,__exit__方法关闭cursor
SQL注入攻击:
字符串拼接提交sql语句,where语句中拼入or 1=1,导致返回所有内容
参数化查询,既可以防止注入攻击,又可以提高查询效率
sql_statement中用%s或%(name)s做站位符,用元组或字典传入参数
cursor.excute(sql_statement, args/kwargs)


元编程介绍

类的实例化, 先经__new__方法,生成实例对象,再用__init__初始化对象,实例化传入参数和init函数signature一致,但事实上是调的new方法,new又将参数传给init初始化. 所以new方法的参数范围要比init大.
class定义类时,如果不设置元类metaclass,默认的元类就是type,表示这个类由这个类的元类(的new方法)生成
普通类的new方法生成类的实例,元类(继承自type的类)的new方法同样生成类的实例,只是元类的实例同样是个类,
普通类的new方法是object的new方法,元类的new方法是type的new方法

type的init(?call)方法,接收一个或三个参数, (重载,传入是会进行参数检查1or3?,再决定执行哪个方法?,实例化之前就判断参数)
type(object) -> the object’s type
type(name, bases, attr) -> a new class 返回一个新类,类型是class type
type的new方法接收三个参数,分别对应name: str类名, bases: 基类tuple, attr: 类属性字典dict

class关键词定义类时,name就是类的标识符,attr就是类属性,bases就是继承的类(默认是object),类的三要素

构建自己的元类(继承type,并覆盖new方法,并返回会父类的new方法返回值),就可以衍生出属于元类的类,但返回还是要调用type的new方法,默认传入三个参数
子类继承父类的元类

class ModelMeta(type):
    def __new__(cls, name, bases, attr): 
        if not attr.get('__tablename__'):    # 自定义
            attr['__tablename__'] = name
        return super().__new__(cls, name, bases, attr)
class Base(metaclass=ModelMeta):
    pass
class table(Base):
    pass

元类应用,元类new方法可以获得所属类定义时的类属性字典,并动态的在其中添加属性,实现类属性的定义,即元编程的定义:
元编程(Metaprogramming)是指某类计算机程序的编写,这类计算机程序编写或者操纵其他程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作。很多情况下与手工编写全部代码相比工作效率更高。编写元程序的语言称之为元语言,被操作的语言称之为目标语言。一门语言同时也是自身的元语言的能力称之为反射。Ruby语言

SQLAlchemy

参见SQLAlchemy介绍
是一个ORM(Object Relational Mapper)框架,大量使用了元编程
sqlalchemy.__version__: 查看sqlalchemy版本
经常使用的sqlalchemy类

import sqlalchemy      # sqlalchemy.create_engine
from sqlalchemy import Column, Integer, String, Enum, Date, ForeignKey, func, Text
from sqlalchemy.ext.declarative import declarative_base
import enum
from sqlalchemy.orm import sessionmaker, relationship

编程步骤:

  1. 创建引擎,引擎和数据库驱动通信,继而和数据库连接
    pymysql的连接
    url = ‘mysql+pymysql://username:password@host:port/dbname[?options]’
    ?charset=utf8
    engine = sqlalchemy.create_engine(url, encoding=’utf-8’, echo=True)
    echo default False, True 表示打印log
  2. 创建基类
    from sqlalchemy.ext.declarative import declarative_base
    Base = declarative_base()
  3. 创建实体类, 都继承自Base, 即表, 实例对应一条条记录

    from sqlalchemy import Column, Integer, String, 
    class MyEnum(enum.Enum):    # 定义MyEnum类
        F = 'F'
        M = 'M'
    class Students(Base):
        __tablename__ = 'students'    #必须有表名
        id = Column(Integer, primary_key=True, nullable=False, ForeignKey('school.cla',ondelete='CASCADE'))
        gender = Column(Enum(MyEnum), nullable=False)   
    
  4. 创建表
    Base.metabase.drop_all(engine) # 删除继承自Base的所有表
    Base.metabase.create_all(engine) # 创建继承自Base的所有表
    生产环境多用模型生成数据库, 再用脚本生成
  5. 实例化, 即创建记录, 两种方法都可以
    • student = Students(id=1, gender=’M’)
    • student = Students(); student.id = 1 …
  6. 创建会话类,并创建会话实例
    from sqlalchemy.orm import sessionmaker
    Session = sessionmaker(bind=engine)
    session = Session()
    session对象是线程不安全的,不同的线程要使用不同的session对象
    Session类和engine都是线程安全的
  7. CRUD操作create, read, update, delete
    增加:session.add(studentsobj), 需要提交session.commit()
    批量insert, session.add_all(iterable)
    查询: query(Students) -> query对象,可迭代为Students实例,
    query(Students).get(2) 通过主键get单条记录,没有记录返回None
    query方法只是生产sql语句,并不执行,迭代时才会执行
    改update: query到的对象属性被修改后再add,再提交
    删除delete: session.delete(studentsobj) 删除单条记录,记录对象必须存在,状态是persistent

定义外键
CONSTRAINT dept_emp_ibfk_1 FOREIGN KEY (emp_no) REFERENCES employees (emp_no) ON DELETE CASCADE
from sqlalchemy import ForeignKey
Column(Integer, ForeignKey(‘employees.emp_no’, ondelete=’CASCADE’), primary_key=True)

记录对象的状态:
sqlalchemy.orm.state.InstanceState 状态类
insp = sqlalchemy.inspect(entity: record_obj) 获得记录对象的状态信息
属性有:
insp.session_id, insp._attached
insp.transient, insp.persistent, insp.pending, insp.deleted, insp.detached
五种状态:
1. transient: 自定义的entity对象,
2. pending: add后的实体对象, 还没flush到数据库
3. persistent: 数据库中真是存在的实体对象,一定是通过query回来的
4. deleted: 实体被删除且已经flush到数据库,但为commit完成, session.flush()
5. detached: 删除实体并提交成功, 实体游离

查询

WHERE: session.query(Students).filter((Students.stuid > 10) & (Students.stuid < 15))
from sqlalchemy import in_, and_, or_ # 支持运算符重载&, |, ~取反
column.in_,column.like_,column.ilike # in, like, 忽略大小写ilike
ORDER BY:
order_by(Students.stuid.desc())
order_by(Students.stuid) => order_by(Students.stuid.asc())
多列排序: order_by(col_name).order_by(col_name2)
LIMIT:
limit(4).offset(10)

query方法返回一个Query对象,是一个查询结果的容器,可迭代

query.count() 查询的结果计算条目,子查询结果求count, 不如用func.count 聚合运算
query.all() -> list,所有查到的记录,查不到返回空list
query.first() -> 查到的第一条记录,查不到返回None
query.one() -> 查询结果有且只能有一行,多行抛异常
query.scalar() -> 返回one()返回元组的第一个元素
query.delete() -> commit,直接删除,查询到的记录
遍历query, 每一次都是一个元组

GROUP BY:
聚合和分组 from sqlalchemy import func
session.query(Students.gender, func.count(Students.stuid)).group_by(Students.gender)

多表查询

SELECT * FROM employees, dept_emp WHERE employees.emp_no == dept_emp.emp_no;
session.query(Employees, Dept_emp).filter(Employees.emp_no==Dept_emp.emp_no).all()
-> 返回一个每一条记录都是一个元组,分别是对应的记录对象
join方法
session.query(Employees).join(Dept_emp, (Employees.emp_no==Dept_emp.emp_no) & (1 or 1)).all()
join()不写条件,自动添加相同col_name的等值条件
这样的查询结果只会保存Employees的记录对象,Dept_emp的记录对象并不会保存
from sqlalchemy import relationship
在Employees类中定义,相关属性dept_emp = relationship('Dept_emp'), 这样就可以根据一个Employees记录对象,得到与其相关的Dept_emp记录对象.但是这样获得其实都是一次重新查询,即
session.query(Dept_emp).filter(Dept_emp.emp_no==record_obj.emp_no).all()
一次都查询到就用:
session.query(Employees, Dept_emp).join(Dept_emp, Employees.emp_no == Dept_emp.emp_no)

猜你喜欢

转载自blog.csdn.net/qq_33287645/article/details/81449086
今日推荐