1三、Flask实战第13天:SQLAlchemy操做MySQL数据库

安装MySQL

在MySQL官网下载win版MySQLphp

双击运行java

后面根据提示设置密码而后启动便可,这里我设置的密码是:123456python

咱们能够经过Navicat客户端工具链接上MySQLmysql

address: 127.0.0.1
port: 3306
username: root
password: 123456
链接信息

 建立一个数据库heboan,字符集设置为utf8c++

安装pymysql和sqlalchemy

#进入cmd窗口,而后进入虚拟环境进行安装
workon flask-env
pip install pymysql
pip install sqlalchemy

 使用SQLAlchemy链接数据库web

在项目目下建立一个文件learn_sql.py,而后运行此文件sql

 

ORM介绍

 随着项目愈来愈大,采用原生SQL的方式在代码中会出现大量的SQL语句,那么问题就出现:数据库

  一、SQL语句重复利用率不高,越复杂的SQL语句条件越多,代码越长;会出现不少相近的SQL语句编程

  二、不少SQL语句是在业务逻辑中拼出来的,若是数据须要更改,就要去修改这些逻辑,比较容易出错flask

  三、写SQL时容易忽略web安全问题,给将来形成隐患,好比sql注入

ORM,全称Object Relationship Mapping,中文名叫作对象关系映射,经过ORM咱们可使用类的方式去操做数据库,而不用再写原生的SQL语句。经过表映射成类,把行做实例,把字段做属性。ORM在执行对象操做的时候最终仍是会把对应的操做转换为数据库原生语句。使用ORM有许多优势:

  一、易用性:使用ORM作数据库的开发能够有效的减小重复SQL语句的几率,写出来的模型也更加直观、清晰

  二、性能损耗小:ORM转换成底层数据库操做指令确实会有一些开销,但从实际状况来看,这种性能损耗不足5%

  三、设计灵活:能够轻松写出复杂的查询

  四、能够移植性:SQLAlchemy封装了底层的数据库实现,支持多个关系型数据库引擎,能够轻松的切换数据库

 

定义ORM模型并将其映射到数据库中(建立表)

运行以后,查看数据库,person表已经建立好了

 注意:一旦使用Base.metadata.create_all()将模型映射到数据库后,即便改变了模型的字段,也不会从新映射了

 

SQLAlchemy对数据库增删改查操做 

SQLAlchemy使用ORM对数据库操做须要构建一个session对象,全部操做都必须经过中国session会话对象来实现

from sqlalchemy.orm import sessionmaker
...

session = sessionmaker(engine)()

说明:

sessionmasker和上面的declarative_base同样,把engine做为参数实例化出一个对象

  Session = sessionmaker(engine)

可是这样还不能直接使用Session,咱们须要调用Session,咱们知道直接实例.()这样运行直接上会调用__call__()方法

  session = Session()

为了简介,咱们就能够写成这样

  session = sessionmaker(engine)()

def add_data():
    p = Person(name='heboan', age=18)
    session.add(p)
    session.commit()

add_data()




#一次添加多条数据
def add_data():
    p1 = Person(name='user1', age=21)
    p2 = Person(name='user2', age=22)
    session.add_all([p1,p2])
    session.commit()

add_data()
def search_data():
    # 查找某个模型表的全部数据
    # all_person = session.query(Person).all()
    # for p in all_person:
    #     print(p)

    # 使用filter_by做条件查询
    # all_person = session.query(Person).filter_by(name='heboan').all()
    # for p in all_person:
    #     print(p)

    # 使用filter做条件查询
    # all_person = session.query(Person).filter(Person.name=='heboan').all()
    # for p in all_person:
    #     print(p)
    
    # 使用get方法查找,get方法是根据主键ID来查找的,只会返回一条数据或None
    person = session.query(Person).get(1)
    print(person)

search_data()
def update_data():
    # 先找出要改的对象
    person = session.query(Person).first()
    # 直接改属性
    person.age = 20
    session.commit()

update_data()
def delete_data():
    persons = session.query(Person).filter_by(name='heboan')
    for p in persons:
        session.delete(p)
    session.commit()

delete_data()

 

SQLAlchemy属性经常使用数据类型

default: 默认值
nullable: 是否可空
primary_key: 是否为主键
unique: 是否惟一
autoincrement: 是否自增加
name: 该属性再数据库中的字段映射
onupdate: 当数据更新时会自动使用这个属性
  好比update_time = Colum(DateTime, onupdate=datetime.now, default=datetime.now)
