文章目錄
- 一、sqlalchemy中的==查詢==分析
- 1、基本查詢
- filter和filter_by的區別:
- 如何查看sqlalchemey的sql語句
- 表中單個字段查詢
- first()
- ==one()==
- get 根據主鍵查詢
- limit 限制查詢結果
- offset() 限制前面n個,顯示後面n+1個,向後便宜Nge
- slice() 切片,也可以直接使用[1,9]
- order_by() 元素排序 順序
- desc() 逆序排序
- ==like()== 模糊搜索,結合佔位符%使用 與原生sql一致
- ilike() 模糊搜索,不區分大小寫
- notlike()
- ==in_()== 判斷在xx裏邊
- notin_
- is_ 是xxx
- isnot
- or_
- 2、聚合函數(在sqlalchemy.func中)
- 二、進階操作
- ==1、反向查詢==
- 2、數據批量操作
- 數據庫==批量數據==的插入:
- 1) 批量插入10000條數據:
- 2)如何讓執行的 SQL 語句增加前綴?
- 3)如何替換一個已有主鍵的記錄?
- 4)如何使用無符號整數?
- 5)模型的屬性名需要和表的字段名不一樣怎麼辦?
- 6)如何獲取字段的長度?
- 7)如何指定使用 InnoDB,以及使用 UTF-8 編碼?
- 數據庫==批量數據==的 ==刪除DELETE==:
- 3、複製`copy`對象,並新建數據到db
- 三、其他sql知識點
一、sqlalchemy中的查詢分析
1、基本查詢
filter和filter_by的區別:
- filter可以在後邊加判斷條件,但是每次只能過濾一個條件
- filter_by可以過濾多個,與Django的orm的方式類似,但是隻能用“=”
如何查看sqlalchemey的sql語句
在不添加結尾的
all()``first()
等,得到的具體查詢對象,直接利用str
方法,便可將其轉換爲sql語句
row = session.query(User).filter(User.username!='aaa')
print(row)
結果
SELECT user.id AS user_id, user.username AS user_username, user.password AS user_password, user.createtime AS user_createtime, user._locked AS user__locked
FROM user
WHERE user.username != %(username_1)s
表中單個字段查詢
print(session.query(User.username).filter(User.username!='dandan').all())
first()
僅查詢顯示第一個
print(session.query(User.username).filter(User.username!='dandan').first())
one()
只查詢出來第一個,有且只有一個
如果有兩個符合條件的,會報錯
print(session.query(User).filter(User.username=='choupi').one())
結果
<User(id='12' ,username='choupi',password='q1',createtime='2018-03-09 20:27:54',_locked ='False',)>
get 根據主鍵查詢
主鍵在表中只有一個
例如ID爲主鍵,查詢id=3的元素
print(session.query(User).get(3))
# 結果
<User(id='3' ,username='tree',password='zzz111',createtime='2018-03-09 14:44:21',_locked ='False',)>
limit 限制查詢結果
limit(3) 僅查出3條結果
print(session.query(User).filter(User.username!='dandan').limit(3).all())
# 結果
[<User(id='2' ,username='tobee',password='234qwe',createtime='2018-03-07 16:16:05',_locked ='False',)>, <User(id='3' ,username='tree',password='zzz111',createtime='2018-03-09 14:44:21',_locked ='False',)>, <User(id='4' ,username='aaa',password='111',createtime='2018-03-09 17:57:15',_locked ='False',)>]
offset() 限制前面n個,顯示後面n+1個,向後便宜Nge
顯示第N個以後
print(session.query(User.username).filter(User.username!='dandan').all())
print(session.query(User.username).filter(User.username!='dandan').limit(3).all())
print(session.query(User.username).filter(User.username!='dandan').offset(3).all())
結果:
[('tobee',), ('tree',), ('aaa',), ('coding',), ('choupi',), ('111',), ('choupidan',), ('youku',)]
[('tobee',), ('tree',), ('aaa',)]
[('coding',), ('choupi',), ('111',), ('choupidan',), ('youku',)]
slice() 切片,也可以直接使用[1,9]
slice(1,3) 與python的slice一致,從0開始 左閉右開,顯示1,2兩個元素
print(session.query(User.username).filter(User.username!='dandan').slice(1,3).all())
# 結果
[('tree',), ('aaa',)]
order_by() 元素排序 順序
print(session.query(User.username).filter(User.username!='dandan').order_by(User.username).all())
# 按數字字符順序排序
[('111',), ('aaa',), ('choupi',), ('choupidan',), ('coding',), ('tobee',), ('tree',), ('youku',)]
desc() 逆序排序
from sqlalchemy import desc
print(session.query(User.username).filter(User.username!='dandan').order_by(desc(User.username)).all())
# 逆序排序
[('youku',), ('tree',), ('tobee',), ('coding',), ('choupidan',), ('choupi',), ('aaa',), ('111',)]
like() 模糊搜索,結合佔位符%使用 與原生sql一致
print(session.query(User.username).filter(User.username.like('%e')).all())
# 結果
[('tobee',), ('tree',)]
ilike() 模糊搜索,不區分大小寫
# 內部實現如下類似操作:
lower(a) LIKE lower(other)
# 同理還有 notilike() 方法,
# 跟like()用法一致,只是不區分大小寫。
# 這個是sqlalchemy ORM 層做的強化,不是數據庫層的用法
notlike()
print(session.query(User.username).filter(User.username.notlike('%e')).all())
# 結果
[('dandan',), ('aaa',), ('coding',), ('choupi',), ('111',), ('choupidan',), ('youku',)]
in_() 判斷在xx裏邊
print(session.query(User.username).filter(User.username.in_(['dandan','aaa'])).all())
# 結果
[('dandan',), ('aaa',)]
notin_
print(session.query(User.username).filter(User.username.notin_(['dandan','aaa'])).all())
# 結果
[('tobee',), ('tree',), ('coding',), ('choupi',), ('111',), ('choupidan',), ('youku',)]
is_ 是xxx
兩種表達方式 None
print(session.query(User.username).filter(User.username==None).all())
print(session.query(User.username).filter(User.username.is_(None)).all())
isnot
filter支持多條件查詢
print(session.query(User.username).filter(User.username.isnot(None),User.password=='111').all())
# 結果
[('aaa',), ('111',)]
or_
from sqlalchemy import or_
print(session.query(User.username).filter(or_(User.username.isnot(None),User.password=='111')).all())
# 結果
[('dandan',), ('tobee',), ('tree',), ('aaa',), ('coding',), ('choupi',), ('111',), ('choupidan',), ('youku',)]
2、聚合函數(在sqlalchemy.func中)
count / group_by
from sqlalchemy import func
print(session.query(User.password,func.count(User.id)).group_by(User.password).all())
查詢原生sql
SELECT user.password AS user_password, count(user.id) AS count_1
FROM user GROUP BY user.password
[('111', 2), ('123asd', 1), ('234qwe', 1), ('333', 1), ('choupidan', 1), ('q1', 1), ('qwer', 1), ('zzz111', 1)]
having
having字句可以讓我們篩選成組後的各種數據,where字句在聚合前先篩選記錄,也就是說作用在group by和having字句前。
而having子句在聚合後對組記錄進行篩選。真實表中沒有此數據,這些數據是通過一些函數生存。
print(session.query(User.password,func.count(User.id)).group_by(User.password).\
having(func.count(User.id)>1).all())
# 結果
[('111', 2)]
sum
print(session.query(User.password,func.sum(User.id)).group_by(User.password).all())
# 結果
[(‘111’, Decimal(‘18’)), (‘123asd’, Decimal(‘1’)), (‘234qwe’, Decimal(‘2’)), (‘333’, Decimal(‘16’)), (‘choupidan’, Decimal(‘15’)), (‘q1’, Decimal(‘12’)), (‘qwer’, Decimal(‘5’)), (‘zzz111’, Decimal(‘3’))]
max
print(session.query(User.password,func.max(User.id)).group_by(User.password).all())
# 結果
[(‘111’, 14), (‘123asd’, 1), (‘234qwe’, 2), (‘333’, 16), (‘choupidan’, 15), (‘q1’, 12), (‘qwer’, 5), (‘zzz111’, 3)]
min
print(session.query(User.password,func.min(User.id)).group_by(User.password).all())
# 結果
[(‘111’, 4), (‘123asd’, 1), (‘234qwe’, 2), (‘333’, 16), (‘choupidan’, 15), (‘q1’, 12), (‘qwer’, 5), (‘zzz111’, 3)]
lable 別名
lable別名不能用在having中
extract 提取
提取時間元素
from sqlalchemy import extract
print(session.query(extract('minute',User.createtime).label('minute'),func.count(User.id)).group_by('minute').all())
基於分鐘排序
[(16, 1), (27, 1), (29, 3), (44, 1), (52, 1), (57, 2)]
基於天數排序
print(session.query(extract('day',User.createtime).label('day'),func.count(User.id)).group_by('day').all())
[(6, 1), (7, 1), (9, 7)]
二、進階操作
1、反向查詢
一對多反向查詢
- sqlalchemy的數據庫層
class TaskTemplateTable(db.Model, SessionMixin):
"""
任務模板綁定的表單模板
"""
__tablename__ = 'patrol_inspect_tasktemplatetable'
# True: 顯示點位信息
# False 不顯示點位信息
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
table_template_id = db.Column(db.Integer(), db.ForeignKey('patrol_inspect_tabletemplate.id'))
task_template = db.relationship('TaskTemplate')
- 查詢層
table_obj = db.session.query(TaskTemplateTable).filter_by(id=1).first()
# 要先拿到查詢表下的對象,再根據對象,進行關聯表的反向查詢
print(table_obj.tamplate_table.name)
>>> 'ups巡檢記錄表'
2、數據批量操作
數據庫批量數據的插入:
- 一般的操作都爲,每一條數據提交一次的方式,這樣相當影響性能,可以通過執行原生sql或者sqlalchemy支持的批量導入數據的方式。進行批量插入
- 轉化爲sql:
insert into 表名 values(...),(...)...;
1) 批量插入10000條數據:
db.session.execute(
User.__table__.insert(),
[{'name': `randint(1, 100)`,'age': randint(1, 100)} for i in xrange(10000)]
)
session.commit()
2)如何讓執行的 SQL 語句增加前綴?
使用 query 對象的 prefix_with() 方法:
session.query(User.name).prefix_with('HIGH_PRIORITY').all()
session.execute(User.__table__.insert().prefix_with('IGNORE'), {'id': 1, 'name': '1'})
3)如何替換一個已有主鍵的記錄?
使用 session.merge() 方法替代 session.add(),其實就是 SELECT + UPDATE:
user = User(id=1, name='ooxx')
session.merge(user)
session.commit()
或者使用 MySQL 的 INSERT … ON DUPLICATE KEY UPDATE
,需要用到 @compiles 裝飾器,有點難懂,自己看吧:《SQLAlchemy ON DUPLICATE KEY UPDATE》 和 sqlalchemy_mysql_ext。
4)如何使用無符號整數?
可以使用 MySQL 的方言:
from sqlalchemy.dialects.mysql import INTEGER
id = Column(INTEGER(unsigned=True), primary_key=True)
5)模型的屬性名需要和表的字段名不一樣怎麼辦?
開發時遇到過一個奇怪的需求,有個其他系統的表裏包含了一個“from”
字段,這在 Python 裏是關鍵字,於是只能這樣處理了:
from_ = Column('from', CHAR(10))
6)如何獲取字段的長度?
Column
會生成一個很複雜的對象,想獲取長度比較麻煩,這裏以 User.name
爲例:
User.name.property.columns[0].type.length
7)如何指定使用 InnoDB,以及使用 UTF-8 編碼?
最簡單的方式就是修改數據庫的默認配置。如果非要在代碼裏指定的話,可以這樣:
class User(BaseModel):
__table_args__ = {
'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8'
}
數據庫批量數據的 刪除DELETE:
- 當查詢結果只有一個對象時,可直接調用改對象的delete方法即可
dbm.AccessToken.query.filter_by(id=1).delete()
- 但當結果不止一個時,直接這麼操作,會報錯。
- 刪除記錄時,默認會嘗試刪除 session 中符合條件的對象,而這裏還不支持,所以報錯
1) 批量刪除數據:
# 方式一:
token_obj_list = dbm.AccessToken.query.filter(text(sql_text)).params(**sql_params).all()
for obj in token_obj_list:
db.session.delete(obj)
db.session.commit()
# 方式二:
# 這裏不讓session同步
delte_count = AccessToken.query.filter(text(sql_text)).params(**sql_params).delete(synchronize_session=False)
print(delete_count)
session.commit()
3、複製copy
對象,並新建數據到db
consume_obj = dbm.ConsumeOrderHistory.query.filter_by(id=raw_index).first()
# 直接copy對象,會造成session會話衝突,這個不能直接用copy的方式來操作
# consume_record = copy.deepcopy(consume_obj)
# 通過make_transient來實現
from sqlalchemy.orm.session import make_transient
logger.info(consume_obj) # <pay_proxy.dbmodels.ConsumeOrderHistory object at 0x7f3ff62ac350>
logger.info(type(consume_obj)) # <class 'pay_proxy.dbmodels.ConsumeOrderHistory'>
logger.info("開始處理對象 ... ")
make_transient(consume_obj)
logger.info(consume_obj) # <pay_proxy.dbmodels.ConsumeOrderHistory object at 0x7f3ff62ac350> ,對象的地址都沒變,只是去掉了session鏈接
logger.info(type(consume_obj)) # <class 'pay_proxy.dbmodels.ConsumeOrderHistory'>
logger.info(dir(consume_obj)) # 仍然包含源對象的所有方法
"""
- 處理前後的兩個對象是完全相等的!
- 經過其處理之後的對象,不再跟session綁定,此時只需要將其主鍵作相應替換即可實現複製一份數據到db
"""
# 兩種方式:
# consume_obj.id = None # 1、 主鍵設null,主鍵設置了autoincrement,則會自動生成
delattr(consume_obj, 'id') # 2、刪除其主鍵屬性,commit到數據庫時會自動賦值
db.session.add(consume_obj)
db.session.commit()
# 最終會在數據庫添加除了主鍵不同之外,其他所有字段都跟查出來的consume_obj字段相一致的一條數據。
三、其他sql知識點
1、sql語句相關
數據庫字段判空:
- 1.爲null
- 2.爲字符串的空’’
判斷是否爲空(包括上邊兩種情況):
select * from table where column is null or trim(column)=''
這樣就可以排除字段內容爲null、’'的。
判斷某個字段不爲空,可直接用!=
來做判斷,便可以排除掉null
和""
的判斷
select * from table where trim(column) != ''