SQLAlchemy Core выбирает запрос значительно медленнее, чем необработанный SQL - PullRequest
0 голосов
/ 07 сентября 2018

Я уже довольно давно запускаю Jupyter Notebook с кодом Python3. Он использует комбинацию pyodbc и SQLAlchemy для подключения к нескольким базам данных SQL Server в интрасети моей компании. Цель записной книжки - извлечь данные из исходной базы данных SQL Server и сохранить их в памяти в виде фрейма данных Pandas. Затем файл извлекает конкретные значения из одного из столбцов и отправляет этот список значений в две разные базы данных SQL Server для получения списка сопоставления.

Все это прекрасно работало, пока я не решил переписать необработанные SQL-запросы в виде операторов SQLAlchemy Core. Я прошел через процесс проверки того, что запросы SQLAlchemy компилируются, чтобы соответствовать необработанным запросам SQL. Однако запросы выполняются невообразимо медленно. Например, исходный необработанный SQL-запрос выполняется за 25 секунд, а тот же запрос, переписанный в SQLAlchemy Core, выполняется за 15 минут! Остальные запросы не завершились даже после того, как позволили им работать в течение 2 часов.

Это может иметь отношение к тому, как я отражаю существующие таблицы. Я даже потратил некоторое время, чтобы переопределить ForeignKey и primary_key в таблицах, надеясь, что это поможет улучшить производительность. Без кубиков.

Я также знаю, "если это не сломано, не чините это". Но SQLAlchemy выглядит намного лучше, чем неприятный блок жестко закодированного SQL.

Может кто-нибудь объяснить, почему запросы SQLAlchemy выполняются так медленно. Документы по SQLAlchemy не дают особого понимания. Я использую SQLAlchemy версии 1.2.11.

import sqlalchemy
sqlalchemy.__version__
'1.2.11'

Вот соответствующие строки. Для краткости я исключаю экспорт, но в случае, если кому-то понадобится убедиться, что я буду рад его поставить.

engine_dr2 = create_engine("mssql+pyodbc://{}:{}@SER-DR2".format(usr, pwd))
conn = engine_dr2.connect()

metadata_dr2 = MetaData()
bv = Table('BarVisits', metadata_dr2, autoload=True, autoload_with=engine_dr2, schema='livecsdb.dbo')
bb = Table('BarBillsUB92Claims', metadata_dr2, autoload=True, autoload_with=engine_dr2, schema='livecsdb.dbo')

mask = data['UCRN'].str[:2].isin(['DA', 'DB', 'DC'])
dr2 = data.loc[mask, 'UCRN'].unique().tolist()

sql_dr2 = select([bv.c.AccountNumber.label('Account_Number'),
                  bb.c.UniqueClaimReferenceNumber.label('UCRN')])
sql_dr2 = sql_dr2.select_from(bv.join(bb, and_(bb.c.SourceID == bv.c.SourceID,
                                               bb.c.BillingID == bv.c.BillingID)))
sql_dr2 = sql_dr2.where(bb.c.UniqueClaimReferenceNumber.in_(dr2))

mapping_list = pd.read_sql(sql_dr2, conn)
conn.close()

Необработанный SQL-запрос, который должен соответствовать sql_dr2 и запускает lickety split, находится здесь:

"""SELECT Account_Number = z.AccountNumber, UCRN = y.UniqueClaimReferenceNumber 
FROM livecsdb.dbo.BarVisits z 
INNER JOIN 
livecsdb.dbo.BarBillsUB92Claims y
ON 
y.SourceID = z.SourceID 
AND 
y.BillingID = z.BillingID
WHERE
y.UniqueClaimReferenceNumber IN ({0})""".format(', '.join(["'" + acct + "'" for acct in dr2]))

Список dr2 обычно содержит более 70000 элементов. Опять же, сырой SQL обрабатывает это за одну минуту или меньше. Перезапись SQLAlchemy выполняется уже более 8 часов и до сих пор не выполнена.

Обновление Дополнительная информация приведена ниже. У меня нет базы данных или таблиц, и они содержат защищенную медицинскую информацию, поэтому я не могу поделиться ею напрямую, но я посмотрю, как сделать некоторые ложные данные.

tables = ['BarVisits', 'BarBillsUB92Claims']
for t in tables:
    print(insp.get_foreign_keys(t))
[], []

for t in tables:
    print(insp.get_indexes(t))
[{'name': 'BarVisits_SourceVisit', 'unique': False, 'column_names': ['SourceID', 'VisitID']}]
[]

for t in tables:
    print(insp.get_pk_constraint(t))
{'constrained_columns': ['BillingID', 'SourceID'], 'name': 'mtpk_visits'}
{'constrained_columns': ['BillingID', 'BillNumberID', 'ClaimID', 'ClaimInsuranceID', 'SourceID'], 'name': 'mtpk_insclaim'}

Заранее благодарим за понимание.

1 Ответ

0 голосов
/ 02 ноября 2018

Я понял, как сделать запрос быстрым, но понятия не имею, зачем он нужен на этом сервере, а не на других.

Принимая

sql_dr2 = str(sql.compile(dialect=mssql.dialect(), compile_kwargs={"literal_binds": True}))

и отправка через

pd.read_sql(sql_dr2, conn)

выполняет запрос примерно за 2 секунды.

Опять же, я понятия не имею, почему это работает, но работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...