xml地图|网站地图|网站标签 [设为首页] [加入收藏]
TDD的一点想法和实践,python数据库编程
分类:编程

面向对象设计(OOD)有助于我们开发出高性能、易扩展以及易复用的程序。其中,OOD有一个重要的思想那就是依赖倒置原则(DIP)。

1.1 约束条件

约束条件与数据类型的宽度一样,都是可选参数

作用:用于保证数据的完整性和一致性

主要分为:

PRIMARY KEY (PK)    标识该字段为该表的主键,可以唯一的标识记录

FOREIGN KEY (FK)    标识该字段为该表的外键

NOT NULL    标识该字段不能为空

UNIQUE KEY (UK)    标识该字段的值是唯一的

AUTO_INCREMENT    标识该字段的值自动增长(整数类型,而且为主键)

DEFAULT    为该字段设置默认值



UNSIGNED 无符号

ZEROFILL 使用0填充

 

说明:

  1. 是否允许为空,默认NULL,可设置NOT NULL,字段不允许为空,必须赋值

2. 字段是否有默认值,缺省的默认值是NULL,如果插入记录时不给字段赋值,此字段使用默认值

sex enum('male','female') not null default 'male'

age int unsigned NOT NULL default 20 必须为正值(无符号) 不允许为空 默认是20

  1. 是否是key

主键 primary key

外键 foreign key

索引 (index,unique...)

 

大纲

数据库的基本操作

函数属性&对象方法

连接数据库

一楼的ORM技术(sqlalchemy)

 

依赖倒置原则(DIP):一种软件架构设计的原则(抽象概念)

1.1.1 not null,default

not null - 不可空

null - 可空

默认值,创建列时可以指定默认值,当插入数据时如果未主动设置,则自动添加默认值

create table t4(x char(4) not null);

create table user(id int,name char(16),sex enum('male','female') not null default 'male');

 

 

数据库的基本操作

创建数据库:

CREATE DATABASE test;

GRANT ALL ON test.* to user(s);

第一行创建了一个名为“test”的数据库,假设你是数据库的管理员,第二行语句可以为指定用户(或所有用户)提升权限

使用数据库

USE test;

如果你已经登录一个数据库系统,但是还没有选择你希望使用的数据库,这条简单的语句可以让你指定一个数据库,用来执行数据库操作。

删除数据库

DROP DATABASE test;

这条简单的语句可以从数据库中移除所有表和数据,并将其从系统中删除。

创建表

CREATE TABLE users (login VARCHAR(8), userid INT, projid INT);

这条语句创建一个新表,其中包含字符串列login,以及两个整型列:userid和projid。

删除表

DROP TABLE users;

这条简单的语句可以删除数据库中的一个表,并清空其中的所有数据。

插入行

INSERT INTO users VALUES('leanna',2111,1);

可以使用INSERT语句向数据库中插入一个新行。需要指定表名以及其中每列的值。对于本例而言,字符串“leanna”对应于login参数,而2111和1分别对应于userid和projid。

更新行

UPDATE users SET projid=4 WHERE projid=2;

UPDATE users SET projid=1 WHERE userid=311;

为了修改表中已存在的行,需要使用UPDATE语句。使用SET来确定要修改的列,并提供条件来确定修改的行。在第一个例子中,所有“projid”为2的用户需要改为4。而在第二个例子中,将指定用户(这里是userid为311的用户)移到编号为#1的项目组中。

删除行

DELETE FROM users WHERE projid=%d;

DELETE FROM users;

为了删除表中的行,需要使用DELETE FROM 命令,指定准备删除的行的表名以及可选的条件。如果没有这个条件,就会像第二个例子样,把所有行都删除了。

以上就是数据库的一些基本的操作,从上我们可以看出:

创建数据库(create)和删除数据库(drop),创建表和删除表也一样。

修改表中已存在的行叫做更新(update)和往数据库里添加新行叫做插入(insert)

而移出表中已存在的行叫做删除(delete)。

这些动作通常称为数据库命令或操作。使用可选的条件请求数据库中的行称为询查(query)。

 

控制反转(IoC):一种反转流、依赖和接口的方式(DIP的具体实现方式)