Colum经常使用属性
Integer: 整形
Float: 浮点型,后面只会保留4位小数,会有精度丢失问题,占据32位
Double: 双精度浮点类型,占据64位,也会存在精度丢失问题
DECIMAL: 定点类型,解决浮点类型精度丢失问题;若是精度要求高,好比金钱,则适合用此类型
Boolean: 传递True/False进行
enum: 枚举类型
Date: 传递datetime.date()进去
Datetime: 传递datetime.datetme()进去
Time: 传递datetime.time()进去   
String: 字符类型,使用时须要指定长度,区别于Text类型
Text: 文本类型,通常能够存储6w多个字符
LONGTEXT: 长文本类型
  from sqlalchemy.dialects.mysql import LONGTEXT
  由于LONGTEXT只在MySQL数据库中存在
经常使用数据类型
...
from sqlalchemy import Column, Integer, String, Enum, Date, Float, DECIMAL, Text, Boolean, DateTime
import datetime
...


class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), unique=True, nullable=False)
    email = Column(String(100), unique=True, nullable=False)
    gender = Column(Enum('m', 'f'), default='m')
    birthday = Column(Date)
    height = Column(Float)
    wages = Column(DECIMAL(10,4)) #10表示10个阿拉伯数字,4表明4位小数,如100000.1234
    introduction = Column(Text)
    is_delete = Column(Boolean, default=False)
    update_time = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)

Base.metadata.create_all()


session = sessionmaker(engine)()

user = User()
user.username = 'heboan'
user.email = 'love@heboan.com'
user.gender = 'm'
user.birthday = datetime.date(2000, 12, 12)
user.height = 1.72
user.wages = 200000.1234
user.introduction = 'hello, my name is heboan....'

session.add(user)
session.commit()
示例

 

query函数可查询的数据

为了方便演示,我先建一个Student表,而后写入10条数据

...
import random

...
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)
    score = Column(Integer, nullable=False)

    def __repr__(self):
        return '<Student-id:{},name:{},score:{}>'.format(self.id,self.name,self.score)

Base.metadata.create_all()
session = sessionmaker(engine)()

for i in range(1, 11):
    student = Student(name='user{}'.format(i), score=random.randint(20,100))
    session.add(student)
    session.commit()
建立测试数据

一、模型对象。指定查找这个模型的全部对象

result = session.query(Student).all()
print(result)

#[<Student-id:1,name:user1,score:98>, <Student-id:2,name:user2,score:42>,...]

二、模型属性。指定查找某个模型的其中几个属性

result = session.query(Student.id, Student.name).first()
print(result)

#(1, 'user1')

三、聚合函数

  func.count 统计行的数量
  func.avg 求平均值
  func.max 求最大值
  func.min 求最小值
  func.sum 求和

...
from sqlalchemy import func
...

result = session.query(func.avg(Student.score)).all()
print(result)

#[(Decimal('55.6000'),)]
示例

 

filter方法经常使用的过滤条件

#查找id为1的记录
article = session.query(Article).filter(Article.id == 1).first()
equal
#查找title不是c++的记录
articles = session.query(Article).filter(Article.title != 'c++').all()
not equal
#查找title以p开头的记录
articles = session.query(Article).filter(Article.title.like('p%')).all()
like
#查找标题不是php也不是java的记录

#方式1:
articles = session.query(Article).filter(Article.title.notin_(['php', 'java'])).all()

#方式2:
articles = session.query(Article).filter(~Article.title.in_(['php', 'java'])).all()
in
#查找detail为空的记录
articles = session.query(Article).filter(Article.detail == None).all()
is null
#查找detail不为空的记录
articles = session.query(Article).filter(Article.detail != None).all()
is not null
//查找title是python而且detail是python111的记录

#方法一:直接使用关键字参数
articles = session.query(Article).filter(Article.title=='python', Article.detail=='python111').all()

#方法二,导入and_方法,from sqlalchemy import and_
articles = session.query(Article).filter(and_(Article.title=='python', Article.detail=='python111')).all()
View Code
#查找title是python,或者detail是python111的记录

导入or_方法, from sqlalchemy import or_
articles = session.query(Article).filter(or_(Article.title=='python', Article.detail=='python111')).all()
or

 

