Есть ли способ в SQLAlchemy ORM для отображения одной таблицы вертикально сохраненных списков? - PullRequest
0 голосов
/ 11 января 2020

У меня есть база данных (только для чтения, не управляемая мной), которая состоит из нескольких таблиц фактов различных измерений. Фактические данные, содержащиеся в этих таблицах, могут быть скалярными (int / цифра c) или векторными (список целых / цифра c). Векторы и скаляры хранятся в отдельных таблицах, поэтому векторы могут храниться вертикально (хранятся в виде скаляров с дополнительным VECTOR_INDEX измерением, которое указывает положение этого элемента внутри вектора).

Поэтому векторные таблицы выглядят несколько например:

+-------+-------+--------------+--------+--------+
| key_a | key_b | vector_index | data_u | data_v |
+-------+-------+--------------+--------+--------+
|     1 |     1 |            1 | 123    |    456 |
|     1 |     1 |            2 | 234    |    567 |
|     1 |     1 |            3 | <null> |    678 |
|     1 |     1 |            4 | <null> |    789 |
|     1 |     2 |            1 | 111    |    222 |
+-------+-------+--------------+--------+--------+

Я уже использую SQLAlchemy для других целей и пытаюсь выяснить, могу ли я получить ORM для преобразования этих вертикальных векторов в дикты или списки для меня. Документация по SQLAlchemy содержит пример работы с вертикально сохраненными данными , но проблема здесь в том, что attribute_mapped_collection("key") работает только применительно к отношению.

Если я пытаюсь выполнить То же самое и определить векторную таблицу как «связанную» с моей скалярной таблицей, она «технически работает»:

from sqlalchemy import Column, Integer
from sqlalchemy.orm import Session, relationship, joinedload
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.collections import attribute_mapped_collection

Base = declarative_base()

class FactTable_Vec(Base):
    __tablename__ = "fact_table"

    key_a = Column(Integer, primary_key=True)
    key_b = Column(Integer, primary_key=True)

    vector_index = Column(Integer, primary_key=True)

    # Vector data
    data_u = Column(Integer)
    data_v = Column(Integer)


class FactTable(Base):
    __tablename__ = "fact_table_v"

    key_a = Column(Integer, primary_key=True)
    key_b = Column(Integer, primary_key=True)

    # Scalar data
    data_x = Column(Integer)
    data_y = Column(Integer)

    _vector_data = relationship(
        "FactTable_Vec",
        collection_class=attribute_mapped_collection("vector_index"),
        uselist=True,
        primaryjoin="and_(FactTable.key_a == foreign(FactTable_Vec.key_a), FactTable.key_b == foreign(FactTable_Vec.key_b))",
    )

    data_u = association_proxy('_vector_data', 'data_u')
    data_v = association_proxy('_vector_data', 'data_v')

def fetch_info(db_sess):
    query = db_sess.query(FactTable).options(joinedload(FactTable._vector_data)).filter(and_(FactTable.key_a == 1))

    for item in query.all():
        print(item.key_a, item.key_b, item.data_u, item.data_v)

Это делает то, что я хочу, возвращает data_u и data_v как диктовки основанный на их векторном индексе , но штраф за производительность от объединения этих двух таблиц фактов (который даже не нужен, так как ключи, по которым я фильтрую, распределяется между обеими таблицами, и мне не нужны никакие данные из скалярной таблицы в данном случае) колоссальный , так что это не совсем вариант.

Возможно ли иметь что-то похожее на attribute_mapped_collection для одной таблицы? Чтобы «объединить» эти вертикально сохраненные векторы в дикты (возможно, активные, но это не имеет значения в моем случае использования) таким же удобным для ORM способом без необходимости включать соединение?

...