web-dev-qa-db-ja.com

単一の列を返すSQL Alchemy ORM、一般的な後処理を回避する方法

私は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()

もちろん、実際のクエリには通常、いくつかのフィルターなどがあります。

67
Derek Litz

PythonのZipと*インライン展開演算子の組み合わせは、これに対する非常に便利なソリューションです。

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

その後、1回だけインデックスを作成する必要があります。このような短いリストの場合、理解は速くなります。

>>> 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

したがって、どちらがあなたの状況に適しているかを判断するのはあなた次第です。

22
Beright

ソースの混乱を減らす1つの方法は、次のように繰り返すことです。

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

このソリューションは[]演算子を使用するよりも1文字長くなりますが、目には簡単だと思います。

さらにすっきりさせるには、括弧を削除します。ただし、実際にタプルを処理していることに気付くには、コードを読むときに難しくなります。

results = [r for r, in results]
18
Dag Høidahl

他のクエリとまったく同じであることに気付くまで、これにも苦労しました。

for result in results:
     print result.column_name
11
Pakman

私は次のことを見つけました読みやすい、また辞書の答えを含みます(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%の速度改善しか提供していません。

2
Roman Susi

私の解決策はこのように見えます;)

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

注:py3のみ。

0
brunsgaard

うわー、みんな、なぜ緊張?より急な方法、より速く、よりエレガントな方法があります)

>>> 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のみを使用します。より高速に圧縮します。

0
user8814926