문제

Sqlalchemy에서 표현하고 싶은 Star-Schema Architectured 데이터베이스가 있습니다. 이제 나는 이것이 가능한 최선의 방법으로 어떻게 할 수 있는지에 대한 문제가 있습니다. 데이터는 다른 테이블에 저장되므로 사용자 정의 조인 조건이 포함 된 많은 속성이 있습니다. 다른 사실 테이블의 차원을 재사용 할 수 있다면 좋을 것입니다.

도움이 되었습니까?

해결책

스타 스키마의 일반적인 사실 테이블에는 모든 차원 테이블에 대한 외국 키 참조가 포함되어 있으므로 일반적으로 사용자 지정 조인 조건이 필요하지 않습니다. 이는 외국 키 참조에서 자동으로 결정됩니다.

예를 들어 두 개의 사실 테이블이있는 스타 스키마는 다음과 같습니다.

Base = declarative_meta()

class Store(Base):
    __tablename__ = 'store'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String(50), nullable=False)

class Product(Base):
    __tablename__ = 'product'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String(50), nullable=False)

class FactOne(Base):
    __tablename__ = 'sales_fact_one'

    store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True)
    product_id = Column('product_id', Integer, ForeignKey('product.id'), primary_key=True)
    units_sold = Column('units_sold', Integer, nullable=False)

    store = relation(Store)
    product = relation(Product)

class FactTwo(Base):
    __tablename__ = 'sales_fact_two'

    store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True)
    product_id = Column('product_id', Integer, ForeignKey('product.id'), primary_key=True)
    units_sold = Column('units_sold', Integer, nullable=False)

    store = relation(Store)
    product = relation(Product)

그러나 어떤 경우에도 보일러 플레이트를 줄이고 싶다고 가정하십시오. 사실 테이블에 자신을 구성하는 치수 클래스에 로컬 생성기를 만들었습니다.

class Store(Base):
    __tablename__ = 'store'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String(50), nullable=False)

    @classmethod
    def add_dimension(cls, target):
        target.store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True)
        target.store = relation(cls)

이 경우 사용량은 다음과 같습니다.

class FactOne(Base):
    ...

Store.add_dimension(FactOne)

그러나 그것에 문제가 있습니다. 추가하는 치수 열이 기본 키 열이라고 가정하면 매핑이 설정되기 전에 클래스가 기본 키를 설정해야하므로 맵퍼 구성이 실패합니다. 따라서 우리가 선언을 사용한다고 가정하면 (아래에서 볼 수있는 좋은 효과가 있습니다),이 접근 방식을 작동시키기 위해 instrument_declarative() 표준 메타 클래스 대신 기능 :

meta = MetaData()
registry = {}
def register_cls(*cls):
    for c in cls:
        instrument_declarative(c, registry, meta)

그러면 우리는 다음의 선을 따라 무언가를 할 것입니다.

class Store(object):
    # ...

class FactOne(object):
    __tablename__ = 'sales_fact_one'

Store.add_dimension(FactOne)

register_cls(Store, FactOne)

실제로 사용자 정의 조인 조건에 대한 정당한 이유가 있다면 해당 조건이 어떻게 생성되는지에 대한 패턴이있는 한,이를 통해이를 생성 할 수 있습니다. add_dimension():

class Store(object):
    ...

    @classmethod
    def add_dimension(cls, target):
        target.store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True)
        target.store = relation(cls, primaryjoin=target.store_id==cls.id)

하지만 마지막으로 멋진 것은 2.6에있는 것입니다. add_dimension 클래스 데코레이터로. 다음은 모든 것을 정리하는 예입니다.

from sqlalchemy import *
from sqlalchemy.ext.declarative import instrument_declarative
from sqlalchemy.orm import *

class BaseMeta(type):
    classes = set()
    def __init__(cls, classname, bases, dict_):
        klass = type.__init__(cls, classname, bases, dict_)
        if 'metadata' not in dict_:
            BaseMeta.classes.add(cls)
        return klass

class Base(object):
    __metaclass__ = BaseMeta
    metadata = MetaData()
    def __init__(self, **kw):
        for k in kw:
            setattr(self, k, kw[k])

    @classmethod
    def configure(cls, *klasses):
        registry = {}
        for c in BaseMeta.classes:
            instrument_declarative(c, registry, cls.metadata)

class Store(Base):
    __tablename__ = 'store'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String(50), nullable=False)

    @classmethod
    def dimension(cls, target):
        target.store_id = Column('store_id', Integer, ForeignKey('store.id'), primary_key=True)
        target.store = relation(cls)
        return target

class Product(Base):
    __tablename__ = 'product'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String(50), nullable=False)

    @classmethod
    def dimension(cls, target):
        target.product_id = Column('product_id', Integer, ForeignKey('product.id'), primary_key=True)
        target.product = relation(cls)
        return target

@Store.dimension
@Product.dimension
class FactOne(Base):
    __tablename__ = 'sales_fact_one'

    units_sold = Column('units_sold', Integer, nullable=False)

@Store.dimension
@Product.dimension
class FactTwo(Base):
    __tablename__ = 'sales_fact_two'

    units_sold = Column('units_sold', Integer, nullable=False)

Base.configure()

if __name__ == '__main__':
    engine = create_engine('sqlite://', echo=True)
    Base.metadata.create_all(engine)

    sess = sessionmaker(engine)()

    sess.add(FactOne(store=Store(name='s1'), product=Product(name='p1'), units_sold=27))
    sess.commit()
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top