SQL Alchemy ORM返回单个列,如何避免常见的后期处理


73

我使用的是SQL Alchemy的ORM,发现返回单列时,结果如下:

[(result,), (result_2,)] # etc...

使用这样的集合,我发现我必须经常这样做:

results = [r[0] for r in results] # So that I just have a list of result values

这并不是那么“糟糕”,因为我的结果集通常很小,但是如果不是这样,则可能会增加大量开销。最大的事情是我感到源代码混乱,而错过此步骤是我遇到的一个非常常见的错误。

有什么办法可以避免这个额外的步骤?

除了一个相关的问题:在这种情况下,orm的这种行为似乎很不方便,但是在另一种情况下,我的结果集是[[id,value)],它最终像这样结束:

[(result_1_id, result_1_val), (result_2_id, result_2_val)]

然后,我可以这样做:

results = dict(results) # so I have a map of id to value

这具有返回结果后有意义的有用步骤的优点。

这真的是问题吗,还是我只是一个小问题,得到结果集后的后处理在两种情况下都有意义?我确定我们可以想到其他一些常见的后处理操作,以使结果集在应用程序代码中更可用。是否有一个全面的高性能和便捷的解决方案,或者后处理是不可避免的,而仅仅是不同应用程序使用所必需的?

当我的应用程序实际上可以利用SQL Alchemy的ORM返回的对象时,它似乎非常有帮助,但是在我不能或不可以的情况下,它就没有那么多了。这仅仅是ORM的普遍问题吗?在这种情况下,我最好不要使用ORM层吗?

我想我应该显示一个我正在谈论的实际orm查询的示例:

session.query(OrmObj.column_name).all()

要么

session.query(OrmObj.id_column_name, OrmObj.value_column_name).all()

当然,在实际查询中,通常会有一些过滤器等。

Answers:


23

减少源代码混乱的一种方法是像这样进行迭代:

results = [r for (r, ) in results]

尽管此解决方案比使用[]运算符长一个字符,但我认为它更容易使用。

为了减少混乱,请删除括号。但是,这会使阅读代码时更加困难,从而注意到您实际上正在处理元组,但是:

results = [r for r, in results]

20

Python的zip结合*内联扩展运算符是解决此问题的便捷方法:

>>> results = [('result',), ('result_2',), ('result_3',)]
>>> zip(*results)
[('result', 'result_2', 'result_3')]

然后,您只需要索引一次[0]。对于这样的简短列表,您的理解速度更快:

>>> timeit('result = zip(*[("result",), ("result_2",), ("result_3",)])', number=10000)
0.010490894317626953
>>> timeit('result = [ result[0] for result in [("result",), ("result_2",), ("result_3",)] ]', number=10000)
0.0028390884399414062

但是对于更长的列表,zip应该更快:

>>> timeit('result = zip(*[(1,)]*100)', number=10000)
0.049577951431274414
>>> timeit('result = [ result[0] for result in [(1,)]*100 ]', number=10000)
0.11178708076477051

因此,取决于您的情况取决于您自己。


1
这里的假设是,列表中可能有100万个元素,但是突然看起来这并不很聪明。
安蒂·哈帕拉

15

我也为此付出了很多努力,直到我意识到它就像其他任何查询一样:

for result in results:
     print result.column_name

是的,我同意,NamedTuples不是立即显而易见的。我最常见的后期处理仍然是创建某种字典。更好的数据库体系结构和SQLAlchemy用法消除了我的大部分后期处理工作,因此我将需要的值附加到ORM对象上,或者可以通过方法获得。
Derek Litz

2

我发现以下内容更具可读性,还包括字典的答案(在Python 2.7中):

d = {id_: name for id_, name in session.query(Customer.id, Customer.name).all()}
l = [r.id for r in session.query(Customer).all()]

对于单个值,请从另一个答案中借用:

l = [name for (name, ) in session.query(Customer.name).all()]

zip适合列表的内置解决方案进行比较:

l = list(zip(*session.query(Customer.id).all())[0])

在我看来,这仅提供了大约4%的速度改进。


0

我的解决方案看起来像这样;)

def column(self):
    for column, *_ in Model.query.with_entities(Model.column).all():
        yield column

注意:仅py3。


-3

哇,伙计们,为什么要紧张?有方法更陡峭,更快,更优雅)

>>> results = [('result',), ('result_2',), ('result_3',)]
>>> sum(results, tuple())
('result', 'result_2', 'result_3')

速度:

>>> timeit('result = zip(*[("result",), ("result_2",), ("result_3",)])', number=10000)
0.004222994000883773
>>> timeit('result = sum([("result",), ("result_2",), ("result_3",)], ())', number=10000)
0.0038205889868550003

但是如果列表中有更多元素,请仅使用zip。压缩速度更快。


2
当您为每个求和步骤创建一个新的元组时,这需要二次时间。对于1000个元素,您将创建一个空的元组,然后是一个具有1个元素的元组,然后是具有2个元素的元组,依此类推。创建的每个新元组都必须首先复制前一个元组的所有引用,因此最终创建了N * N !参考文献的副本。不要sum()用于连接序列
马丁·彼得斯

1
3元素的总和更快是幸运的,您确实应该使用更多得多的输入来计时。
马丁·彼得斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.