1、排序
-
排序概述:
-
order_by:可以指定根據這個表中的某個字段進行排序,如果在前面加了一個-,代表的是降序排序。
-
在模型定義的時候指定默認排序:有些時候,不想每次在查詢的時候都指定排序的方式,可以在定義模型的時候就指定排序的方式。有以下兩種方式:
-
relationship的order_by參數:在指定relationship的時候,傳遞order_by參數來指定排序的字段。
-
在模型定義中,添加以下代碼,即可讓文章使用標題來進行排序。
__mapper_args__ = { "order_by": title }
-
-
正序排序與倒序排序:默認是使用正序排序。如果需要使用倒序排序,那么可以使用這個字段的
desc()
方法,或者是在排序的時候使用這個字段的字符串名字,然后在前面加一個負號。
-
-
代碼示例:
2、limit、offset和切片操作
-
limit、offset和切片操作概述:
- limit:可以限制每次查詢的時候只查詢幾條數據。
- offset:可以限制查找數據的時候過濾掉前面多少條。
- 切片:可以對Query對象使用切片操作,來獲取想要的數據。可以使用
slice(start,stop)
方法來做切片操作。也可以使用[start:stop]
的方式來進行切片操作。一般在實際開發中,中括號的形式是用得比較多的。希望大家一定要掌握。示例代碼如下:
articles = session.query(Article).order_by(Article.id.desc())[0:10]
-
代碼示例:
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")) author = relationship("User",backref=backref("articles")) __mapper_args__ = { "order_by": create_time.desc() } def __repr__(self): return "<Article(title:%s,create_time:%s)>" % (self.title,self.create_time) # 倒序排序 articles = session.query(Article).all() print(articles)
3、懶加載
-
懶加載概述:
1、在一對多,或者多對多的時候,如果想要獲取多的這一部分的數據的時候,往往能通過一個屬性就可以全部獲取了。比如有一個作者,想要或者這個作者的所有文章,那么可以通過user.articles就可以獲取所有的。但有時候我們不想獲取所有的數據,比如只想獲取這個作者今天發表的文章,那么這時候我們可以給relationship傳遞一個lazy='dynamic',以后通過user.articles獲取到的就不是一個列表,而是一個AppenderQuery對象了。這樣就可以對這個對象再進行一層過濾和排序等操作。
2、通過lazy='dynamic'
,獲取出來的多的那一部分的數據,就是一個AppenderQuery
對象了。這種對象既可以添加新數據,也可以跟Query
一樣,可以再進行一層過濾。
總而言之一句話:如果你在獲取數據的時候,想要對多的那一邊的數據再進行一層過濾,那么這時候就可以考慮使用lazy='dynamic'
。
3、lazy可用的選項:select
:這個是默認選項。還是拿user.articles
的例子來講。如果你沒有訪問user.articles
這個屬性,那么sqlalchemy就不會從數據庫中查找文章。一旦你訪問了這個屬性,那么sqlalchemy就會立馬從數據庫中查找所有的文章,並把查找出來的數據組裝成一個列表返回。這也是懶加載。dynamic
:這個就是我們剛剛講的。就是在訪問user.articles
的時候返回回來的不是一個列表,而是AppenderQuery
對象。
-
代碼示例:
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) create_time = Column(DateTime,nullable=False,default=datetime.now) uid = Column(Integer,ForeignKey("user.id")) author = relationship("User",backref=backref("articles",lazy="dynamic")) def __repr__(self): return "<Article(title: %s)>" % self.title """ Base.metadata.drop_all() Base.metadata.create_all() user = User(username='zhilio') for x in range(100): article = Article(title="title %s" % x) article.author = user session.add(article) session.commit() """ from sqlalchemy.orm.collections import InstrumentedList from sqlalchemy.orm.dynamic import AppenderQuery from sqlalchemy.orm.query import Query user = session.query(User).first() # 是一個Query對象 print(user.articles.filter(Article.id > 50).all()) # 可以繼續追加數據進去 article =Article(title='title 100') user.articles.append(article) session.commit()
4、group_by
-
根據某個字段進行分組。比如想要根據性別進行分組,來統計每個分組分別有多少人,那么可以使用以下代碼來完成:
session.query(User.gender,func.count(User.id)).group_by(User.gender).all()
5、having
-
having是對查找結果進一步過濾。比如只想要看未成年人的數量,那么可以首先對年齡進行分組統計人數,然后再對分組進行having過濾。示例代碼如下:
result = session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age >= 18).all()
-
代碼示例:
from sqlalchemy import create_engine,Column,Integer,Float,Boolean,DECIMAL,Enum,Date,DateTime,Time,String,Text,func,and_,or_,ForeignKey,Table from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker,relationship,backref HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'first_sqlalchemy' USERNAME = 'root' PASSWORD = 'root' # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8mb4".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE) engine = create_engine(DB_URI) Base = declarative_base(engine) session = sessionmaker(engine)() 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") """ Base.metadata.drop_all() Base.metadata.create_all() 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() """ # 每個年齡的人數 # from sqlalchemy.orm.query import Query result = session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age < 18).all() print(result)
6、join
-
join概述:
- join分為left join(左外連接)和right join(右外連接)以及內連接(等值連接)。
- 參考的網頁:http://www.jb51.net/article/15386.htm
- 在sqlalchemy中,使用join來完成內連接。在寫join的時候,如果不寫join的條件,那么默認將使用外鍵來作為條件連接。
- query查找出來什么值,不會取決於join后面的東西,而是取決於query方法中傳了什么參數。就跟原生sql中的select 后面那一個一樣。
比如現在要實現一個功能,要查找所有用戶,按照發表文章的數量來進行排序。示例代碼如下:
result = session.query(User,func.count(Article.id)).join(Article).group_by(User.id).order_by(func.count(Article.id).desc()).all()
-
代碼示例:
#encoding: utf-8 from sqlalchemy import create_engine,Column,Integer,Float,Boolean,DECIMAL,Enum,Date,DateTime,Time,String,Text,func,and_,or_,ForeignKey,Table from sqlalchemy.dialects.mysql import LONGTEXT from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker,relationship,backref # 在Python3中才有這個enum模塊,在python2中沒有 import enum from datetime import datetime import random HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'first_sqlalchemy' USERNAME = 'root' PASSWORD = 'root' # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8mb4".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE) engine = create_engine(DB_URI) Base = declarative_base(engine) session = sessionmaker(engine)() class User(Base): __tablename__ = 'user' id = Column(Integer,primary_key=True,autoincrement=True) username = Column(String(50),nullable=False) def __repr__(self): return "<User(username: %s)>" % self.username 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")) author = relationship("User",backref="articles") def __repr__(self): return "<Article(title: %s)>" % self.title """ Base.metadata.drop_all() Base.metadata.create_all() user1 = User(username='zhiliao') user2 = User(username='ketang') for x in range(1): article = Article(title='title %s' % x) article.author = user1 session.add(article) session.commit() for x in range(1,3): article = Article(title='title %s' % x) article.author = user2 session.add(article) session.commit() """ # 找到所有的用戶,按照發表的文章數量進行排序 result = session.query(User,func.count(Article.id)).join(Article).group_by(User.id).order_by(func.count(Article.id).desc()).all() print(result) # sql語句: # select user.username,count(article.id) from user join article on user.id=article.uid group by user.id order by count(article.id) desc;
7、subquery
-
subquery概述:
子查詢可以讓多個查詢變成一個查詢,只要查找一次數據庫,性能相對來講更加高效一點。不用寫多個sql語句就可以實現一些復雜的查詢。那么在sqlalchemy中,要實現一個子查詢,應該使用以下幾個步驟:
- 將子查詢按照傳統的方式寫好查詢代碼,然后在
query
對象后面執行subquery
方法,將這個查詢變成一個子查詢。 - 在子查詢中,將以后需要用到的字段通過
label
方法,取個別名。 - 在父查詢中,如果想要使用子查詢的字段,那么可以通過子查詢的返回值上的
c
屬性拿到。
整體的示例代碼如下:
stmt = session.query(User.city.label("city"),User.age.label("age")).filter(User.username=='李A').subquery() result = session.query(User).filter(User.city==stmt.c.city,User.age==stmt.c.age).all()
- 將子查詢按照傳統的方式寫好查詢代碼,然后在
-
代碼示例:
from sqlalchemy import create_engine,Column,Integer,Float,Boolean,DECIMAL,Enum,Date,DateTime,Time,String,Text,func,and_,or_,ForeignKey,Table from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker,relationship,backref HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = 'first_sqlalchemy' USERNAME = 'root' PASSWORD = 'root' # dialect+driver://username:password@host:port/database DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8mb4".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE) engine = create_engine(DB_URI) Base = declarative_base(engine) session = sessionmaker(engine)() 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 __repr__(self): return "<User(username: %s)>" % self.username """ Base.metadata.drop_all() Base.metadata.create_all() 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這個人在同一個城市,並且是同年齡的人 user = session.query(User).filter(User.username=='李A').first() users = session.query(User).filter(User.city==user.city,User.age==user.age).all() print(users) # 子查詢 stmt = session.query(User.city.label("city"),User.age.label("age")).filter(User.username=='李A').subquery() result = session.query(User).filter(User.city==stmt.c.city,User.age==stmt.c.age).all() print(result)