SQL Alchemy ORM gibt eine einzelne Spalte zurück, um eine häufige Nachbearbeitung zu vermeiden


73

Ich verwende das ORM von SQL Alchemy und finde, wenn ich eine einzelne Spalte zurückgebe, erhalte ich die folgenden Ergebnisse:

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

Bei einem Set wie diesem muss ich das oft machen:

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

Das ist nicht so "schlecht", weil meine Ergebnismengen normalerweise klein sind, aber wenn dies nicht der Fall wäre, könnte dies einen erheblichen Overhead verursachen. Das Größte ist, dass ich das Gefühl habe, dass es die Quelle überfüllt, und das Fehlen dieses Schritts ist ein ziemlich häufiger Fehler, auf den ich stoße.

Gibt es eine Möglichkeit, diesen zusätzlichen Schritt zu vermeiden?

Nebenbei bemerkt: Dieses Verhalten des Orms scheint in diesem Fall unpraktisch zu sein, aber in einem anderen Fall, in dem meine Ergebnismenge [(id, value)] lautete, endet es folgendermaßen:

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

Ich kann dann einfach machen:

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

Dieser hat den Vorteil, dass er als nützlicher Schritt nach der Rückgabe der Ergebnisse sinnvoll ist.

Ist das wirklich ein Problem oder bin ich nur ein Trottel und die Nachbearbeitung nach Erhalt der Ergebnismenge ist in beiden Fällen sinnvoll? Ich bin sicher, wir können uns einige andere gängige Nachbearbeitungsvorgänge vorstellen, um die Ergebnismenge im Anwendungscode benutzerfreundlicher zu machen. Gibt es allgemein leistungsstarke und bequeme Lösungen oder ist eine Nachbearbeitung unvermeidbar und lediglich für unterschiedliche Anwendungsnutzungen erforderlich?

Wenn meine Anwendung tatsächlich die Objekte nutzen kann, die von ORM von SQL Alchemy zurückgegeben werden, scheint dies äußerst hilfreich zu sein, aber in Fällen, in denen ich nicht kann oder nicht, nicht so sehr. Ist dies nur ein häufiges Problem von ORMs im Allgemeinen? Bin ich in solchen Fällen besser dran, die ORM-Schicht nicht zu verwenden?

Ich nehme an, ich sollte ein Beispiel für die tatsächlichen Orm-Abfragen zeigen, über die ich spreche:

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

oder

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

Natürlich gibt es in einer echten Abfrage normalerweise einige Filter usw.

Antworten:


23

Eine Möglichkeit, die Unordnung in der Quelle zu verringern, besteht darin, wie folgt zu iterieren:

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

Obwohl diese Lösung ein Zeichen länger ist als die Verwendung des []Operators, ist sie meiner Meinung nach augenschonender.

Entfernen Sie die Klammer, um noch weniger Unordnung zu vermeiden. Dies macht es beim Lesen des Codes schwieriger zu bemerken, dass Sie tatsächlich mit Tupeln umgehen:

results = [r for r, in results]

20

Pythons Zip in Kombination mit dem Inline-Erweiterungsoperator * ist eine ziemlich praktische Lösung dafür:

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

Dann müssen Sie nur einmal [0] indizieren. Für eine so kurze Liste ist Ihr Verständnis schneller:

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

Für längere Listen sollte zip jedoch schneller sein:

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

Es liegt also an Ihnen, zu bestimmen, welche für Ihre Situation besser ist.


1
Die Annahme hier ist, dass es eine Million Elemente in der Liste geben könnte und dies plötzlich nicht mehr sehr klug aussieht.
Antti Haapala

15

Ich hatte auch damit zu kämpfen, bis mir klar wurde, dass es genau wie bei jeder anderen Abfrage ist:

for result in results:
     print result.column_name

Ja, ich stimme zu, die NamedTuples sind nicht sofort offensichtlich. Meine häufigste Nachbearbeitung besteht immer noch darin, ein Wörterbuch zu erstellen. Der größte Teil meiner Nachbearbeitung wurde durch eine bessere Datenbankarchitektur und SQLAlchemy-Nutzung eliminiert, sodass ich die Werte, die ich benötige, an ORM-Objekte angehängt habe oder über Methoden verfügbar bin.
Derek Litz

2

Ich fand das Folgende besser lesbar , enthält auch die Antwort für das Diktat (in 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()]

Für den Einzelwert Ausleihen aus einer anderen Antwort:

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

Vergleichen Sie mit der integrierten zipLösung, die an die Liste angepasst ist:

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

was in meiner Zeit nur etwa 4% Geschwindigkeitsverbesserungen bietet.


0

Meine Lösung sieht so aus;)

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

HINWEIS: Nur py3.


-3

Wow, Leute, warum anstrengen? Es gibt Methoden steiler, schneller und eleganter)

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

Geschwindigkeit:

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

Aber wenn mehr Elemente in der Liste - verwenden Sie nur zip . Reißverschluss mehr Geschwindigkeit.


2
Dies dauert quadratisch, da Sie für jeden Summenschritt ein neues Tupel erstellen. Für 1000 Elemente erstellen Sie ein leeres Tupel, dann ein Tupel mit 1 Element, dann ein Tupel mit 2 Elementen usw. Jedes neu erstellte Tupel muss zuerst alle Referenzen aus dem vorherigen Tupel kopieren, sodass Sie am Ende N * N erstellt haben ! Kopien der Referenzen. Nicht sum()zum Verketten von Sequenzen verwenden .
Martijn Pieters

1
Dass die 3-Elemente-Summe schneller ist, ist ein Glücksfall. Sie sollten dies wirklich mit viel, viel größeren Eingaben zeitlich festlegen.
Martijn Pieters
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.