2017-06-01 5 views
0

Ich verwende den folgenden sqlalchemy Code einige Daten aus einer DatenbankLeistung SQLAlchemy und oder

q = session.query(hd_tbl).\ 
    join(dt_tbl, hd_tbl.c['data_type'] == dt_tbl.c['ID']).\ 
    filter(or_(and_(hd_tbl.c['object_id'] == get_id(row['object']), 
        hd_tbl.c['data_type'] == get_id(row['type']), 
        hd_tbl.c['data_provider'] == get_id(row['provider']), 
        hd_tbl.c['data_account'] == get_id(row['account'])) 
      for index, row in data.iterrows())).\ 
    with_entities(hd_tbl.c['ID'], hd_tbl.c['object_id'], 
        hd_tbl.c['data_type'], hd_tbl.c['data_provider'], 
        hd_tbl.c['data_account'], dt_tbl.c['value_type']) 

wo hd_tbl und dt_tbl sind zwei Tabellen in SQL-Datenbank und data ist Pandas Datenrahmen abzurufen typischerweise etwa 1k-9k Einträgen . hd_tbl enthält im Moment rund 90k Zeilen.

Die Ausführungszeit scheint exponentiell mit der Länge von data zu wachsen. Die entsprechende SQL-Anweisung (von sqlalchemy) sieht wie folgt aus:

SELECT data_header.`ID`, data_header.object_id, data_header.data_type, data_header.data_provider, data_header.data_account, basedata_data_type.value_type 
FROM data_header INNER JOIN basedata_data_type ON data_header.data_type = basedata_data_type.`ID` 
WHERE data_header.object_id = %s AND data_header.data_type = %s AND data_header.data_provider = %s AND data_header.data_account = %s OR 
data_header.object_id = %s AND data_header.data_type = %s AND data_header.data_provider = %s AND data_header.data_account = %s OR 
... 
data_header.object_id = %s AND data_header.data_type = %s AND data_header.data_provider = %s AND data_header.data_account = %s OR 

Die Tabellen und Spalten vollständig indiziert sind, und die Leistung ist nicht zufriedenstellend. Derzeit ist es viel schneller, alle Daten von hd_tbl und dt_tbl in den Speicher zu lesen und mit der Pandas-Merge-Funktion zusammenzuführen. Dies scheint jedoch suboptimal zu sein. Jemand, der eine Idee hat, wie man den Anruf von sqlalchemy verbessert?

EDIT: konnte ich Leistung signifcantly verbessern, indem sqlalchemy tuple_ auf folgende Weise mit:

header_tuples = [tuple([int(y) for y in tuple(x)]) for x in 
       data_as_int.values] 
q = session.query(hd_tbl). \ 
    join(dt_tbl, hd_tbl.c['data_type'] == dt_tbl.c['ID']). \ 
    filter(tuple_(hd_tbl.c['object_id'], hd_tbl.c['data_type'], 
        hd_tbl.c['data_provider'], 
        hd_tbl.c['data_account']).in_(header_tuples)). \ 
    with_entities(hd_tbl.c['ID'], hd_tbl.c['object_id'], 
        hd_tbl.c['data_type'], hd_tbl.c['data_provider'], 
        hd_tbl.c['data_account'], dt_tbl.c['value_type']) 

mit entsprechender Abfrage ...

SELECT data_header.`ID`, data_header.object_id, data_header.data_type, data_header.data_provider, data_header.data_account, basedata_data_type.value_type 
FROM data_header INNER JOIN basedata_data_type ON data_header.data_type = basedata_data_type.`ID` 
WHERE (data_header.object_id, data_header.data_type, data_header.data_provider, data_header.data_account) IN ((%(param_1)s, %(param_2)s, %(param_3)s, %(param_4)s), (%(param_5)s, ...)) 
+0

Nun, natürlich geht es langsam sein; Sie machen einen 'JOIN', indem Sie eine der Tabellen überschleifen und eine riesige' WHERE'-Klausel mit Tausenden von Bedingungen erstellen. Sie müssen eine temporäre Tabelle aus Ihrem Datenrahmen erstellen und eine ordnungsgemäße 'JOIN' durchführen. – univerio

Antwort

1

würde ich empfehlen Sie erstellen Composite-Index auf den Feldern object_id, data_type, data_provider, ... mit der gleichen Reihenfolge, die sie in der Tabelle platziert sind, und stellen sie sicher, dass sie folgende in den gleichen Bestellen Sie in Ihrer WHERE-Bedingung. Es kann ein wenig Ihre Anforderungen durch Kosten des Speicherplatzes beschleunigen.

Auch Sie können mehr daraus folgende kleine SQL-Anfragen verwenden, anstatt eine große Abfrage mit komplexem OR Zustand. Akkumulieren Sie extrahierte Daten auf der Anwendungsseite oder, wenn die Menge groß genug ist, in einem schnellen temporären Speicher (eine temporäre Tabelle, noSQL, etc.)

Zusätzlich können Sie die MySQL-Konfiguration überprüfen und die Werte, bezogen auf das Speichervolumen, erhöhen Ein Thread, eine Anfrage usw. Eine gute Idee ist es zu überprüfen, ob der zusammengesetzte Index in den verfügbaren Speicher passt oder ob er nutzlos ist.

denke, ich DB Tuning viel helfen kann die Produktivität zu erhöhen. Andernfalls können Sie die Architektur Ihrer Anwendung analysieren, um aussagekräftigere Ergebnisse zu erhalten.