1.1.2 primary key

    约束字段不为空并且唯一

    依据:主键是innodb的表组织数据的依据

注意:一张表中必须有且只能有一个主键

一个表中可以:

单列做主键

多列做主键(复合主键)

create table t5(

        id int primary key auto_increment,

        name char(20)

    );

    insert into t5(name) values('egon'),('alex');



    select name from t5 where id = 1000;

 

 

    

 

    了解:联合主键

  create table t6(

        id int,

        name char(20),

        primary key(id,name)

    );

 

 

函数属性&对象方法

依赖注入(DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)

1.1.3 unique key

   

 create table service(

        id int primary key auto_increment,

        name char(20) unique

    );

 

 

    not null+unique:会在没有指定主键的情况下被识别为主键

    

create table service1(

        id int,

        name char(20) not null unique

    );



    create table service2(

        id int primary key auto_increment,

        name char(20) unique,

        ip char(15),

        port int,

        unique(ip,port)

    );

    insert into service2(name,ip,port) values

    ('nginx','192.168.1.10',80),

    ('haproxy','192.168.1.10',8080),

    ('mysql','192.168.1.10',3306);



    insert into service2(name,ip,port) values

    ('nginx1','192.168.1.10',80);

 

 

connect()函数属性

user                     用户名
password               密码
host                     主机名
database               数据库名  MySQLdb数据库使用的是db
dsn                      数据源名
connect()函数通过 Connection 对象访问数据库。兼容模块必须实现 connect()函数,该函数创建并返回一个 Connection 对象。

应用与数据库之间进行通信需要建立数据库连接。它是最基本的机制,只有通过数据库连接才能把命令传递到服务器,并得到返回的结果。当一个连接(或一个连接池)建立后,可以创建一个游标,向数据库发送请求,然后从数据库中接收回应。

 

Connection 对象方法
Connection 对象不需要包含任何数据属性

close()                                关闭数据库连接
commit()                                提交当前事务
rollback()                              取消当前事务
cursor()                                使用该连接创建(并返回)一个游标或类游标的对象
errorhandler(cxn,cur,errcls,errval)          作为给定连接的游标的处理程序                                

 

当使用 close()时,这个连接将不能再使用,否则会进入到异常处理中。

如果数据库不支持事务处理,或启用了自动提交功能,commit()方法都将无法使用。如果你愿意,可以实现单独的方法用来启动或关闭自动提交功能。

处理的数据库而言,只需要在方法中实现“pass”即可。

和 commit()相似,rollback()方法也只有在支持事务处理的数据库中才有用。发生异常之后,rollback()会将数据库的状态恢复到事务处理开始时。

如果 RDBMS 不支持游标,那么 cursor()仍然会返回一个尽可能模仿真实游标的对象。这是最基本的要求。每个适配器开发者都可以为他的接口或数据库专门添加特殊属性。

当你完成数据库连接并关闭游标时,需要对所有操作执行 commit(),并对你的连接执行close()。

 

IoC容器:依赖注入的框架,用来映射依赖,管理对象创建和生存周期(DI框架)、

1.1.4 auto_increment

约束字段为自动增长,被约束的字段必须同时被key约束

    

show variables like '%auto%';

 

    # 结果

    auto_increment_increment    | 1 # 步长

    auto_increment_offset       | 1 # 起始位置

    #强调:起始位置的值必须<=步长

基于全局级别的

   

 set global auto_increment_increment=5;

 set global auto_increment_offset=3;

 

如果auto_increment_offset的值大于auto_increment_increment的值,则auto_increment_offset的值会被忽略

   

 create table t7(id int unique auto_increment);

    insert into t7 values();



    set global auto_increment_increment=1;

    set global auto_increment_offset=1;

 

Cursor 对象

