Я уже довольно давно запускаю 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'}
Заранее благодарим за понимание.