外键及其四种约束

在MySQL中,外键可让表之间的关系更加紧密。而SQLAlchemy一样支持外键。经过ForeignKey类来实现。而且能够指定表的外键约束。

from sqlalchemy import ForeignKey

外键的约束有如下几项

  一、RESTRICT : 父表数据被删除,会阻止删除。默认是此项

  二、NO ACTION : 在MySQL中,同RESTRICT同样

  三、CASCADE : 级联删除

  四、SET NULL : 父表被删除,子表数据会设置为NULL(该字段容许为NULL)

...
from sqlalchemy import ForeignKey
...

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)


class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text)
    #注意:这里的uid的数据类型必需要和user表中的id数据类型一致
    uid = Column(Integer, ForeignKey('user.id', ondelete='RESTRICT'))

Base.metadata.create_all()
session = sessionmaker(engine)()

user = User(username='heboan')
session.add(user)
session.commit()

article = Article(title='hello world', content='this is a test line', uid=1)
session.add(article)
session.commit()
示例

ondelete设置外键约束,没有设置该属性则为RESTRICT, 上面咱们设置为了RESTRICT

由于咱们设置了外键约束是RESTRICT,因此如今删除父表user中id为1 的记录是会被阻止的

 

ORM层面(代码)删除数据,会无视MySQL级别的外键约束。直接将对应的数据删除,而后将从表中的那个外键设置为NULL,若是想要避免这种行为,应该将从表中的外键的nullable=False

ORM层外键和一对多关系

基于上面的状况,就是有两个表,分别为user 和article。他们两之间存在一个外键关系

需求:根据文章找到用户信息原始的作法以下

article = session.query(Article).first()
uid = article.uid
user = session.query(User).get(uid)
print(user)

 上面的方法不够方便,orm能够这样实现

...
from sqlalchemy.orm import relationship
...

class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text)
    uid = Column(Integer, ForeignKey('user.id', ondelete='RESTRICT'))
    author = relationship("User")  #添加做者属性,经过relationship指向user模型表


article = session.query(Article).first()
print(article.author)
orm经过实现relationship外键查找

咱们也能够经过user 来找到该做者发表的文章,一个做者可能有发表多篇文章,这是一对多关系。一样的也是relationship实现

...
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    articles = relationship('Article')
View Code
user = session.query(User).get(2)
for article in user.articles:
    print(article.title)

上面咱们两个模型都互相作了relationship实现,这样比较麻烦,还有一种更简便的方法

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)


class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text)
    uid = Column(Integer, ForeignKey('user.id', ondelete='RESTRICT'))
    author = relationship("User", backref="articles" ) #这样user模型表就自动拥有了articles属性关联
反向映射

有了上面的关系后,咱们添加文章就能够这样

...
user = session.query(User).get(2)
article1 = Article(title='aaa', content='11aa')
article1.author = user

session.add(article1)
session.commit()
添加文章方法1
...
user = session.query(User).get(2)
article1 = Article(title='bb', content='11bb')
user.articles.append(article1)

session.add(user)
session.commit()
添加文章方法2

一对一关系

 举个例子,把user表一些不经常使用查询的字段放到另一种表user_extent,这种状况下,这两张表就是一对一的关系

若是想要将两个模型映射成一对一的关系,那么应该在父模型中,指定引用的时候,须要传递一个‘uselist=False’参数,就是告诉父模型,之后引用这个从模型的时候,再也不是一个列表,而是一个对象

from sqlalchemy.orm import backref
...

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

class User_Extent(Base):
    __tablename__ = 'user_extent'
    id = Column(Integer, primary_key=True, autoincrement=True)
    school = Column(String(50))
    uid = Column(ForeignKey('user.id'))
    user = relationship('User', backref=backref('extend', uselist=False))
示例

一对一关系映射后,当咱们把一个用户设置2个学校就会失败

所以只能一个user只能对应user_extend中的一条记录

 

多对多关系

场景: 一遍文章能够有多个标签,一个标签能够对应多篇文章

运行后,能够发现数据库成功建立了三张表

咱们能够看看建立表的语句

如今咱们能够写入数据

...
article1 = Article(title='Python开发')
article2 = Article(title='Java进阶')

tag1 = Tag(name='编程')
tag2 = Tag(name='计算机')

article1.tags.append(tag1)
article1.tags.append(tag2)

