【问题标题】:Is it possible to join a table with itself twice? (Using SQLAlchemy or SQL)是否可以将一个表与自身连接两次? (使用 SQLAlchemy 或 SQL)
【发布时间】:2022-01-21 14:52:47
【问题描述】:

我在 SQLAlchemy 中有以下模型:

    class SomeEvent(db.Model):
        id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
        created_on = db.Column(db.DateTime())
        type = db.Column(db.String(20))
        event_target = db.Column(db.String(10))
        group = db.Column(db.String(20))

有没有办法产生一个查询,该查询将返回一个结果,该结果由一个类型的所有记录 + 两个其他类型的记录(如果这两个记录具有相同的 event_target 否则只是一个类型的记录)的相同组排序通过created_on 日期?

我想出了什么:

        Single_type_event = aliased(SomeEvent)
        Paired_type_event_1 = aliased(SomeEvent)
        Paired_type_event_2 = aliased(SomeEvent)

        columns = [
            Single_type_event.id.label('single_type_event_id'),
            Single_type_event.type.label('single_type_event_type'),
            Single_type_event.event_target.label('single_type_event_target'),
            Paired_type_event_1.id.label('paired_type_event_1_id'),
            Paired_type_event_1.type.label('paired_type_event_1_type'),
            Paired_type_event_1.event_target.label('paired_type_event_1_target'),
            Paired_type_event_2.id.label('paired_type_event_2_id'),
            Paired_type_event_2.type.label('paired_type_event_2_type'),
            Paired_type_event_2.event_target.label('paired_type_event_2_target'),
        ]

        query = (db.session.query(*columns)
                .outerjoin(SomeEvent, (
                    (SomeEvent.group == 'some_group') & 
                    (SomeEvent.id == Single_type_event.id) |
                    (SomeEvent.id == Paired_type_event_1.id) |
                    (SomeEvent.id == Paired_type_event_2.id)
                ))
                .outerjoin(Single_type_event, (
                    (SomeEvent.id == Single_type_event.id) &
                    (SomeEvent.event_target == Single_type_event.event_target) &
                    (SomeEvent.type == 'type_1')
                ))
                .outerjoin(Paired_type_event_1, (
                    (SomeEvent.id == Paired_type_event_1.id) &
                    (SomeEvent.event_target == Paired_type_event_1.event_target) &
                    (SomeEvent.type == 'type_2')
                ))
                .outerjoin(Paired_type_event_2, (
                    (SomeEvent.id == Paired_type_event_2.id) &
                    (SomeEvent.event_target == Paired_type_event_2.event_target) &
                    (SomeEvent.type == 'type_3')
                ))
                .order_by(SomeEvent.created_on.asc())
        )

但当然它不起作用,因为我缺乏对如何正确执行此操作的理解,如果可能的话 =(

我想达到的查询结果示例(._asdict()已申请):

  1. 如果具有给定event_target 的记录具有type == 'type_1'

    {'single_type_event_id': 'some_id',
    'single_type_event_type': 'type_1',
    'single_type_event_target': 'target_1',
    'paired_type_event_1_id': None,
    'paired_type_event_1_type': None,
    'paired_type_event_1_target': None,
    'paired_type_event_2_id': None,
    'paired_type_event_2_type': None,
    'paired_type_event_2_target': None}

  1. 如果对于给定的event_target,只有一条带有type == 'type_2' 的记录(它之前也可以有一条type_1 记录,但它应该像之前一样位于不同的行中):

    {'single_type_event_id': None,
    'single_type_event_type': None,
    'single_type_event_target': None,
    'paired_type_event_1_id': 'some_id',
    'paired_type_event_1_type': 'type_2',
    'paired_type_event_1_target': 'target_1',
    'paired_type_event_2_id': None,
    'paired_type_event_2_type': None,
    'paired_type_event_2_target': None}

  1. 最后,如果对于给定的event_target 有两种事件类型的记录(每种类型不应该有不同的行 - 只有组合的这一行):
    {'single_type_event_id': None,
    'single_type_event_type': None,
    'single_type_event_target': None,
    'paired_type_event_1_id': 'some_id_1',
    'paired_type_event_1_type': 'type_2',
    'paired_type_event_1_target': 'target_1',
    'paired_type_event_2_id': 'some_id_2',
    'paired_type_event_2_type': 'type_3',
    'paired_type_event_2_target': 'target_1'}

我希望所有这些结果具有相同的组并按 created_on 排序(对于最后一个示例,它应该按paired_type_event_1 日期排序)。

一个抽象的例子: person_1(event_target) 住在 town_a(group) 中。他有他出生时间的记录(type == 'birth'),这是他的single_type_event(上面结果的第一个案例)。这个person_1也有开学记录(type == 'enrollment'),但是没有毕业记录。这是他的paired_type_event_1(结果的第二种情况)。如果此人有毕业记录 (type == 'graduation'),那么它将与注册记录显示在同一行中。假设这张表实际上是town_a的纸质记录簿,这些记录应该按以下顺序组织:出生(单行)->注册(另一行)(出生​​(单行)->注册+毕业(另一行)毕业的人)

我知道这看起来很混乱,对此我有一些坚定的怀疑,但如果可以在 SQLAlchemy 或使用原始 SQL 中实现,我将非常感谢您的指导!

【问题讨论】:

    标签: python sql sqlalchemy


    【解决方案1】:

    不确定我是否完全符合要求,但假设“事件类型”在“事件组”中是唯一的(即“事件组”绑定单个事件和成对事件)...

    这个想法是从所有事件组和目标的列表(allGroups)开始,然后在各种类型的数据可用时匹配它(左连接)

    ;with allGroups as 
    (
        select distinct eventGroup, eventTarget 
        from SomeEvent
        where ???
    )
    select ag.eventGroup, ag.eventTarget, type1 = e1.eventType, type2 = e2.eventType, type3 = e3.eventType
        , createDate = case when e1.createDate is not null then e1.createDate
                            when e2.createDate is not null then e2.createDate
                            when e3.createDate is not null then e3.createDate
                            else null
                        end
    from allGroups ag
    left join SomeEvent e1 on ag.eventTarget = e1.eventTarget and ag.eventGroup = e1.eventGroup and e2.eventType = 'type_1'
    left join SomeEvent e2 on ag.eventTarget = e2.eventTarget and ag.eventGroup = e2.eventGroup and e2.eventType = 'type_2'
    left join SomeEvent e3 on ag.eventTarget = e3.eventTarget and ag.eventGroup = e3.eventGroup and e3.eventType = 'type_3'
    order by createDate
    

    如果您不能在您的平台上执行 CTE(通用表表达式),您可以对临时表、表变量等进行初始选择。

    希望这能让你思考。

    【讨论】:

    • 非常感谢您的帮助。虽然我不确定我是否能够足够清楚地表达我的问题,所以我在纸质记录簿中添加了一个示例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-26
    • 1970-01-01
    相关资源
    最近更新 更多