【问题标题】:How to select Postgresql record based on calculated json value with Sqlalchemy?如何根据使用 Sqlalchemy 计算的 json 值选择 Postgresql 记录?
【发布时间】:2018-07-12 02:33:36
【问题描述】:

我有一个包含 2 列的 postgres 表,用于查询:

start_date_detail: date 
flex: jsonb

弹性域示例:

{
    "communication": [
        {
            "remind_on": "mxmw@cxla.nl",
            "type": "email",
            "remind_date": -150
        },
        {
            "remind_on": "+31612345678",
            "type": "sms",
            "remind_date": -360
        }
    ]
}

我需要选择所有提醒日期在上周的记录,所以(伪代码):

(now() - 1-week) < (start_date_detail + remind_date) < now()

我如何使用 sqlalchemy 实现这一点?

因为它是查询中的计算值,所以我不知道该怎么做。 在 postgres 中,我想出了这个,并且有效:

SELECT * FROM time_item 
WHERE 
    (start_date_detail + INTERVAL '1 second' * (flex->'communication'->0->>'remind_date')::numeric <= NOW())
    OR (start_date_detail + INTERVAL '1 second' * (flex->'communication'->1->>'remind_date')::numeric <= NOW())

如何将其放入 sqlalchemy 中?

还有一点: 在上面的查询中,我将每个通信项添加到 where 子句。我怎样才能使它更灵活?那就是我不需要为每个通信项都添加 where 子句。

【问题讨论】:

    标签: python postgresql sqlalchemy


    【解决方案1】:

    您可以使用jsonb_array_elements() 将数组扩展为一组jsonb 元素,然后您可以在谓词中使用这些元素。 SQLAlchemy 通过使用alias 支持将函数表达式作为可选择项。使用诸如

    之类的模型
    In [4]: class TimeItem(Base):
       ...:     __tablename__ = 'time_item'
       ...:     id = Column(Integer, primary_key=True)
       ...:     start_date_detail = Column(Date)
       ...:     flex = Column(JSONB)
       ...:     
    

    查询可能看起来像

    In [39]: session.query(TimeItem).\
        ...:     select_from(TimeItem,
        ...:                 func.jsonb_array_elements(TimeItem.flex['communication']).
        ...:                     alias('comm')).\
        ...:     filter((TimeItem.start_date_detail +
        ...:             timedelta(seconds=1) *
        ...:             column('comm', type_=JSONB)['remind_date'].
        ...:                 astext.
        ...:                 cast(Integer)).
        ...:                     between(func.now() - timedelta(weeks=1),
        ...:                             func.now())).\
        ...:     all()
    

    然后您可以调整谓词以满足您的需求——我尝试按照您的示例将remind_date 解释为start_date_detail 的偏移秒数,与前一周的现在和现在相比。在查询 TimeItem 等实体时,SQLAlchemy 会根据对象身份进行自己的重复数据删除,因此查询可以省略 SQL 端 DISTINCT、在 EXISTS 子查询表达式中移动数组元素等。

    【讨论】:

    • 非常好,非常感谢,给我几天使用时间,我会回来的,但我想我明白这是怎么回事
    • 查询完美,非常感谢。我仍然觉得很难掌握 JSON 查询材料。你会建议在 start_date_detail 和remind_date 上放置一个索引吗?这看起来像吗?
    • 不幸的是,您无法索引使用来自集合返回函数的派生值的表达式,例如jsonb_array_elements()。如果start_date_detailremind_date 组合存在已知限制,您可以使用start_date_detail 上的范围作为可索引的“粗略”谓词。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-07
    • 1970-01-01
    • 2019-01-23
    • 1970-01-01
    • 2023-03-17
    • 2016-11-06
    • 1970-01-01
    相关资源
    最近更新 更多