arraysize                          使用fetchmany方法时,一次取出的结果行数,默认为1
connection                         创建此游标的链接(可选)
description                        返回游标活动状态(7选项元组):(name,type_code,display_size,internal_size,precision,scale,null_ok)
lastrowid                          上次修改的行ID(可选:如果不支持行ID,则返回None)
rowcount                           上次execute*()方法处理或影响的行数
callproc(func[,args])              调用储存过程
close()                              关闭游标
execute(op[,args])                执行数据库查询或命令
executemany(op,args)             类似execute()和map()的结合,为给定的所有参数准备执行的数据库查询或命令
fetchone()                           获取查询结果的下一行
fetchmany([size=cursor.arraysize])      获取查询结果的下面size行
fetchall()                            获取查询结果的所有(剩余)行
__iter__()                           为游标创建迭代器对象(可选,参考next())
messages                            游标执行后从数据库中获取的消息列表(元组结合,可选)
next()                                被迭代器用于获取查询结果的下一行(可选,类似fetchone(),参考__iter__())
nextset()                             移动到下一个结果集合(如果支持)
rownumber                            当前结果集中游标的索引(以行为单位,从0开始,可选)
setinputsizes(sizes)               设置允许的最大输入大小(必须有,但是实现是可选的)
setoutputsize(size[,col])         设置大列获取的最大缓冲区大小(必须有,但是实现是可选的)

当建立连接后,就可以和数据库进行通信了。游标可以让用户提交数据库命令,并获得查询的结果行。

当游标创建好后,就可以执行查询或命令(或多个查询和命令),并从结果集中取回一行或多行结果。

游标对象最重要的属性是 execute*()和 fetch*()方法,所有针对数据库的服务请求都是通过它们执行的。arraysize 数据属性在为 fetchmany()设置默认大小时非常有用。当然,在不需要时关闭游标是个好主意,而如果你的数据库支持存储过程,可能会用到callproc()。

 

Unity:是微软推出的IOC框架

1.1.5 foregn key

    创建表:

        1、先创建被关联的表:部门表

        #强调:被关联的字段必须唯一

       

 create table dep(

            id int primary key auto_increment,

            name char(10)

        );

 

        2、再创建关联表:员工表

 

  create table emp(

            id int primary key auto_increment,

            name char(16),

            age int,

            dep_id int,

            foreign key(dep_id) references dep(id)

            on delete cascade

            on update cascade

        );

 

 

插入记录时:

        #1、先往被关联表插入:部门表

      

  insert into dep(name) values

        ('外交部'),

        ('技术哈哈部'),

        ('销售部');

 

 

        #2、再往关联表插入:员工表

       

 insert into emp(name,age,dep_id) values

        ('egon',19,1),

        ('alex',74,2),

        ('wxx',38,2),

        ('yxx',22,3);

 

 

        #错误操作

        

 insert into emp(name,age,dep_id) values

        ('lhf',29,4);

 

 

 delete from dep where id=2;

delete from emp where dep_id=2;

 

 

 

类对象和构造函数

Date (yr, mo, dy)                  日期值对象
Time (hr, min, sec)                  时间值对象
Timestamp (yr, mo, dy, hr, min, sec)         时间戳值对象
DateFromTicks (ticks)                日期对象,给出从新纪元时间(1970 年 1 月 1 日 00:00:00 UTC)以来的秒数
TimeFromTicks (ticks)                时间对象,给出从新纪元时间(1970 年 1 月 1 日 00:00:00 UTC)以来的秒数
TimestampFromTicks (ticks)             时间戳对象,给出从新纪元时间(1970 年 1 月 1 日 00:00:00 UTC)以来的秒数
Binary (string)                   对应二进制(长)字符串对象
STRING                       表示基于字符串列的对象,比如 VARCHAR
BINARY                        表示(长)二进制列的对象,比如 RAW、BLOB
NUMBER                       表示数值列的对象
DATETIME                      表示日期/时间列的对象
ROWID                       表示“行 ID”列的对象

 

TDD:测试驱动开发,是敏捷开发中的一项核心实践和技术,也是一种设计方法论

1.2 表之间关系

分析步骤:

#1、先站在左表的角度去找

是否左表的多条记录可以对应右表的一条记录,如果是,则证明左表的一个字段foreign key 右表一个字段(通常是id)

 

#2、再站在右表的角度去找

是否右表的多条记录可以对应左表的一条记录,如果是,则证明右表的一个字段foreign key 左表一个字段(通常是id)