article2.tags.append(tag1)
article2.tags.append(tag2)

session.add_all([article1, article2])
session.commit()
写入数据

...
article = session.query(Article).get(1)
for tag in article.tags:
    print(tag.name)
查询文章id为1(Python开发)的标签有哪些
...
tag = session.query(Tag).get(1)
for article in tag.articles:
    print(article.title)
查询标签是'编程'的文章有哪些

 

relationship方法中的cascade参数详解

若是将数据库的外键设置为RESTRICT,那么在ORM层面,删除了父表中的数据,那么从表中的数据将会NULL。若是不想要这种状况发生,那么应该将这个值的nullable=False。

在SQLAlchemy,只要将一个数据添加到session中,和其余相关联的数据能够一块儿存入到数据库中了。这些是怎么设置的呢?实际上是经过relationship的时候,有一个关键字参数cascade能够这是这些属性:

  一、save-update:默认选项。在添加一条数据的时候,会把其余和其余关联的数据都添加到数据中

  二、delete: 表示当删除某一个模型的数据的时候,是否也删掉使用relationship和他关联的数据

  三、delete-orphan: 表示当对一个ORM对象解除了父表中的关联对象的时候,本身便会被删除掉。固然若是父表中的数据被删除,本身也会被删除。这个选项只能用在一对多上,不能用在多对多以及多对以上。而且还须要再子模型中的relationship中,增长一个single_parent=True的参数

  四、merge:默认选项。当在使用session.merge合并一个对象的时候,会将使用了relationship相关联的对象也进行merge操做

  五、expunge: 移除操做的时候,会将相关联的对象也进行移除。这个操做只是从session中移除,并不会真正的从数据中删除

  六、all: 是对save-update, merge, refresh-expire, expunge, delete几种的缩写

 

排序

order_by:根据表中的某个字段进行排序,若是前面加了一个"-",表明的是降序

class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, nullable=False, default=datetime.now)

    def __str__(self):
        return "<title:{},create_time:{}>".format(self.title, self.create_time)

    def __repr__(self):
        return self.__str__()
表结构

插入两条测试数据

如今按照文章发表时间排序

article=session.query(Article).order_by(Article.create_time).all()
print(article)

#结果:[<title:title1,create_time:2018-07-08 10:33:17>, <title:title2,create_time:2018-07-08 10:34:00>]
升序
article=session.query(Article).order_by(Article.create_time.desc()).all()
print(article)

#结果:[<title:title2,create_time:2018-07-08 10:34:00>, <title:title1,create_time:2018-07-08 10:33:17>]
降序

方法二:

#升序
article=session.query(Article).order_by('create_time').all()

#降序
article=session.query(Article).order_by('-create_time').all()
示例

__mapper_args__ : 定表模型的时候肯定默认的排序规则

class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, nullable=False, default=datetime.now)
    __mapper_args__ = {
        "order_by": create_time.desc()   #根据发表时间降序
    }

    def __str__(self):
        return "<title:{},create_time:{}>".format(self.title, self.create_time)

    def __repr__(self):
        return self.__str__()

...


article=session.query(Article).all()
print(article)

#结果
[<title:title2,create_time:2018-07-08 10:34:00>, <title:title1,create_time:2018-07-08 10:33:17>]
定义默认排序规则

 

limit、offset以及切片操做

...

class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, nullable=False, default=datetime.now)

    def __str__(self):
        return "<title:{},create_time:{}>".format(self.title, self.create_time)

    def __repr__(self):
        return self.__str__()



Base.metadata.drop_all()
Base.metadata.create_all()
session = sessionmaker(engine)()

#写入100条测试数据
for x in range(1,101):
    time.sleep(2)
    title = "title {}".format(x)
    article = Article(title=title)
    session.add(article)
    session.commit()
建立表并写入测试数据

limit:能够限制每次查询的时候只查询几条数据

#查询出所有的数据
articles = session.query(Article).all()
print(articles)


#经过limit查出10条
articles = session.query(Article).limit(10).all()
print(articles)
limit
articles = session.query(Article).order_by(Article.create_time.desc()).limit(10).all()
print(articles)
查出最近发表的10篇文章

offset: 能够限制查找数据的时候过滤前面多少条

#从第5条后面开始查询出10条数据
articles = session.query(Article).offset(5).limit(10).all()
print(articles)


