#python #sql #oracle #cx-oracle
#python #sql #Oracle #cx-oracle
Вопрос:
(Я знаю, что это можно сделать с помощью SQL Alchemy, но это также упражнение в обучении, поэтому я понимаю, как это делается без таких инструментов, как SQL Alchemy) Моя компания использует Oracle DB 10.1.
Структура БД Первичная таблица (первичный ключ) Вторая таблица (внешний ключ с первичным ключом) Третья таблица (внешний ключ с первичным ключом)
Вторая таблица может содержать от 0 до 4 связанных записей, третья таблица может иметь 0 — бесконечное число (хотя вряд ли их будет больше 20)
Мой SQL-запрос работает нормально и отлично передает данные. Если мы получим несколько записей в других таблицах, это просто вернет несколько строк с одним и тем же первичным ключом. Кажется стандартным (новым для SQL, поэтому я предполагаю, что так и должно быть).
Проблема заключается в том, когда я пытаюсь обработать данные в единственную запись JSON, перебирающую все. Вот что у меня есть в настоящее время в качестве моего сценария:
cursor = conn.cursor()
sql = """
SELECT *
FROM RC_CAL
join CALENDAR_EVENT
ON RC_CAL.rc_id = CALENDAR_EVENT.rc_id
left outer join CALENDAR_EVENT CALENDAR_EVENT_1
ON RC_CAL.rc_id = CALENDAR_EVENT_1.rc_id
left outer join RC_HISTORY RC_HISTORY_1
ON RC_CAL.rc_id = RC_HISTORY_1.rc_id
"""
has_where = False
params = {}
query_conditions = []
if is_active:
has_where = True
query_conditions.append('WHERE CALENDAR_EVENT.ACTIVE_YN = :ACTIVE')
params['ACTIVE'] = is_active
if env:
env_string = 'AND CALENDAR_EVENT.env = :ENV' if has_where else 'WHERE CALENDAR_EVENT.env = :ENV'
has_where = True
query_conditions.append(env_string)
params['ENV'] = env
if from_date:
from_string = 'AND CALENDAR_EVENT.end_dt >= :FROM_DATE' if has_where else 'WHERE CALENDAR_EVENT.end_dt >= :FROM_DATE'
has_where = True
query_conditions.append(from_string)
params['FROM_DATE'] = from_date
if to_date:
to_string = 'AND CALENDAR_EVENT.end_dt <= :TO_DATE' if has_where else 'WHERE CALENDAR_EVENT.end_dt <= :TO_DATE'
query_conditions.append(to_string)
params['TO_DATE'] = to_date
if len(query_conditions) > 0:
sql = " ".join(query_conditions)
cursor.execute(sql, **params)
rows = cursor.fetchall()
events = []
for rcId, data in groupby(rows, key=lambda t: t[0]):
rc_item = None
generic_list = []
history_list = []
for item in data:
if not rc_item:
rc_item = EventModalV2(*item[0:11])
generic_id = item[11]
history_id = item[17]
generic_data = item[11:17]
history_data = item[17:]
if not any(generic_id in generic_item for generic_item in generic_list) and generic_id:
rc_item.update_with_generic(generic_data)
generic_list.append(generic_data)
if not any(history_id in history_item for history_item in history_list) and history_id:
rc_item.update_with_history(history_data)
history_list.append(history_data)
events.append(rc_item)
cursor.close()
conn.close()
return events
пример набора данных, если у меня было несколько записей в таблице 3, хотя их могло быть больше, если в таблице 2 указано больше записей (где указано PROD, что является частью таблицы 2. Это таблица, в которой мы храним даты в зависимости от продукта среды:
(170079, 3, 1, None, None, None, 'Test Demo Event - DS', 'N', 'N', None, datetime.datetime(2020, 11, 5, 19, 16, 22), 237247, datetime.datetime(2020, 11, 1, 4, 0), 'PROD', datetime.datetime(2020, 11, 1, 4, 0), 91, 'Y', 5051, <cx_Orac
le.LOB object at 0x00000237AE8954B8>, 'rc_create', datetime.datetime(2020, 11, 5, 19, 16, 22))
(170079, 3, 1, None, None, None, 'Test Demo Event - DS', 'N', 'N', None, datetime.datetime(2020, 11, 5, 19, 16, 22), 237247, datetime.datetime(2020, 11, 1, 4, 0), 'PROD', datetime.datetime(2020, 11, 1, 4, 0), 91, 'Y', 5052, <cx_Orac
le.LOB object at 0x00000237AE895580>, 'rc_patch', datetime.datetime(2020, 11, 5, 19, 17, 7))
(170079, 3, 1, None, None, None, 'Test Demo Event - DS', 'N', 'N', None, datetime.datetime(2020, 11, 5, 19, 16, 22), 237247, datetime.datetime(2020, 11, 1, 4, 0), 'PROD', datetime.datetime(2020, 11, 1, 4, 0), 91, 'Y', 5053, <cx_Orac
le.LOB object at 0x00000237AE895648>, 'rc_patch', datetime.datetime(2020, 11, 5, 19, 17, 59))
(170079, 3, 1, None, None, None, 'Test Demo Event - DS', 'N', 'N', None, datetime.datetime(2020, 11, 5, 19, 16, 22), 237247, datetime.datetime(2020, 11, 1, 4, 0), 'PROD', datetime.datetime(2020, 11, 1, 4, 0), 91, 'Y', 5054, <cx_Orac
le.LOB object at 0x00000237AE895710>, 'rc_patch', datetime.datetime(2020, 11, 5, 19, 19, 9))
Это выполняется очень медленно при больших запросах. Я не уверен, как я могу сделать это лучше.
Комментарии:
1. Добавить инструментарий, чтобы показать, что происходит медленно? Просмотрите план выполнения SQL?
Ответ №1:
Во-первых, как предложил Крис, выясните, где происходит замедление. В зависимости от того, что вы найдете, у меня есть пара предложений:
(1) Избегайте извлечения значений LOB, поскольку для извлечения данных требуется дополнительный цикл туда и обратно, что замедляет процесс. Вы можете увидеть пример здесь.
(2) Если предыдущего предложения недостаточно, вы также можете рассмотреть возможность простой выборки трех (четырех?) запрашивает независимо и объединяет их в Python (с помощью dicts). Это предотвращает необходимость многократной выборки строк верхнего уровня с последующим удалением дубликатов.