连接数据库

以MySQL为例:

打开cmd输入:pip3 install mysqlclient    安装mysql驱动

>>> import MySQLdb as mysql         导入MySQL驱动重名为mysql
>>> conn = mysql.connect('127.0.0.1','root','root')            连接数据库
>>> conn = mysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root', db='test', charset='utf8')        也可以使用关键字连接数据库
>>> config = {
    'host': '127.0.0.1',
    'port': 3306,
    'user': 'root',
    'passwd': 'root',
    'db': 'test',
    'charset': 'utf8'
}
>>> conn = mysql.connect(**config)        也可以使用字典连接数据库
>>> cur = conn.cursor()      创建cursor对象
>>> cur.execute('create table users (id varchar(20) primary key, name varchar(20))')         创建users表
>>> id = 1
>>> name = "smelond"
>>> cur.execute("insert into users values('%s','%s')" % (id,name))            插入一行记录,注意MySQL的占位符是%s
>>> cur.execute("update users set id=2 where id=1")                      修改一行记录
>>> cur.execute("select * from users")                    查询记录
>>> values = cur.fetchall()                    将查询的记录赋给values
>>> values                                         输出values
(('2', 'smelond'),)
>>> cur.execute("insert into users values(3,'admin')")                     再次添加记录
>>> cur.execute("select * from users")
>>> values = cur.fetchall()
>>> values
(('2', 'smelond'), ('3', 'admin'))

>>> conn.commit()             提交
>>> cur.close()              关闭cursor
>>> conn.close()                关闭connection

 

 

1.2.1 多对一

三张表:出版社,作者信息,书

书    出版社 

一个出版社可以出版多本书

如果只有步骤1成立,则是左表多对一右表

如果只有步骤2成立,则是右表多对一左表

ORM技术(sqlalchemy)

在Python中,最有名的ORM框架是SQLAlchemy

安装SQLAlchemy

pip3 install sqlalchemy

# 导入模块
from sqlalchemy import Column, String, create_engine, INT
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 创建对象的基类:
Base = declarative_base()


# 定义User对象
class User(Base):
    __tablename__ = "users"

    # 表的结构
    id = Column(INT, primary_key=True)
    name = Column(String(20))


# 初始化数据库连接
# create_engine()用来初始化数据库连接。SQLAlchemy用一个字符串表示连接信息
engine = create_engine("mysql://root:root@127.0.0.1:3306/test")
# 创建DBSession类型
DBSession = sessionmaker(bind=engine)

向数据库表中添加一行记录。
由于有了ORM,我们向数据库表中添加一行记录,可以视为添加一个User对象:

# 创建session对象
session = DBSession()
# 创建新User对象
new_user = User(name='smelond')  # 由于我在数据库里面设置id为主键,并且自动增长,所以这个地方可以不带id。
# 添加到session
session.add(new_user)
# 提交即保存到数据库
session.commit()
# 关闭session
session.close()

关键是获取session,然后把对象添加到session,最后提交并关闭。DBSession对象可视为当前数据库连接。

查询记录:

# 创建session对象
session = DBSession()
# 创建Query查询,filter是where条件,最后调用one()返回唯一行,如果条用all()则返回所有行:
result = session.query(User).filter(User.name == 'smelond').one()
print("type:", type(result))
print("name:", result.name)
session.close()


结果:
type: <class '__main__.User'>
name: smelond

 从上面我们可以看出“还是ORM比较好用”。

 

上面的内容都是抄的(英文不好,放在这里备查),相关的内容看了一些,不明觉厉。

1.2.2 多对多

三张表:出版社,作者信息,书

多对多:一个作者可以写多本书,一本书也可以有多个作者,双向的一对多,即多对多

作者    书

如果步骤1和2同时成立,则证明这两张表时一个双向的多对一,即多对多,需要定义一个这两张表的关系表来专门存放二者的关系

 

1.2.3 一对一:fk+unique

    客户表  学生表

如果1和2都不成立,而是左表的一条记录唯一对应右表的一条记录,反之亦然。这种情况很简单,就是在左表foreign key右表的基础上,将左表的外键字段设置成unique即可