#结果
[<title:title 6,create_time:2018-07-08 12:32:03>,...<title:title 15,create_time:2018-07-08 12:32:21>]
offset

切片: 能够对Query对象使用切片操做,来获取想要的数据

#切片有两种方式:

#需求:找出最近发表的10篇文章

#一、是slice(start,end)
articles = session.query(Article).order_by(Article.create_time.desc()).slice(0, 10).all()
print(articles)

#二、和列表同样的操做
articles = session.query(Article).order_by(Article.create_time.desc())[0:10]
print(articles)
切片

 

数据查询懒加载技术

 在一对多,或者多对多的时候,若是想要获取多的这一部分的数据的时候,每每能经过一个属性就能够获取所有了。好比有一个做者,想要获取这个做者的全部文章,那么能够经过user.articles就能够。但有时候咱们不想获取全部的数据,好比只想获取这个做者今天发表的文章,那这时候咱们就能够给relationship传递一个lazy='dynamic',之后经过user.articles获取到的就不是一个列表,而是一个AppendQuery对象了。这样就能够对这个对象再进行一层过滤和排序等操做

lazy可用的选项:

一、'select':这个是默认选选项,好比‘user.articles’的例子,若是你没有访问‘user.articles’这个属性,那么sqlalchemy就不会从数据库中查找文章。一旦你访问了这个属性,那么sqlalchemy就会立马从数据库中查找全部的文章。这也是懒加载。

二、‘dynamic’:就是在访问‘user.articles’的时候返回的不是一个列表,而是一个AppendQuery对象

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

    def __str__(self):
        return self.username

    def __repr__(self):
        return self.__str__()


class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, nullable=False, default=datetime.now)
    uid = Column(Integer, ForeignKey('user.id'), nullable=False)

    author = relationship(User, backref=backref('articles', lazy='dynamic'))   #lazy='dynamic'

    def __str__(self):
        return "<title:{},create_time:{}>".format(self.title, self.create_time)

    def __repr__(self):
        return self.__str__()



Base.metadata.drop_all()
Base.metadata.create_all()
session = sessionmaker(engine)()

#写入测试数据
user = User(username='heboan')
session.add(user)
session.commit()

user = session.query(User).first()

for x in range(1,101):
    time.sleep(2)
    title = "title {}".format(x)
    article = Article(title=title)
    article.author = user
    session.add(article)
    session.commit()
dynamic

上面配置了lazy='dynamic',并写入了测试数据,咱们能够看看‘user.articles’类型为AppendQuery

...
user = session.query(User).first()
print(type(user.articles))


#结果
<class 'sqlalchemy.orm.dynamic.AppenderQuery'>
示例

既然user.articles为AppendQuery类型,那么它就有query拥有的属性,好比(filter、order_by等等),还能够append数据,以下

...
user = session.query(User).first()
# filter
print(user.articles.filter(Article.create_time == '2018-07-08 16:55:05').all())
#结果: [<title:title 2,create_time:2018-07-08 16:55:05>]

#append
article = Article(title='python is simple')
user.articles.append(article)
session.commit()
示例

 

group_by和having子句

...

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    age = Column(Integer, default=0)
    gender = Column(Enum('male', 'female', 'secret'), default='male')

    def __str__(self):
        return self.username

    def __repr__(self):
        return self.__str__()


# Base.metadata.drop_all()
Base.metadata.create_all()
session = sessionmaker(engine)()


user1 = User(username='王五', age=17, gender='male')
user2 = User(username='赵四', age=17, gender='male')
user3 = User(username='张三', age=18, gender='female')
user4 = User(username='张伟', age=19, gender='female')
user5 = User(username='刘海', age=20, gender='female')

session.add_all([user1, user2, user3, user4, user5])
session.commit()
建立测试表和数据

group_by

根据某个字段进行分组。好比想要根据年龄进行分组,来统计每一个分组分别有多少人

from sqlalchemy import func
...

#每一个年龄的人数
result = session.query(User.age, func.count(User.id)).group_by(User.age).all()
print(result)    


#结果
[(17, 2), (18, 1), (19, 1), (20, 1)]
每一个年龄的人数

查看sql语句

having

having是对查找结果进一步过滤。好比只想要当作年人的数量,那么能够首先对年龄进行分组统计人数,而后再对分组进行having过滤

#每一个年龄的人数
result = session.query(User.age, func.count(User.id)).group_by(User.age).having(User.age >= 18).all()
print(result)

