Как расширить столбец SQL JSON на несколько строк с помощью sqlalchemy?

Я хочу расширить столбец типа JSON, содержащий массив строк (псевдонимы записи), на несколько строк в sqlalchemy 2.0. Я получаю следующий SQL-запрос для SQL Server, который делает то же самое, но его нужно писать с использованием sqlalchemy.

SELECT * FROM general_info OUTER APPLY OPENJSON(alias)

Из этой таблицы:

идентификатор имя псевдоним 1 Роберт ["боб", "бобби"] 2 Алиса нулевой

Я хотел бы получить:

идентификатор имя псевдоним 1 Роберт Боб 1 Роберт Бобби 2 Алиса нулевой

Вы уже прочитали документацию SQLAlchemy Использование операторов SELECT? Что вы пробовали?

AlwaysLearning 16.04.2024 10:57

Конечно, да, но эта операция, похоже, не является обычной операцией. Кроме того, не существует расширенной поддержки операций типа JSON, помимо индексации.

Asier Pabolleta 16.04.2024 11:17
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
2
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Смоделировать OPENJSON как то, что SQLAlchemy называет функцией табличного значения, тривиально, т.е.:

oj = func.OPENJSON(general_info.alias).table_valued("value")

К сожалению, сложность заключается в OUTER APPLY поддержке Microsoft SQL Server.

Поиск выявит следующую проблему на Github: Добавьте поддержку оператора APPLY для mssql #5133, на основе которой основано следующее:

import re
from sqlalchemy import String, func, join, lateral, select, text, true
from sqlalchemy.dialects import mssql
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy.sql import Join

# Based on REF:
# Add APPLY operator support for mssql #5133
# https://github.com/sqlalchemy/sqlalchemy/issues/5133
@compiles(Join, "mssql")
def _compile_join_lateral(element, compiler, **kw):
    statement = compiler.visit_join(element, **kw)
    statement = re.sub(" JOIN LATERAL ", " OUTER APPLY ", statement)
    statement = re.sub(" ON 1(\s*=\s*1)?", "", statement)
    return statement

class Base(DeclarativeBase):
    pass

class general_info(Base):
    __tablename__ = "general_info"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))
    alias: Mapped[str] = mapped_column(String)

stmt1 = select(general_info.id, general_info.name, text("j1.value as alias")).select_from(
    join(general_info, lateral(func.OPENJSON(general_info.alias).table_valued("value"), name = "j1"), true())
    )
stmt2 = stmt1.compile(dialect=mssql.dialect())
print(stmt1)
print(stmt2)

print(stmt1) выведет общий оператор SQL с JOIN LATERAL:

SELECT general_info.id, general_info.name, j1.value as alias 
FROM general_info JOIN LATERAL OPENJSON(general_info.alias) AS j1 ON true

print(stmt2) выведет оператор T-SQL с OUTER APPLY:

SELECT general_info.id, general_info.name, j1.value as alias 
FROM general_info OUTER APPLY OPENJSON(general_info.alias) AS j1

И выполнение этого на примере ваших данных должно дать результаты:

идентификатор имя псевдоним 1 Роберт Боб 1 Роберт Бобби 2 Алиса нулевой

В противном случае, существует ли более общий или совместимый подход для решения этой проблемы среди других серверных частей базы данных, даже менее эффективный? Может быть, связанный подзапрос на JOIN или что-то подобное?

Asier Pabolleta 16.04.2024 13:30

Вы не можете сделать это коррелированным подзапросом в предложении select на Microsoft SQL Server, потому что openjson() может возвращать много строк.

AlwaysLearning 16.04.2024 14:06

На самом деле не существует общего подхода к обработке JSON в обычных СУБД. например: хотя и Oracle, и Microsoft SQL Server имеют функции json_query() и json_value(), они достаточно разные, чтобы не быть взаимозаменяемыми. В то время как Oracle рассматривает JSON как собственный тип базы данных, к которому можно запрашивать напрямую (поэтому вы можете делать такие вещи, как select t.jdoc.person.birthdate.date() from person_collection t), Microsoft SQL Server объединяет его в типы данных varchar(max) или nvarchar(max) и запрашивает из него наборы результатов, используя openjson().

AlwaysLearning 16.04.2024 14:06

Другие вопросы по теме