Я хочу повторить SQL-запрос, который я использовал в pgAdmin, чтобы объединить несколько таблиц и загрузить полученную таблицу в файл CSV. Мне удалось добиться успеха с pgAdmin, и я хочу добиться того же результата с помощью SQLAlchemy, поскольку мое приложение взаимодействует с базой данных через SQLAlchemy.
В pgAdmin я использую такой запрос:
SELECT * FROM visits
LEFT JOIN patients ON visits.patient_id = patients.patient_id
LEFT JOIN [many more left joins here]
WHERE visits.visit_id = 1;
Чтобы сделать то же самое с помощью SQLAlchemy, я попытался использовать следующий код:
def get_all_data_for_visit(self, visit_id: int):
try:
stmt = select(Visit, Patient).outerjoin(Patient, and_(Visit.patient_id == Patient.patient_id, Visit.visit_id == visit_id))
result = self.db.execute(stmt).all()
if result:
for row in result:
print(row.Patient.patient_id, row.Visit.visit_type)
return result
else:
return None
except OperationalError as e:
self.show_error_msg()
Однако этот код возвращает объект пациентов и объект посещений, что не является желаемым результатом. Я хочу перебрать объекты и распечатать все их атрибуты (например, row.Visit.visit_type
) в CSV. Я не могу просто перебрать все существующие объекты, потому что некоторые из них могут быть None
, но мне все равно нужны результаты (в данном случае null
) в таблице. С моим SQL-запросом это работает. С sqlalchemy я получаю AttributeError.
Я прочитал связанные вопросы, но не смог найти решение моего конкретного вопроса.
Спасибо за помощь!
Visit.visit_id == visit_id
не должно быть в outerJoin()
состоянии.
@Barmar: Сначала я попробовал использовать только «Пациент» и «Посещение», поэтому в списке выбора у меня есть только «Посещение» и «Пациент». Моя проблема в том, что я получаю обратно объект-пациент и объект-посещение. Но мне нужен один объект, в котором все атрибуты (пациента и посещения) объединены (нулевые, если они отсутствуют). Где мне поставить Visit.visit_id == visit_id?
Я не использую SQLAlchemy, но не думаю, что это вообще так работает. Каждая таблица соответствует своему классу, поэтому вы получаете экземпляры этого класса. В каком классе вы хотели бы получить желаемый результат?
Уровень ORM SQLAlchemy возвращает кортежи объектов в результатах соединения, потому что, как правило, программисты используют ORM, потому что с ним удобно работать с графиками объектов, а не с плоскими массивами данных строк. В случае с вопросом - запись результатов запроса на соединение непосредственно в отчет - работать с плоским массивом удобнее. Решение состоит в том, чтобы использовать базовый уровень SQLAlchemy вместо ORM, который будет вести себя как обычный SQL.
Базовый уровень работает с таблицами, а не с объектами ORM. Учитывая, что классы сущностей ORM существуют, таблицы можно получить из них:
patients = Patient.__table__
visits = Visit.__table__
Оператор запроса практически идентичен, за исключением того, что доступ к столбцам осуществляется через атрибут таблицы .c
(или .columns
), а не напрямую:
q = stmt = (
sa.select(Patient, Visit)
.outerjoin(Patient, Visit.patient_id == Patient.patient_id)
.where(Visit.visit_id == visit_id)
)
Чтобы воспроизвести SQL в вопросе, я изменил порядок таблиц в проекции и, как предполагал Бармар, переместил фильтр visit_id
из условия соединения в предложение WHERE
. Запрос может быть выполнен посредством сеанса или соединения.
Результатом выполнения будет экземпляр CursorResult, который можно рассматривать как итерацию именованных кортежей. Доступ к отдельным столбцам в строке можно получить по имени или позиции.
Обратите внимание, что SQLAlchemy автоматически устраняет неоднозначность идентификаторов, которые появляются несколько раз в столбцах результатов. Вы можете увидеть имена столбцов в результате, вызвав метод CursorResult
.keys()
.
Вам необходимо перечислить все таблицы в списке аргументов
select()
. Если вы перечислите толькоVisit
иPatient
, это все равно, что написатьSELECT visits.*, patients.*
вместоSELECT *
.