#结果
[(18, 1), (19, 1), (20, 1)]
按年龄分组并查当作年人人数

 

join实现复杂查询

是使用SQLAlemy 的join功能以前,咱们先了解原生的sql语句中的left join, right join, inner join用法

#建立表a
CREATE TABLE a(
aID int(1) AUTO_INCREMENT PRIMARY KEY,
aNUM char(20)
);

#建立表b
CREATE TABLE b(
bID int(1) AUTO_INCREMENT PRIMARY KEY,
bNUM char(20)
);

#插入数据到表a
INSERT INTO a VALUES
( 1, 'a20050111' ),
( 2, 'a20050112' ),
( 3, 'a20050113' ),
( 4, 'a20050114' ),
( 5, 'a20050115' );

#插入数据到表b
INSERT INTO b VALUES
( 1, ' 2006032401'),
( 2, '2006032402' ),
( 3, '2006032403' ),
( 4, '2006032404' ),
( 8, '2006032408' );
建立a,b两张表

表a记录以下

表b记录以下

left join(左联接)

结果说明:

left join 是以a表的记录为基础,a能够当作左表,B能够当作右表,left join是以左表为准的,换句话说,左表(a)的记录将会所有表示出来,而右表(b)只会显示复合搜索条件的记录,B表记录不足的地方均为NULL.

right join(右联接)

结果说明:

和left join的结果恰好相反,此次是以右表(b)为基础的,a表不足的地方用null填充

inner join(内联接)

结果说明

很明显,这里只显示出了 a.aID = b.bID的记录.这说明inner join并不以谁为基础,它只显示符合条件的记录. 

SQLAlcemy使用join

...
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

    def __str__(self):
        return self.username

    def __repr__(self):
        return self.__str__()

class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    uid = Column(Integer, ForeignKey('user.id'), nullable=False)
    author = relationship(User, backref='articles')

    def __str__(self):
        return self.title

    def __repr__(self):
        return self.__str__()


# Base.metadata.drop_all()
# Base.metadata.create_all()
session = sessionmaker(engine)()

user1 = User(username='jack')
user2 = User(username='slina')


#用户jack有3篇文章
for x in range(3):
    article = Article(title='jack title {}'.format(x))
    article.author = user1
    session.add(article)
    session.commit()

#用户slina有5篇文章
for x in range(5):
    article = Article(title='slina title {}'.format(x))
    article.author = user2
    session.add(article)
    session.commit()
建立表及数据

 需求:查询出每一个用户发表的文章数,并按倒序排序

咱们先用原生sql写一遍:

一、先把两张表内链接查询

二、使用user.id进行分组统计出每一个用户的文章数

三、最后把查询出来的结果依据文章数进行倒序排序

咱们再来用sqlalchemy写出来

看下他的SQL语句是怎么样的

 注意2点:

  在sqlalchemy中,使用join来完成内联接。在写join的时候,若是不写join条件(User.id==Article=uid),那么默认使用外键做为条件链接

  query查找出什么值,不会取决于join后面的东西,而是取决于query方法中传了什么参数。就跟原生sql 中的select后面那个同样

 

subquery实现复杂查询

子查询可让多个查询变成一个查询,只要查找一次数据库,性能相对来说更高效一点

...
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    city = Column(String(50), nullable=False)
    age = Column(Integer, default=0)

    def __str__(self):
        return self.username

    def __repr__(self):
        return self.__str__()


# Base.metadata.drop_all()
Base.metadata.create_all()
session = sessionmaker(engine)()

user1 = User(username='李A', city="长沙", age=18)
user2 = User(username='王B', city="长沙", age=18)
user3 = User(username='赵C', city="北京", age=18)
user4 = User(username='张D', city="长沙", age=20)

session.add_all([user1, user2, user3, user4])
session.commit()
建立测试表及数据

需求:寻找和李A这我的在同一个城市,而且是同年龄的人

咱们可能会这样查询:

上面的方法能够实现需求,可是却须要查询两次数据库,这样一定会下降数据库查询性能,咱们可使用子查询的方式来实现需求:

原生sql子查询以下

使用sqlalchemy实现

在子查询中,将之后须要用到的字段经过 label方法取个别名

在父查询中,若是想要使用子查询的字段,那么能够经过子查询的返回值的 c属性难道

相关文章
相关标签/搜索