曾经写了一个酒店读写房卡的程序,不同的酒店门锁是不一样的,同一个酒店也有可能更换门锁,程序流程:

1.3 单表查询

1.通过Api获取当前酒店的门锁类型

1.3.1 单表语法:

    select

        distinct

        字段1,

        字段2,...

    from

        表名

    where 约束条件

    group by 分组条件

    having 过滤条件

    order by 排序字段

    limit n;

 

1.找到表:from

2.拿着where指定的约束条件,去文件/表中取出一条条记录

3.将取出的一条条记录进行分组group by,如果没有group by,则整体作为一组

4.将分组的结果进行having过滤

5.执行select

6.去重

7.将结果按条件排序:order by

8.限制结果的显示条数

2.如果需要则下载对应门锁的dll

1.3.1.1  简单查询

       

select * from emp;

select id,name from emp;

 

 

去除重复

      

  select distinct post from emp;

 

四则运算

       

 select name,salary*12 as annual_salary from emp;

 select name,salary*12 annual_salary from emp;

 

 

3.实现读写功能

1.3.1.2    定义显示格式

     

   select

            concat('姓名: ',name) as new_name,

            concat('年薪: ',salary*12) as annual_salary

        from

            emp;

 

#CONCAT() 函数用于连接字符串

     

select concat(name,'_SB') as new_name from emp;



select concat(name,'_SB') as new_name from emp where name != 'egon';

 

结合CASE语句:

 

 select

            (

                case

                when name = 'egon' then

                    name

                when name = 'alex' then

                    concat(name,'_BIGSB')

                else

                    concat(name,'_SB')

                end

            ) as new_name

        from

            emp;

 

       

CONCAT_WS() 第一个参数为分隔符

       

 select concat(name,':',salary,':',sex) from emp;

 select concat_ws(':',name,salary,sex) from emp;

 

 

一、定义接口

1.3.2 where约束

where字句中可以使用:

 

  1. 比较运算符:> < >= <= <> !=

  2. between 80 and 100 值在10到20之间

  3. in(80,90,100) 值是10或20或30

  4. like 'egon%'

    pattern可以是%或_,

    %表示任意多字符

    _表示一个字符

  1. 逻辑运算符:在多个条件直接可以使用逻辑运算符 and or not

    

select id,name,age from emp where age > 20;

    select id,name,age from emp where age >= 20 and age <= 38;

    select id,name,age from emp where age between 20 and 38;

    select id,name,age from emp where age <= 20 or age >= 38;

    select id,name,age from emp where age not between 20 and 38;



    select * from emp where age=18 or age=28 or age=38;

    select * from emp where age in (18,28,38);



    select * from emp where age=18 or sex='male';



    select * from emp where name like 'jin%';

    select * from emp where name like '__';

 

新建一个类库(Lock.Interface),代码如下:

1.3.3 group by分组

    分组依据应该找重复度比较高的字段

分组之后只能查到分组字段,或者聚合的结果

首先明确一点:分组发生在where之后,即分组是基于where之后得到的记录而进行的

分组指的是:将所有记录按照某个相同字段进行归类,比如针对员工信息表的职位分组,或者按照性别进行分组等

为何要分组呢?

    取每个部门的最高工资

    取每个部门的员工数

    取男人数和女人数

小窍门:‘每’这个字后面的字段,就是我们分组的依据

大前提:

    可以按照任意字段分组,但是分组完毕后,比如group by post,只能查看post字段,如果想查看组内信息,需要借助于聚合函数

由于没有设置ONLY_FULL_GROUP_BY,于是也可以有结果,默认都是组内的第一条记录,但其实这是没有意义的

单独使用GROUP BY关键字分组

    

SELECT post FROM employee GROUP BY post;

 

    注意:我们按照post字段分组,那么select查询的字段只能是post,想要获取组内的其他相关信息,需要借助函数

 

GROUP BY关键字和GROUP_CONCAT()函数一起使用

    SELECT post,GROUP_CONCAT(name) FROM employee GROUP BY post;#按照岗位分组,并查看组内成员名

    SELECT post,GROUP_CONCAT(name) as emp_members FROM employee GROUP BY post;

 

