【发布时间】:2022-01-02 23:53:17
【问题描述】:
我已经坚持了几天了。一个事件可以有多个日期,我希望查询只返回最接近今天的日期(下一个日期)。我考虑过查询事件,然后将混合属性添加到返回下一个事件日期的事件,但我相信这不会成功(例如,如果我想查询某个范围内的事件日期)。
我遇到了 distinct() 无法按预期工作的问题。请记住,我不是 SQL 专家。另外,我正在使用 postgres。
我的查询是这样开始的:
distance_expression = func.ST_Distance(
cast(EventLocation.geo, Geography(srid=4326)),
cast("SRID=4326;POINT(%f %f)" % (lng, lat), Geography(srid=4326)),
)
query = (
db.session.query(EventDate)
.populate_existing()
.options(
with_expression(
EventDate.distance,
distance_expression,
)
)
.join(Event, EventDate.event_id == Event.id)
.join(EventLocation, EventDate.location_id == EventLocation.id)
)
然后我有多个过滤器(仅显示几个作为示例)
query = query.filter(EventDate.start >= datetime.utcnow)
if kwargs.get("locality_id", None) is not None:
query = query.filter(EventLocation.locality_id == kwargs.pop("locality_id"))
if kwargs.get("region_id", None) is not None:
query = query.filter(EventLocation.region_id == kwargs.pop("region_id"))
if kwargs.get("country_id", None) is not None:
query = query.filter(EventLocation.country_id == kwargs.pop("country_id"))
然后我想按日期和距离排序(使用我的查询表达式)
query = query.order_by(
EventDate.start.asc(),
distance_expression.asc(),
)
最后我想得到不同的行,并且只返回事件的下一个 EventDate,根据上面代码块中的顺序。
query = query.distinct(Event.id)
问题是这不起作用,我得到一个数据库错误。这是生成的 SQL 的样子:
SELECT DISTINCT ON (events.id) ST_Distance(CAST(event_locations.geo AS geography(GEOMETRY,4326)), CAST(ST_GeogFromText(%(param_1)s) AS geography(GEOMETRY,4326))) AS "ST_Distance_1", event_dates.id AS event_dates_id, event_dates.created_at AS event_dates_created_at, event_dates.event_id AS event_dates_event_id, event_dates.tz AS event_dates_tz, event_dates.start AS event_dates_start, event_dates."end" AS event_dates_end, event_dates.start_naive AS event_dates_start_naive, event_dates.end_naive AS event_dates_end_naive, event_dates.location_id AS event_dates_location_id, event_dates.description AS event_dates_description, event_dates.description_attribute AS event_dates_description_attribute, event_dates.url AS event_dates_url, event_dates.ticket_url AS event_dates_ticket_url, event_dates.cancelled AS event_dates_cancelled, event_dates.size AS event_dates_size
FROM event_dates JOIN events ON event_dates.event_id = events.id JOIN event_locations ON event_dates.location_id = event_locations.id
WHERE events.hidden = false AND event_dates.start >= %(start_1)s AND (event_locations.lat BETWEEN %(lat_1)s AND %(lat_2)s OR false) AND (event_locations.lng BETWEEN %(lng_1)s AND %(lng_2)s OR false) AND ST_DWithin(CAST(event_locations.geo AS geography(GEOMETRY,4326)), CAST(ST_GeogFromText(%(param_2)s) AS geography(GEOMETRY,4326)), %(ST_DWithin_1)s) ORDER BY event_dates.start ASC, ST_Distance(CAST(event_locations.geo AS geography(GEOMETRY,4326)), CAST(ST_GeogFromText(%(param_3)s) AS geography(GEOMETRY,4326))) ASC
我尝试了很多不同的东西和顺序,但我无法解决这个问题。我还尝试在最后使用 from_self() 创建一个子查询,但它没有保持顺序。
任何帮助将不胜感激!
编辑:
在进一步的实验中,我似乎无法使用 order_by,只有在排序与我用于 distinct() 相同的字段时才会起作用。所以
query = query.order_by(EventDate.event_id).distinct(EventDate.event_id)
会起作用,但是
query.order_by(EventDate.start).distinct(EventDate.event_id)
不会:/
【问题讨论】:
-
如果您尝试使用 DISTINCT ON...ORDER BY best-n-per-group 方法,请按事件 id 排序,然后按开始日期(或您的“最大”日期)。这将为您提供所需的每个事件 ID 的行。
-
您是否尝试过最初使用使 DISTINCT ON 工作所需的 ORDER BY,然后使用 from_self 强加您想要的最终排序?你说 from_self 没有保持顺序。它应该强加它想要的顺序。
-
@jjanes 我确实尝试过,但我想先对查询进行排序(使用 EventDate.start),然后根据该排序获取不同的 EventDate.event_id 行。这似乎是不可能的。但是我找到了一种解决方法,如果您好奇,请参阅下面的答案。
标签: sql postgresql sqlalchemy flask-sqlalchemy