Как я могу кэшировать результат SQL, чтобы мне не приходилось повторно вызывать SQL для получения данных для графиков Da sh? - PullRequest
1 голос
/ 06 мая 2020

Я пытаюсь создать приборную панель, которая будет генерировать несколько графиков на основе одного запроса данных SQL. Я хочу, чтобы запрос можно было изменять через панель управления (например, для запроса другой суммы заказа или аналогичного), а затем изменять все графики сразу. Запрос может быть дорогостоящим, поэтому я не хочу, чтобы он выполнялся N раз для N разных графиков.

Я пытался сделать это с помощью flask декоратора кеша @cache.memoize(), аналогично примеру, приведенному в docs: https://dash.plotly.com/performance

Вот урезанная версия того, что я делаю. Я могу сказать, что функция query_data не выполняет то, что я намеревался, потому что: 1. Полученные графики показывают разные точки данных на оси x. Если бы он использовал один и тот же кешированный набор данных, точки данных в x должны быть одинаковыми 2. Операторы print в функции query_data появляются дважды каждый раз, когда я меняю ячейку ввода.

Кто-нибудь может объяснить, почему это не работает или как я могу достичь того, чего хочу.

import sys

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output

from setup_redshift import setup_connection
from flask_caching import Cache
from datetime import datetime
import pandas as pd

conn = setup_connection()

app = dash.Dash(__name__)
cache = Cache(app.server, config={
    # 'CACHE_TYPE': 'filesystem',
    'CACHE_TYPE': 'memcached',
    'CACHE_DIR': 'cache-directory'
})

sql_query = '''select i.order_amount_in_usd, r.calibrated_score, r.score
from datalake.investigations i 
inner join datalagoon.prod_model_decision r
                    ON i.investigation_id = r.investigation_id

where i.team_id = {}
AND i.order_amount_in_usd < {}
AND r.calibrated_score >= 0
order by RANDOM()
limit 1000'''


@cache.memoize()
def query_data(team_id, max_usd):
    print("Calling data query now with team_id={} and max_usd={} at time {}".format(team_id, max_usd, datetime.now()))
    _sql = sql_query.format(team_id, max_usd)
    print(_sql)
    data = pd.read_sql(sql_query.format(team_id, max_usd), conn)
    print("data is {} rows ".format(len(data)))
    print("data max usd is {}".format(data['order_amount_in_usd'].max()))
    return data


@app.callback(Output(component_id='output-graph', component_property='figure'),
              [Input(component_id='data-select-team-id', component_property='value'),
               Input(component_id='data-select-max-usd', component_property='value')])
def plot_data(team_id, max_usd):
    print("calling query_data at from graph at {}".format(datetime.now()))
    in_data = query_data(team_id, max_usd)
    print("going to make graph1 now at {}".format(datetime.now()))

    fig = px.scatter(in_data,
                     x='order_amount_in_usd',
                     y='calibrated_score')
    return fig


@app.callback(Output(component_id='output-graph2', component_property='figure'),
              [Input(component_id='data-select-team-id', component_property='value'),
               Input(component_id='data-select-max-usd', component_property='value')])
def plot_second_data(team_id, max_usd):
    print("calling query_data at from graph2 at {}".format(datetime.now()))
    in_data = query_data(team_id, max_usd)
    print("going to make graph2 now at {}".format(datetime.now()))
    fig = px.scatter(in_data,
                     x='order_amount_in_usd',
                     y='score')
    return fig


app.layout = html.Div(  # style={'backgroundColor': colors['background']},

    children=[dcc.Input(id='data-select-team-id',
                        value=7625,
                        placeholder='Input Team ID',
                        type='number',
                        min=0,
                        max=1_000_000_000,
                        debounce=True
                        ),
              dcc.Input(id='data-select-max-usd',
                        value=5000,
                        type='number',
                        debounce=True),
              dcc.Graph(id='output-graph'),
              dcc.Graph(id='output-graph2')]
)

if __name__ == '__main__':
    app.run_server(debug=True)

1 Ответ

1 голос
/ 06 мая 2020

Раньше я сохранял результаты, используя d cc .Store (см. здесь )

Вы можете структурировать свое приложение следующим образом:

  • Запустите запрос SQL и сохраните результаты, используя d cc .Store (локальный или в памяти в зависимости от вашего варианта использования). Это выполняется только один раз (для каждой загрузки приложения, интервального таймера или обновления пользовательской кнопки sh et c)
  • Обратные вызовы для генерации различных разрезов данных в таблицах или диаграммах da sh загружают хранилище

Если результаты запроса большие (см. «Ограничения по хранению; в приведенной выше ссылке), вы должны сохранить результаты в локальном плоском файле, таком как JSON или CSV, и читать его каждый раз. .

Альтернативой является использование PostgreSQL и материализованных представлений, чтобы сделать запрос SQL дешевым (с компромиссом на пространство для хранения)

Эти подходы делают приложение da sh кажутся очень отзывчивыми для пользователя, позволяя анализировать большие данные

...