我正在尝试在表上执行自联接,并查找与集合(state,office_type,office_class,district)匹配的所有行,以便识别日期范围该集合没有数据。
我当前的查询:
term_alias = aliased(schema.Term, name='term_alias')
query = Session.query(schema.Term, term_alias).filter(schema.Term.office_type_id == term_alias.office_type_id).\
filter(schema.Term.state_id == term_alias.state_id).\
filter(schema.Term.office_class == term_alias.office_class).\
filter(schema.Term.term_end < term_alias.term_begin).\
filter(or_(schema.Term.district_id == term_alias.district_id,
schema.Term.district_id == None)).\
group_by(schema.Term.term_end).\
group_by(schema.Term.state_id).\
group_by(schema.Term.office_class).\
group_by(schema.Term.office_type_id).\
having(schema.Term.term_end < func.min(term_alias.term_begin)).\
having((term_alias.term_begin - schema.Term.term_end) > 1)
我得到的结果并不完全有效。
突出显示的行,不应该通过。
正如您所看到的,这实际上并不存在差距。第一项,第二项重叠因此不应作为间隙
我在上面的查询中尝试了一些变体,但是我没有得到正确的结果。我的问题是,我如何解释重叠数据?并确保仅报告真正的差距,
由print(query)
SELECT terms.id AS terms_id, terms.term_begin AS terms_term_begin, terms.term_en
d AS terms_term_end, terms.term_served AS terms_term_served, terms.office_type_i
d AS terms_office_type_id, terms.person_id AS terms_person_id, terms.state_id AS
terms_state_id, terms.district_id AS terms_district_id, terms.removal_reason_id
AS terms_removal_reason_id, terms.political_party_id AS terms_political_party_i
d, terms.is_elected AS terms_is_elected, terms.is_holdover AS terms_is_holdover,
terms.neat_race_id AS terms_neat_race_id, terms.office_class AS terms_office_cl
ass, terms.notes AS terms_notes, terms.is_vacant AS terms_is_vacant, term_alias.
id AS term_alias_id, term_alias.term_begin AS term_alias_term_begin, term_alias.
term_end AS term_alias_term_end, term_alias.term_served AS term_alias_term_serve
d, term_alias.office_type_id AS term_alias_office_type_id, term_alias.person_id
AS term_alias_person_id, term_alias.state_id AS term_alias_state_id, term_alias.
district_id AS term_alias_district_id, term_alias.removal_reason_id AS term_alia
s_removal_reason_id, term_alias.political_party_id AS term_alias_political_party
_id, term_alias.is_elected AS term_alias_is_elected, term_alias.is_holdover AS t
erm_alias_is_holdover, term_alias.neat_race_id AS term_alias_neat_race_id, term_
alias.office_class AS term_alias_office_class, term_alias.notes AS term_alias_no
tes, term_alias.is_vacant AS term_alias_is_vacant
FROM terms, terms AS term_alias
WHERE terms.office_type_id = term_alias.office_type_id AND terms.state_id = term
_alias.state_id AND terms.office_class = term_alias.office_class AND terms.term_
end < term_alias.term_begin AND (terms.district_id = term_alias.district_id OR t
erms.district_id IS NULL) GROUP BY terms.term_end, terms.state_id, terms.office_
class, terms.office_type_id
HAVING terms.term_end < min(term_alias.term_begin) AND term_alias.term_begin - t
erms.term_end > :param_1
SQLFiddle的当前状态: http://sqlfiddle.com/#!9/3e030/1
答案 0 :(得分:1)
您的查询无法处理重叠,可以从简单比较中看出。
查看您的数据,我将使用以下步骤执行搜索:
begins
:找到所有此类Term
Term.term_end
未被覆盖的任何其他Term
(适当的office_type / office_class / state / district)begin
,通过查找在差距Term
之后开始的最早begin
实例来找到差距。我参加了以下实施:
TE = aliased(Term, name='TE') # gap period start (end of last before gap)
TO = aliased(Term, name='TO') # other term for check of TE
TS = aliased(Term, name='TS') # start of the next period after gap
# condition for the existance of TE: no other periods overlaping with the next
# day after TE.term_end
# define an alias for the `TE.term_end + 1 day` (engine specific)
# gap_sdate = TE.term_end
gap_sdate = func.date(TE.term_end, text("'+1 days'")) # sqlite version
# gap_sdate = func.ADDDATE(TE.term_end, 1)) # mysql version
# subquery which checks if there are Terms covering next day after term_end
subq = (
session
.query(TO.id)
.filter(and_(
TE.office_type_id == TO.office_type_id,
TE.state_id == TO.state_id,
or_(TE.district_id == TO.district_id,
and_(TE.district_id == None, TO.district_id == None)
),
TE.office_class == TO.office_class,
))
.filter(TO.id != TE.id)
.filter(TO.term_begin <= gap_sdate)
.filter(or_(TO.term_end == None, TO.term_end >= gap_sdate))
)
# query to find the gaps
q = (
session
.query(
TE.office_type_id,
TE.state_id,
TE.district_id,
TE.office_class,
gap_sdate.label("vacant_from"),
func.date(func.min(TS.term_begin), text("'-1 days'")).label("vacant_till"), # sqlite version
# func.ADDDATE(func.min(TS.term_begin), -1).label("vacant_till"), # mysql version
)
.join(TS, and_(
TE.office_type_id == TS.office_type_id,
TE.state_id == TS.state_id,
or_(TE.district_id == TS.district_id,
and_(TE.district_id == None, TS.district_id == None)
),
TE.office_class == TS.office_class,
))
# filters for gap start
.filter(TE.term_end != None)
.filter(~subq.exists())
# filters for gap end
.filter(TE.id != TS.id)
.filter(TS.term_begin > gap_sdate)
.group_by(TE)
)
gaps = q.all()
for gap in gaps:
print(gap)