在PostgreSQL中查找重叠的date范围

它是否正确?

SELECT * FROM contract JOIN team USING (name_team) JOIN player USING(name_player) WHERE name_team = ? AND DATE_PART('YEAR',date_join)>= ? AND DATE_PART('YEAR',date_leave)<= ? 

我的桌面contract有球员姓名,球队名称和他join和离开俱乐部的date。
我想列出一个函数,列出特定年份所有在队中的球员。
上述查询似乎并没有工作…

为什么不使用date部分之间的东西:

WHERE datefield BETWEEN '2009-10-10 00:00:00' AND '2009-10-11 00:00:00'

或类似的东西?

接受的答案是不好的

build议a BETWEEN x AND y的答案得到了很多upvotes,并被接受了近2年。 但这不但不能回答问题,原则上也是错误的。

a BETWEEN x AND y转换就是:

 a >= x AND a <= y 

包括上边界,而人们通常需要排除它:

 a >= x AND a < y 

date,你可以很容易地调整。 2009年使用“2009-12-31”作为上边界。
但是对于允许小数位的时间戳来说并不那么简单。 现代Postgres版本在内部使用一个8字节的整数来存储多达6个小数秒(μs分辨率)。 知道这一点,我们仍然可以使其工作,但这不是直观的,取决于实施细节。 馊主意。

此外,在这种特殊情况下, a BETWEEN x AND y之间无法find与另一个范围重叠的范围。 我们需要的是:

 b >= x AND a < y 

从来没有离开的球员还没有被考虑。

正确的答案

假设在2009 ,我会重写这个问题而不改变它的含义:

“find2010年之前join的所有球员,并且在2009年之前不会离开。”

 SELECT p.* FROM team t JOIN contract c USING (name_team) JOIN player p USING (name_player) WHERE t.name_team = ? AND c.date_join < date '2010-01-01' AND c.date_leave >= date '2009-01-01'; 

运算符优先级对我们有效, ANDOR之前进行绑定。 我们需要括号。

如果参照完整性没有被破坏,表格team本身就是这个查询中的噪声,可以被删除。

虽然同一个玩家可以离开并重新join同一个团队,但我们也需要折叠可能的重复,例如DISTINCT

我们可能需要提供一个特殊情况:从未离开过的玩家。 假设这些玩家有date_leave IS NULL

“一个不知道离开的球员现在被认为是为球队效力。”

到达这个优化的查询:

 SELECT DISTINCT p.* FROM contract c JOIN player p USING (name_player) WHERE c.name_team = ? AND c.date_join < date '2010-01-01' AND (c.date_leave >= date '2009-01-01' OR c.date_leave IS NULL); 

相关的答案与优化DISTINCT (如果重复是常见的):

  • 多桌多桌 – performance糟糕

通常, 名称不是唯一的,代理主键用于自然人。 但显然, name_playerplayer的主要关键。 如果您只需要玩家名称,则不需要查询中的表格player

 SELECT DISTINCT c.name_player FROM contract c WHERE c.name_team = ? AND c.date_join < date '2010-01-01' AND (c.date_leave >= date '2009-01-01' OR c.date_leave IS NULL); 

我们也可以使用SQL OVERLAPS操作符 :

OVERLAPS自动以该对的较早值作为开始。 每个时间段被认为代表半开区间start <= time < end ,除非startend相等,在这种情况下它代表单个时刻。

但是我们需要注意可能的NULL值。 使用COALESCE最简单:

 SELECT DISTINCT c.name_player FROM contract c WHERE t.name_team = ? AND (c.date_join, COALESCE(c.date_leave, current_date)) OVERLAPS ('2009-01-01'::date, '2009-12-31'::date) 

在Postgres 9.2或更高版本中,您还可以将实际范围types重叠操作符&& (可以使用GiST索引支持)结合使用。 例:

  • 平均库存历史表