query
此文算是自己的一個總結,不敢說對sqlalchemy有多精通,只能算是入門的總結,免得后面忘記了這些個基本的東西。數據庫的增,刪,改,查,前面已經介紹了session的增,刪,改,現在來介紹session的查,當然還有expression的查,這個就留到后面的文章來總結了。
同樣是由簡入難

1 from sqlalchemy import create_engine 2 3 engine = create_engine("mysql+pymysql://root:max123@127.0.0.1/test?charset=utf8", echo=True) 4 5 from sqlalchemy import * 6 from sqlalchemy.orm import relationship 7 from sqlalchemy.ext.declarative import declarative_base 8 from datetime import datetime, date 9 from sqlalchemy.orm import deferred 10 Base = declarative_base() 11 12 13 class Parent(Base): 14 __tablename__ = 'parent' 15 16 id = Column(Integer(), primary_key=True) 17 name = Column(String(50)) 18 summary = deferred(Column(Text)) 19 children = relationship('ParentChild', back_populates='parent', cascade='all,delete-orphan') 20 21 22 class Child(Base): 23 __tablename__ = 'child' 24 25 id = Column(Integer(), primary_key=True) 26 name = Column(String(50)) 27 parents = relationship('ParentChild', back_populates='child') 28 29 30 class ParentChild(Base): 31 __tablename__ = 'parent_child' 32 33 id = Column(Integer(), primary_key=True) 34 child_id = Column(Integer(), ForeignKey('child.id'), nullable=False) 35 parent_id = Column(Integer(), ForeignKey('parent.id'), nullable=False) 36 description = Column(String(100)) 37 38 parent = relationship('Parent', back_populates='children') 39 child = relationship('Child', back_populates='parents') 40 41 42 Base.metadata.drop_all(engine) 43 Base.metadata.create_all(engine) 44 45 46 from sqlalchemy.orm import sessionmaker 47 48 Session = sessionmaker(bind=engine) 49 50 db = Session() 51 52 child_one = Child(name='purk1') 53 child_two = Child(name='purk2') 54 child_three = Child(name='purk3') 55 child_four = Child(name='purk4') 56 parent_one = Parent(name='Wu1') 57 parent_two = Parent(name='Wu2') 58 parent_child_one = ParentChild(description='association one') 59 parent_child_two = ParentChild(description='association two') 60 parent_child_one.child = child_one 61 parent_child_two.child = child_two 62 parent_one.children.extend([parent_child_one, parent_child_two]) 63 64 db.add_all([parent_one, parent_two, child_four]) 65 db.commit()
1.delete
有依賴關系的是不能刪除的。
1 db.query(Parent).filter_by(name='wu1').delete(synchronize_session=False)
由於parent.id是ParentChild.parent_id的外鍵,直接使用這種方式刪除就會報錯了。不過可以使用session.delete(),此方式是可以處理relationship里面的配置,如果配置了cascade='all,delete-orphan',就會去刪除從表的數據了。
1 parent_child = db.query(ParentChild).filter_by(parent_id=1).all() 2 db.query(ParentChild).filter_by(parent_id=1).delete(synchronize_session='fetch') 3 db.commit() 4 print(parent_child[0].id)
這樣是可以刪除沒有依賴的表數據,但是synchronize_session到底有什么用,鄙人看文檔也沒看懂,希望高人來解答下
2. update
-> update(values, synchronize_session=’evaluate’, update_args=None)
1 db.query(Parent).filter_by(id=1).update({'name': Parent.name + '_update'}, synchronize_session=False) 2 db.query(Parent).filter_by(id=1).update({Parent.name: Parent.name + '_update'}, synchronize_session=False) 3 db.query(Parent).filter_by(id=2).update({Parent.name: case([(Parent.name == 'wu1', 'wu1_wu1')], else_='wu2_wu2')}, synchronize_session=False) 4 db.commit()
這兩種方式都可以
然后這個synchronize_session參數的一些含義,和delete一樣,也是沒有搞懂。
3. query
1) count query的方法,和func.count有點區別
1 db.query(Parent).count() 2 >>SELECT count(*) AS count_1 3 >>FROM (SELECT parent.id AS parent_id, parent.name AS >>parent_name FROM parent) AS anon_1 4 5 >> 2 6 7 db.query(func.count(1).label('cnt')).select_from(Parent).all() 8 >>SELECT count(%(count_1)s) AS cnt FROM parent 9 10 >>[(2,)]
count是query的方法,其返回的結果是integer,sql是子查詢,
func.count有一個參數,sql利用聚合函數。且返回的結果是list對象。
2) distinct 一個是query的方法,還有一個就是sqlalchemy的方法
1 db.query(Parent).distinct().all() 2 >>SELECT DISTINCT parent.id AS parent_id, parent.name AS parent_name FROM parent 3 4 >>[<__main__.Parent object at 0x0363B1B0>, <__main__.Parent object at 0x0363B550>] 5 6 7 db.query(distinct(Parent.name)).all() 8 >>SELECT DISTINCT parent.name AS anon_1 FROM parent 9 10 >>[('Wu1',), ('Wu2',)]
這兩個方法的不同之處是query的distinct不需要參數,sqlclchemy的distinct需要傳入參數。
3)exsits()
這個好像沒有什么特別的,看下面的代碼和sql log就明白了。
1 q = db.query(Parent).filter(Parent.name=='wu1') 2 3 db.query(q.exists()).all() 4 >>SELECT EXISTS (SELECT 1 FROM parent WHERE parent.name = %(name_1)s) AS anon_1 5 6 >>[(True,)] 7 8 9 db.query(Parent.name).filter(q.exists()).all() 10 >>SELECT parent.name AS parent_name FROM parent WHERE EXISTS (SELECT 1 FROM parent WHERE parent.name = %(name_1)s) 11 12 >>[('Wu1',), ('Wu2',)]
4) filter() 這個就牛X了
1 db.query(Parent).filter(Parent.name=='wu1').all() 2 >>> SELECT parent.id AS parent_id, parent.name AS parent_name 3 >>> FROM parent 4 >>> WHERE parent.name = %(name_1)s 5 >>> {'name_1': 'wu1'} 6 >>> [<__main__.Parent object at 0x0363B1B0>]
7 db.query(Parent).filter(Parent.name == 'wu1',Parent.name.like('Wu%')).all() 8 >>> SELECT parent.id AS parent_id, parent.name AS parent_name 9 >>> FROM parent 10 >>> WHERE parent.name = %(name_1)s AND parent.name LIKE %(name_2)s 11 >>> {'name_2': 'Wu%', 'name_1': 'wu1'} 12 >>> [<__main__.Parent object at 0x0363B1B0>]
13 db.query(Parent).filter(or_(Parent.name=='wu1',Parent.name=='wu2')).all() 14 >>> SELECT parent.id AS parent_id, parent.name AS parent_name 15 >>> FROM parent 16 >>> WHERE parent.name = %(name_1)s OR parent.name = %(name_2)s 17 >>> {'name_2': 'wu2', 'name_1': 'wu1'} 18 >>> [<__main__.Parent object at 0x0363B1B0>, <__main__.Parent object at 0x0363B550>]
19 db.query(Parent).filter(Parent.name=='wu1',Parent.id>1).all()
20 >>> SELECT parent.id AS parent_id, parent.name AS parent_name WHERE parent.name = %(name_1)s AND parent.id > %(id_1)s
21 >>> []
22 db.query(Parent).filter(Parent.name!='wu1',Parent.id>1).all() 23 >>> SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent WHERE parent.name != %(name_1)s AND parent.id > %(id_1)s 24 >>> {'id_1': 1, 'name_1': 'wu1'} 25 >>> [<__main__.Parent object at 0x0363B550>]
########################################下面這個實在對查詢出來的字段進行操作,真的是只有你想不到的,沒有他做不到的。 26 db.query(Parent.name +','+Parent.name,literal('developer').label('role'),case([(Parent.name=='wu1','is wu1'),(Parent.name=='wu2','is wu2')],else_='everything').label('case_column')).all() 27 >>>SELECT concat(concat(parent.name, %(name_1)s), parent.name) AS anon_1, %(param_1)s AS role, CASE WHEN (parent.name = %(name_2)s) THEN %(param_2)s WHEN (parent.name = %(name_3)s) THEN %(param_3)s ELSE %(param_4)s END AS case_column FROM parent 28 >>> {'name_2': 'wu1', 'param_3': 'is wu2', 'name_3': 'wu2', 'param_4': 'everything', 'name_1': ',', 'param_2': 'is wu1', 'param_1': 'developer'} 29 >>> [('Wu1,Wu1', 'developer', 'is wu1'), ('Wu2,Wu2', 'developer', 'is wu2')]
5) filter_by()
相較filter,就沒有那么靈活了,本身的便捷之處在於可以少些一個前綴(Parent),但是很多功能
>>> db.query(Parent).filter_by(name='wu1',id=1).all() SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent WHERE parent.id = %(id_1)s AND parent.name = %(name_1)s {'id_1': 1, 'name_1': 'wu1'} [<__main__.Parent object at 0x0363B1B0>]
>>> db.query(Parent).filter_by(name='wu1',name='wu2').all() # 'name'關鍵字一樣 報錯 SyntaxError: keyword argument repeated
>>> db.query(Parent).filter_by(or_(name='wu1',id=1)).all() # or_() 報錯 Traceback (most recent call last): File "<pyshell#45>", line 1, in <module> db.query(Parent).filter_by(or_(name='wu1',id=1)).all() TypeError: or_() got an unexpected keyword argument 'name'
>>> db.query(Parent).filter_by(name='wu1',id>1).all() # '>' 報錯 SyntaxError: non-keyword arg after keyword arg
>>> db.query(Parent).filter_by(name='wu1',id=1).all() SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent WHERE parent.id = %(id_1)s AND parent.name = %(name_1)s {'id_1': 1, 'name_1': 'wu1'} [<__main__.Parent object at 0x0363B1B0>]
6)group_by(*criterion) and having(criterion)
1 >>> db.query(Parent.name).join(Parent.children).group_by(Parent.id).having(func.count(Parent.id)>1).all() 2 >>> SELECT parent.name AS parent_name FROM parent INNER JOIN parent_child ON parent.id = parent_child.parent_id GROUP BY parent.id HAVING count(parent.id) > %(count_1)s 3 >>> {'count_1': 1} 4 >>> [('Wu1',)]
7) limit(limit) and offset(offset)
1 >>> db.query(Parent).limit(1).offset(0).all() 2 >>> SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent LIMIT %(param_1)s, %(param_2)s 3 >>> {'param_2': 1, 'param_1': 0} 4 >>> [<__main__.Parent object at 0x037CB310>]
limit 返回的行數 offset 其實偏移位置
8) order_by(*criterion)
1 >>> db.query(Parent).order_by(Parent.id,Parent.name).all() 2 >>> SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent ORDER BY parent.id, parent.name 3 >>> [<__main__.Parent object at 0x037BB6B0>, <__main__.Parent object at 0x037BB790>]
9) join(*props,**kwargs) and outerjoin(*props,**kwargs) 重點
以下的join關聯需要 relationship的配置(必備條件)
1 db.query(Parent).join(Parent.children).all() 2 >>> SELECT parent.id AS parent_id, parent.name AS parent_name 3 FROM parent INNER JOIN parent_child ON parent.id = parent_child.parent_id 4 5 db.query(Parent).join(ParentChild).all() 6 >>> SELECT parent.id AS parent_id, parent.name AS parent_name 7 FROM parent INNER JOIN parent_child ON parent.id = parent_child.parent_id 8 9 db.query(Parnet).join('ParentChild').all() #報錯 10 db.query(Parent).join('children').all() #說明join(str)時,str表示的relationship的配置,而不是被關聯對象的類名 11 >>> SELECT parent.id AS parent_id, parent.name AS parent_name 12 FROM parent INNER JOIN parent_child ON parent.id = parent_child.parent_id 13 14 db.query(ParentChild).join('parent','child').all() #報錯,說明這種方式的join是從左至右關聯的即 ParentChild.parent > Parent.child 15 >>> AttributeError: type object 'Parent' has no attribute 'child' 16 17 db.query(ParentChild).join(ParentChild.parent,ParentChild.child).all() #這種方式就是與順序無關了, 18 >>> SELECT parent_child.id AS parent_child_id, parent_child.child_id AS parent_child_child_id, parent_child.parent_id AS parent_child_parent_id, parent_child.description AS parent_child_description 19 FROM parent_child INNER JOIN parent ON parent.id = parent_child.parent_id INNER JOIN child ON child.id = parent_child.child_id 20 21 parent_child = db.query(ParentChild).subquery() 22 db.query(Parent).join(parent_child).all() 23 >>> SELECT parent.id AS parent_id, parent.name AS parent_name 24 FROM parent INNER JOIN (SELECT parent_child.id AS id, parent_child.child_id AS child_id, parent_child.parent_id AS parent_id, parent_child.description AS description 25 FROM parent_child) AS anon_1 ON parent.id = anon_1.parent_id
以下是join萬能關聯
db.query(Parent).join(ParentChild,Parent.id==ParentChild.parent_id).all() >>> SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent INNER JOIN parent_child ON parent.id = parent_child.parent_id db.query(Parent).join(ParentChild,or_(Parent.id==ParentChild.parent_id,ParentChild.description=='test')).all() >>> SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent INNER JOIN parent_child ON parent.id = parent_child.parent_id OR parent_child.description = %(description_1)s db.query((Parent.name + Parent.summary).label('test_join'),case([(Parent.name=='wu1','find_wu1'),(Parent.name=='wu2','find_wu2')],else_='nothing').label('test_case')).join(ParentChild,ParentChild.parent_id==Parent.id).all() >>> SELECT concat(parent.name, parent.summary) AS test_join, CASE WHEN (parent.name = %(name_1)s) THEN %(param_1)s WHEN (parent.name = %(name_2)s) THEN %(param_2)s ELSE %(param_3)s END AS test_case FROM parent INNER JOIN parent_child ON parent_child.parent_id = parent.id #####別名的使用,這樣可以join同一個表多次###### from sqlalchemy.orm import aliased parent_child_alias = aliased(ParentChild) db.query(Parent).join(ParentChild,Parent.id == ParentChild.parent_id).join(parent_child_alias,parent_child_alias.parent_id==Parent.id).all() >>> SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent INNER JOIN parent_child ON parent.id = parent_child.parent_id INNER JOIN parent_child AS parent_child_1 ON parent_child_1.parent_id = parent.id
最后一個內容就是返回query的結果集
10) all() first() one() scalar() get() one_or_none()
all: 返回所有數據
first: 所有rows里面取第一行,如果為沒有結果則為None,不會拋異常
one: 返回的rows必須是只有一行,有多行或者沒有數據都會拋異常
one_or_none: 返回的rows必須是一行或者沒有數據,有多行數據就會拋異常
scalar:返回的rows必須是一行或者沒有數據,有多行數據就會拋異常,並且scalar只會取result里面的第一個結果集
1 db.query(Parent).filter(Parent.name=='wu1').scalar() #取到的是Parent對象 2 >>> <__main__.Parent object at 0x0368BA10> 3 4 db.query(Parent.name,Parent.id).filter(Parent.name=='wu1').scalar() #取到的是只是Wu1 5 >>> 'Wu1'
11) cte() subquery()
cte和subquery返回的都是selecttables,表達是的訪問需要用 .c. attribute 。
12) from_self()
print(db.query(Parent).from_self()) >>> SELECT anon_1.parent_id AS anon_1_parent_id, anon_1.parent_name AS anon_1_parent_name FROM (SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent) AS anon_1 db.query(Parent).from_self().join(ParentChild).all() >>> SELECT anon_1.parent_id AS anon_1_parent_id, anon_1.parent_name AS anon_1_parent_name FROM (SELECT parent.id AS parent_id, parent.name AS parent_name FROM parent) AS anon_1 INNER JOIN parent_child ON anon_1.parent_id = parent_child.parent_id
13) select_from() 在不想要parent的數據只想要ParentChild時,當然在多表關聯且只要部分字段時就很有實用價值了。
db.query(ParentChild).select_from(Parent).join(ParentChild).all() >>> SELECT parent_child.id AS parent_child_id, parent_child.child_id AS parent_child_child_id, parent_child.parent_id AS parent_child_parent_id, parent_child.description AS parent_child_description FROM parent INNER JOIN parent_child ON parent.id = parent_child.parent_id