Tips:在命令行操作这个命令过程中,如果出现以下的错误,不要紧张,可能是session已经失效,可以重新创建一个会话。
错误信息:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2980, in count
return self.from_self(col).scalar()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2749, in scalar
ret = self.one()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2718, in one
ret = list(self)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2761, in __iter__
return self._execute_and_instances(context)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2776, in _execute_and_instances
result = conn.execute(querycontext.statement, self._params)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 914, in execute
return meth(self, multiparams, params)
File "C:\Python27\lib\site-packages\sqlalchemy\sql\elements.py", line 323, in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1010, in _execute_clauseelement
compiled_sql, distilled_params
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1146, in _execute_context
context)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1341, in _handle_dbapi_exception
exc_info
File "C:\Python27\lib\site-packages\sqlalchemy\util\compat.py", line 202, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb, cause=cause)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1139, in _execute_context
context)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\default.py", line 450, in do_execute
cursor.execute(statement, parameters)
sqlalchemy.exc.InternalError: (psycopg2.InternalError)
[SQL: 'SELECT count(*) AS count_1 \nFROM (SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password \nFROM users \nWHERE users.name LIKE %(name_1)s) AS anon_1'] [parameters: {'name_1': '%e%'}]
查询
我们使用query()
方法在会话中创建一个Query
对象。
>>> for instance in session.query(User).order_by(User.id):
... print instance.name, instance.fullname
...
2017-03-09 09:02:10,990 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users ORDER BY users.id
2017-03-09 09:02:10,990 INFO sqlalchemy.engine.base.Engine {}
ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
>>>
Query还可以接受表中的列名作为参数,这样查询返回的是tuples:
>>> for name, fullname in session.query(User.name, User.fullname):
... print name, fullname
...
2017-03-09 09:04:20,615 INFO sqlalchemy.engine.base.Engine SELECT users.name AS users_name, users.fullname AS users_fullname
FROM users
2017-03-09 09:04:20,615 INFO sqlalchemy.engine.base.Engine {}
ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
>>>
还可以返回table中的记录行所映射的的类的对象
>>> for row in session.query(User, User.name).all():
... print row.User, row.name
...
2017-03-09 09:07:41,134 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
2017-03-09 09:07:41,137 INFO sqlalchemy.engine.base.Engine {}
<User(name='ed', fullname='Ed Jones', password='0000')> ed
<User(name='wendy', fullname='Wendy Williams', password='foobar')> wendy
<User(name='mary', fullname='Mary Contrary', password='xxf12')> mary
使用all()
方法返回的是一个列表,保存所有的查询结果。
您可以使用label()构造控制单个列表达式的名称,该构造可以从任何ColumnElement派生的对象获得,也可以使用任何映射到一个(例如User.name)的类属性
>>> for row in session.query(User.name.label('name_label')).all():
... print row.name_label
...
2017-03-09 09:11:57,808 INFO sqlalchemy.engine.base.Engine SELECT users.name AS name_label
FROM users
2017-03-09 09:11:57,809 INFO sqlalchemy.engine.base.Engine {}
ed
wendy
mary
从上面的运行结果可以看出,我们给查询的列名定义了别名,使用别名就像使用原来的列名效果是一样的。
在数据库查询中,例如SELECT users(name, fullname) FROM users
这样的SQL语句,users(name, fullname)
就是我们想查询后显示的记录的列名。所以在学习SQLAlchemy查询语句的时候可以和SQL查询语句做个比较,这样理解起来更加容易。
基本的查询操作还包含限制和偏移量查询,最常用的就是使用Python的切片操作。
>>> for u in session.query(User).order_by(User.id)[1:3]:
... print u
...
2017-03-09 09:25:35,711 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users ORDER BY users.id
LIMIT %(param_1)s OFFSET %(param_2)s
2017-03-09 09:25:35,713 INFO sqlalchemy.engine.base.Engine {'param_1': 2, 'param_2': 1}
<User(name='wendy', fullname='Wendy Williams', password='foobar')>
<User(name='mary', fullname='Mary Contrary', password='xxf12')>
>>>
在查询到的结果进行过滤,得到我们想要的结果使用filter_by()
>>> for name in session.query(User.name).filter_by(fullname='Ed Jones'):
... print name
...
2017-03-09 09:28:35,479 INFO sqlalchemy.engine.base.Engine SELECT users.name AS users_name
FROM users
WHERE users.fullname = %(fullname_1)s
2017-03-09 09:28:35,479 INFO sqlalchemy.engine.base.Engine {'fullname_1': 'Ed Jones'}
(u'ed',)
>>>
或者使用filter()
.
>>> for name in session.query(User.name).filter(User.fullname=='Ed Jones'):
... print name
...
2017-03-09 09:29:40,678 INFO sqlalchemy.engine.base.Engine SELECT users.name AS users_name
FROM users
WHERE users.fullname = %(fullname_1)s
2017-03-09 09:29:40,680 INFO sqlalchemy.engine.base.Engine {'fullname_1': 'Ed Jones'}
(u'ed',)
>>>
Query对象是完全生成的,意味着大多数方法调用返回一个新的Query对象,可以在其上添加更多的条件,我们可以调用两次filter()
方法。
>>> for user in session.query(User).filter(User.name=='ed').filter(User.fullname=='Ed Jones'):
... print user
...
2017-03-09 09:32:03,328 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = %(name_1)s AND users.fullname = %(fullname_1)s
2017-03-09 09:32:03,328 INFO sqlalchemy.engine.base.Engine {'fullname_1': 'Ed Jones', 'name_1': 'ed'}
<User(name='ed', fullname='Ed Jones', password='0000')>
>>>
对比filter()
和filter_by()
方法的区别。
- filter() 的参数是一个boolean类型,所以它的参数不是一个表达式。
- filter_by()的参数是一个表达式,而不是boolean值。
常用的Filter操作
-
equals:
直接可以使用连登号表示==
>>> session.query(User).filter(User.name=='ed').all()
2017-03-09 09:41:05,974 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = %(name_1)s
2017-03-09 09:41:05,976 INFO sqlalchemy.engine.base.Engine {'name_1': 'ed'}
[<User(name='ed', fullname='Ed Jones', password='0000')>]
-
Not equal:
可以使用不等号!表示
>>> session.query(User).filter(User.name!='ed').all()
2017-03-09 09:43:05,983 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name != %(name_1)s
2017-03-09 09:43:05,983 INFO sqlalchemy.engine.base.Engine {'name_1': 'ed'}
[<User(name='wendy', fullname='Wendy Williams', password='foobar')>, <User(name='mary', fullname='Mary Contrary', password='xxf12')>]
>>>
-
LIKE:
%e%这样就可以匹配所有含有e字母的单词。
>>> session.query(User).filter(User.name.like('%we%')).all()
2017-03-09 09:44:48,046 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE %(name_1)s
2017-03-09 09:44:48,048 INFO sqlalchemy.engine.base.Engine {'name_1': '%we%'}
[<User(name='wendy', fullname='Wendy Williams', password='foobar')>]
>>>
Note
ColumnOperators.like()是区分大小写的,如果想使用不区分大小写的就使用ilike
-
ILIKe:
首字母I的含义我认为就是Ignore
忽略的意思。
>>> session.query(User).filter(User.name.ilike('%We%')).all()
2017-03-09 09:47:38,999 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name ILIKE %(name_1)s
2017-03-09 09:47:39,000 INFO sqlalchemy.engine.base.Engine {'name_1': '%We%'}
[<User(name='wendy', fullname='Wendy Williams', password='foobar')>]
>>>
IN
>>> session.query(User).filter(User.name.in_(['ed', 'wendy'])).all()
2017-03-09 09:48:39,142 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name IN (%(name_1)s, %(name_2)s)
2017-03-09 09:48:39,144 INFO sqlalchemy.engine.base.Engine {'name_2': 'wendy', 'name_1': 'ed'}
[<User(name='ed', fullname='Ed Jones', password='0000')>, <User(name='wendy', fullname='Wendy Williams', password='foobar')>]
>>>
-
NOT IN
和In相反的就是不在给定的列表中,但是在实现时有所不同,注意那个符号
>>> session.query(User).filter(~User.name.in_(['ed', 'wendy'])).all()
2017-03-09 09:49:41,615 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name NOT IN (%(name_1)s, %(name_2)s)
2017-03-09 09:49:41,617 INFO sqlalchemy.engine.base.Engine {'name_2': 'wendy', 'name_1': 'ed'}
[<User(name='mary', fullname='Mary Contrary', password='xxf12')>]
>>>
IS NULL
>>> session.query(User).filter(User.name==None)
或者使用下面的方法,两种方法是等效的
query.filter(User.name.isnot(None))
AND
# 使用 and_()
>>> from sqlalchemy import and_
>>>
session.query(User).filter(and_(User.name=='ed', User.fullname=='Ed Jones'))
<sqlalchemy.orm.query.Query object at 0x00000000037B1DA0>
# 直接传给filter一个元组
>>> session.query(User).filter(User.name=='ed', User.fullname=='Ed Jones')
<sqlalchemy.orm.query.Query object at 0x00000000037B1EB8>
# 或者使用多个过滤器组成的链来实现
>>> session.query(User).filter(User.name=='ed').filter(User.fullname=='Ed Jones')
<sqlalchemy.orm.query.Query object at 0x0000000003821320>
>>>
OR
>>> print session.query(User).filter(or_(User.name=='ed', User.name=='wendy'))
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = :name_1 OR users.name = :name_2
>>>
Note
这里使用的or_()
不是python中的or
返回列表或者标量
-
all()
返回一个列表
>>> query = session.query(User).filter(User.name.like('%ed')).order_by(User.id)
>>> query.all()
2017-03-09 10:06:01,701 INFO sqlalchemy.engine.base.Engine select version()
2017-03-09 10:06:01,701 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,703 INFO sqlalchemy.engine.base.Engine select current_schema()
2017-03-09 10:06:01,704 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,706 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2017-03-09 10:06:01,707 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,707 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2017-03-09 10:06:01,707 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,709 INFO sqlalchemy.engine.base.Engine show standard_conforming_strings
2017-03-09 10:06:01,710 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,710 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2017-03-09 10:06:01,711 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE %(name_1)s ORDER BY users.id
2017-03-09 10:06:01,713 INFO sqlalchemy.engine.base.Engine {'name_1': '%ed'}
[<User(name='ed', fullname='Ed Jones', password='0000')>]
-
first()
限制返回第一个结果作为标
量
>>> query.first()
2017-03-09 10:07:46,233 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE %(name_1)s ORDER BY users.id
LIMIT %(param_1)s
2017-03-09 10:07:46,234 INFO sqlalchemy.engine.base.Engine {'name_1': '%ed', 'param_1': 1}
<User(name='ed', fullname='Ed Jones', password='0000')>
>>>
-
one()
完全获取所有行,如果结果中不存在一个对象标识或复合行,则会引发错误。
>>> query = session.query(User).filter(User.name.like('%e%')).order_by(User.id)
>>> user = query.one()
2017-03-09 10:10:34,095 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE %(name_1)s ORDER BY users.id
2017-03-09 10:10:34,095 INFO sqlalchemy.engine.base.Engine {'name_1': '%e%'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2727, in one
"Multiple rows were found for one()")
sqlalchemy.orm.exc.MultipleResultsFound: Multiple rows were found for one()
>>>
我们使用like查找到多行记录,但是使用one出现错误,意思是有多行记录被one找到。
下面我们修改一下,让查找结果为空
>>> query = session.query(User).filter(User.name.like('%haha%')).order_by(User.id)
>>> query.one()
2017-03-09 10:12:14,464 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE %(name_1)s ORDER BY users.id
2017-03-09 10:12:14,464 INFO sqlalchemy.engine.base.Engine {'name_1': '%haha%'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2724, in one
raise orm_exc.NoResultFound("No row was found for one()")
sqlalchemy.orm.exc.NoResultFound: No row was found for one()
>>>
one()
方法对于希望处理“没有找到项目”与“找到多个项目”的系统有所不同;例如RESTful Web服务,当没有找到结果时,它可能想要引发“404未找到”,但在找到多个结果时引发应用程序错误。
-
one_or_none()
有点和one()
很相似,但是它在没有查询到结果的时候不会返回错误信息,而是返回None
,但是当查找到多个结果时,它会抛出异常。
>>> query = session.query(User).filter(User.name.like('%haha%')).order_by(User.id)
>>> query.one_or_none()
2017-03-09 10:15:23,055 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE %(name_1)s ORDER BY users.id
2017-03-09 10:15:23,056 INFO sqlalchemy.engine.base.Engine {'name_1': '%haha%'}
上面没有查到结果,也没有返回错误。
>>> query = session.query(User).filter(User.name.like('%e%')).order_by(User.id)
>>> query.one_or_none()
2017-03-09 10:16:31,351 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE %(name_1)s ORDER BY users.id
2017-03-09 10:16:31,351 INFO sqlalchemy.engine.base.Engine {'name_1': '%e%'}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2698, in one_or_none
"Multiple rows were found for one_or_none()")
sqlalchemy.orm.exc.MultipleResultsFound: Multiple rows were found for one_or_none()
这个查询到多个记录,使用one_or_none()
返回了错误。
-
scalar()
调用one()
方法,并且在成功时返回该行的在表中的位置
>>> query = session.query(User.id).filter(User.name=='ed').order_by(User.id)
>>> query.scalar()
2017-03-09 10:24:43,888 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id
FROM users
WHERE users.name = %(name_1)s ORDER BY users.id
2017-03-09 10:24:43,888 INFO sqlalchemy.engine.base.Engine {'name_1': 'ed'}
1
使用文本SQL
文字字符串可以灵活使用Query
,通过指定text()
结构的使用,文本sql能被很多方法接受,比如filter() order_by()
>>> for user in session.query(User).filter(text("id<2")).order_by(text("id")).all():
... print usr.name
...
2017-03-09 10:30:36,473 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE id<2 ORDER BY id
2017-03-09 10:30:36,476 INFO sqlalchemy.engine.base.Engine {}
ed
>>>
在没有使用文本SQL时,我们的filter()和order_by()
中都要指定类名User
。
绑定参数可以使用基于字符串的SQL指定,使用冒号。要指定值,使用params()方法。这个方法可以类比在java中操作数据库 时,定义PreparedStatement对象一样,可以使用?
来做占位符,在后面来讲这个问号多代表的值赋值给问号。
>>> session.query(User).filter(text("id < :value and name = :name")).params(value=2, name='ed').order_by(User.id).one()
2017-03-09 10:37:25,960 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE id < %(value)s and name = %(name)s ORDER BY users.id
2017-03-09 10:37:25,960 INFO sqlalchemy.engine.base.Engine {'name': 'ed', 'value': 2}
<User(name='ed', fullname='Ed Jones', password='0000')>
>>>
上面的查询语句中,我们使用:变量名
起占位符的作用,然后在params
中使用关键字参数形式将占位符所代表的的值赋给它。
要使用完全基于字符串的语句,可以将表示完整语句的text()结构传递给from_statement()。如果没有其他说明符,字符串SQL中的列将基于名称与模型列匹配,如下所示,其中我们仅使用星号来表示加载所有列。
>>> session.query(User).from_statement(text("SELECT * FROM users WHERE name=:name")).params(name='ed').all()
2017-03-09 10:41:43,762 INFO sqlalchemy.engine.base.Engine SELECT * FROM users WHERE name=%(name)s
2017-03-09 10:41:43,762 INFO sqlalchemy.engine.base.Engine {'name': 'ed'}
[<User(name='ed', fullname='Ed Jones', password='0000')>]
>>>
这样就可以将完整的SQL语句通过text()
传给from_statement
。
上面的只是使用*号模糊匹配了所有列,但是我们想查询某些具体列怎么办?
可是使用下面的方法
>>>stmt = text("SELECT name, fullname FROM users
WHERE name=:name")
>>>stmt = stmt.columns(User.name, User.fullname)
>>> session.query(User).from_statement(stmt).params(name="ed").all()
计数Counting
Query包含了一个方便的计数count
方法:
>>> print session.query(User).filter(User.name.like("%e%")).count()
2017-03-09 10:55:40,842 INFO sqlalchemy.engine.base.Engine SELECT count(*) AS count_1
FROM (SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE %(name_1)s) AS anon_1
2017-03-09 10:55:40,842 INFO sqlalchemy.engine.base.Engine {'name_1': '%e%'}
2
>>>
count()
方法用于确定SQL语句将返回多少行,类比我们的SQL语句SELECT COUNT(*) FROM users
我们只是想知道返回多少行结果,可以使用func.count()
产生和SQL
语句中的count()
一样的效果。
>>> print session.query(func.count(User.name), User.name).group_by(User.name).all()
2017-03-09 10:58:27,927 INFO sqlalchemy.engine.base.Engine SELECT count(users.name) AS count_1, users.name AS users_name
FROM users GROUP BY users.name
2017-03-09 10:58:27,936 INFO sqlalchemy.engine.base.Engine {}
[(1L, u'wendy'), (1L, u'ed'), (1L, u'mary')]
实现简单的SELECT count(*) from table
我们可以这样:
>>> session.query(func.count("*")).select_from(User).scalar()
2017-03-09 11:04:57,135 INFO sqlalchemy.engine.base.Engine SELECT count(%(param_1)s) AS count_1
FROM users
2017-03-09 11:04:57,137 INFO sqlalchemy.engine.base.Engine {'param_1': '*'}
3L
如果直接使用User主键表示计数,可以删除select_from()
的用法:
>>> session.query(func.count(User.id)).scalar()
2017-03-09 11:06:22,874 INFO sqlalchemy.engine.base.Engine SELECT count(users.id) AS count_1
FROM users
2017-03-09 11:06:22,874 INFO sqlalchemy.engine.base.Engine {}
3L