GROUP BY与聚合函数一起使用

select post,count(id) as count from employee group by post;#按照岗位分组,并查看每个组有多少人

 

 

强调:

如果我们用unique的字段作为分组的依据,则每一条记录自成一组,这种分组没有意义

多条记录之间的某个字段值相同,该字段通常用来作为分组的依据

 set global sql_mode='strict_trans_tables,only_full_group_by'; #设置成功后,一定要退出,然后重新登录方可生效

    select post,max(salary) from emp group by post;

    select post,min(salary) from emp group by post;

    select post,sum(salary) from emp group by post;

    select post,avg(salary) from emp group by post;

    select post,count(id) from emp group by post;

    select post,group_concat(name) from emp group by post;



    select count(id) from emp;

    select count(id) from emp where max(salary) > 1;

 

 

namespace Lock
{
    public interface ILock
    {
        /// <summary>
        /// 门锁初始化
        /// </summary>
        /// <param name="password">初始化密码</param>
        /// <returns></returns>
        bool Init(int password);

    }
}

1.3.4 having 过滤条件

HAVING与WHERE不一样的地方在于!!!!!!

#!!!执行优先级从高到低:where > group by > having

#1. Where 发生在分组group by之前,因而Where中可以有任意字段,但是绝对不能使用聚合函数。

#2. Having发生在分组group by之后,因而Having中可以使用分组的字段,无法直接取到其他字段,可以使用聚合函数

   

 select post,avg(salary) from emp group by post having avg(salary) > 20000;

 

是不是算是IoC了?(我不确定)

1.3.5 order by排序

按单列排序

   

SELECT * FROM employee ORDER BY salary;

SELECT * FROM employee ORDER BY salary ASC;

SELECT * FROM employee ORDER BY salary DESC;

 

按多列排序:先按照age排序,如果年纪相同,则按照薪资排序

    

SELECT * from employee

        ORDER BY age,

        salary DESC;



    select * from emp order by age asc; # 默认升序,从小到大

    select * from emp order by age desc; #从大到小



    select * from emp order by age asc,id desc;



    select post,avg(salary) from emp group by post order by avg(salary);

 

二、单元测试

1.3.6 limit n

SELECT * FROM employee ORDER BY salary DESC

        LIMIT 3;                    #默认初始位置为0



    SELECT * FROM employee ORDER BY salary DESC

        LIMIT 0,5; #从第0开始,即先查询出第一条,然后包含这一条在内往后查5条



    SELECT * FROM employee ORDER BY salary DESC

        LIMIT 5,5; #从第5开始,即先查询出第6条,然后包含这一条在内往后查5条

 

 

 

练习:分页显示,每页5条

    

select * from emp limit 3;

    select * from emp limit 0,5;

    select * from emp limit 5,5;

    select * from emp limit 10,5;

    select * from emp limit 15,5;





def from(db,table):

    f=open(filepath,'r')

    return f



def where(f,pattern):

    for line in f:

        if pattern:

            yield line



def group():

    pass



def having():

    pass



def distinct():

    pass



def order():

    pass



def limit():

    pass







def select():

    f=from(db,table)

    lines=where(f,'id > 1')

    group_res=group(lines)

    having_res=having(group_res)

    distinct_res=distinct(having_res)

    order_res=order(distinct_res)

    res=limit(order_res)

    print(res)

 

新建一个单元测试项目(Lock.Tests),代码如下:

1.3.7 正则

select * from emp where name regexp '^jin.*$';

SELECT * FROM employee WHERE name REGEXP 'on$';

SELECT * FROM employee WHERE name REGEXP 'm{2}'

 

 

小结:对字符串匹配的方式

WHERE name = 'egon';

WHERE name LIKE 'yua%';

WHERE name REGEXP 'on$';

 

本文由澳门新葡亰手机版发布于编程,转载请注明出处:TDD的一点想法和实践,python数据库编程

上一篇:自学课程,创建工作集并将element加入工作集中 下一篇:没有了
猜你喜欢
热门排行
精彩图文