【问题标题】:Which Django ORM or raw SQL should I write to get exactly what I need我应该编写哪个 Django ORM 或原始 SQL 才能得到我需要的东西
【发布时间】:2022-01-13 19:41:03
【问题描述】:

我正在使用 Postgresql。我有模特:

class EventsList(CreatedUpdatedMixin):
    start = models.DateTimeField()
    end = models.DateTimeField()
    is_inner = models.BooleanField()

假设我有这些数据库条目:

start end is_inner
2021-12-09 14:30:12 2021-12-09 15:00:21 true
2021-12-09 14:00:05 2021-12-10 21:00:15 false
2021-12-10 09:00:39 2021-12-10 09:30:50 true
2021-12-10 14:00:00 2021-12-11 15:00:00 true
2021-12-14 10:00:00 2021-12-14 11:00:00 true
2021-12-13 13:30:00 2021-12-16 14:30:00 false
2021-12-14 13:10:00 2021-12-15 00:30:00 true
2021-12-14 10:30:00 2021-12-16 13:34:00 false
2021-12-15 13:30:00 2021-12-15 18:30:00 true

还有我需要的结果:

[
    {"2021-12-09": {"external_events": 1, "internal_events": 1}},
    {"2021-12-10": {"external_events": 0, "internal_events": 2}},
    {"2021-12-11": {"external_events": 0, "internal_events": 1}},
    {"2021-12-13": {"external_events": 1, "internal_events": 0}},
    {"2021-12-14": {"external_events": 2, "internal_events": 2}},
    {"2021-12-15": {"external_events": 2, "internal_events": 2}},
    {"2021-12-16": {"external_events": 2, "internal_events": 0}},
]

所以我想获取所有现有日期,并为每个日期获取外部事件的计数(其中 is_inner == False)和内部事件的计数(其中 is_inner == True)。 如何使用 Django ORM 或原始 SQL 来实现? 现在,我想出了

EventsList.objects.annotate(
    start_day=Cast("start", output_field=DateField())
).values("start_day").annotate(
    external_events=Count("id", filter=Q(is_inner=False)),
    internal_events=Count("id", filter=Q(is_inner=True)),
).values(
    "start_day", "external_events", "internal_events"
)

此代码返回几乎正确的结果(但仅适用于“开始”日期)。我需要包括所有日期(开始日期、结束日期以及它们之间的所有日期)。将不胜感激。

【问题讨论】:

    标签: python sql django postgresql orm


    【解决方案1】:

    使用原始 SQL,首先使用 generate_serieslateral join 扩展日期列表,然后进行条件聚合。所以在这里,有点冗长,但我希望易于阅读。 SQL Fiddle

    with t as
    (
     select is_inner, d::date from the_table 
     cross join lateral generate_series
     (
      date_trunc('day', "start"), 
      date_trunc('day', "end"),  
      interval '1 day'
     ) as d
    )
    select d as event_date, 
           count(*) filter (where not is_inner) external_events,   
           count(*) filter (where is_inner) internal_events
    from t
    group by d order by d; 
    

    您也可以使用jsonb_build_object 来塑造问题中的 JSON 结构,如下所示:

    with t as
    (
     select is_inner, d::date from the_table 
     cross join lateral generate_series
     (
      date_trunc('day', "start"), 
      date_trunc('day', "end"),  
      interval '1 day'
     ) as d
    )
    select jsonb_build_object
    (
      d::text, 
      jsonb_build_object('external_events', count(*) filter (where not is_inner),   
                         'internal_events', count(*) filter (where is_inner))
    ) as date_info   
    from t
    group by d order by d; 
    
    date_info
    {"2021-12-09": {"external_events": 1, "internal_events": 1}}
    {"2021-12-10": {"external_events": 1, "internal_events": 2}}
    {"2021-12-11": {"external_events": 0, "internal_events": 1}}
    {"2021-12-13": {"external_events": 1, "internal_events": 0}}
    {"2021-12-14": {"external_events": 2, "internal_events": 2}}
    {"2021-12-15": {"external_events": 2, "internal_events": 2}}
    {"2021-12-16": {"external_events": 2, "internal_events": 0}}

    【讨论】:

      【解决方案2】:

      首先,要获取不在您的数据库中的所有日期,您需要在最大和最小日期之间循环以获取所有日期,并为每个日期运行查询以匹配您想要的过滤器。

      【讨论】:

        猜你喜欢
        • 2023-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-20
        • 2011-03-29
        • 2018-03-10
        • 1970-01-01
        • 2019-08-05
        相关资源
        最近更新 更多