aoc*_*ira 12 sql postgresql date-range overlap
它是否正确?
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)<= ?
Run Code Online (Sandbox Code Playgroud)
我的桌子上contract有球员姓名,球队名称以及他加入和离开俱乐部的日期.
我想制作一个功能,列出特定年份团队中的所有玩家.
上面的查询似乎没有工作......
Erw*_*ter 68
在目前接受的答案不回答这个问题.原则上这是错误的.a BETWEEN x AND y翻译为:
a >= x AND a <= yRun Code Online (Sandbox Code Playgroud)
包括上边框,而人们通常需要排除它:
a >= x AND a < yRun Code Online (Sandbox Code Playgroud)
有了日期,您可以轻松调整.2009年使用'2009-12-31'作为上边界.
但是时间戳允许小数位并不简单.现代Postgres版本在内部使用8字节整数来存储最多6个小数秒(μs分辨率).知道了这一点,我们可能仍然使它的工作,但是这并不直观,依赖于实现细节.馊主意.
而且,a BETWEEN x AND y没有找到重叠的范围.我们需要:
b >= x AND a < yRun Code Online (Sandbox Code Playgroud)
从未离开的球员尚未考虑.
假设这一年2009,我会在不改变其含义的情况下重新解释这个问题:
"查找在2010年之前加入并且在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';
Run Code Online (Sandbox Code Playgroud)
但还有更多:
如果使用FK约束强制执行引用完整性,则表team本身只是查询中的噪声,可以删除.
虽然同一个玩家可以离开并重新加入同一个团队,但我们还需要折叠可能的重复项,例如DISTINCT.
我们可能需要提供一个特殊情况:从未离开的球员.假设这些玩家中有NULL date_leave.
"一名不知道已经离开的球员被假定为今天为球队效力."
精炼查询:
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);
Run Code Online (Sandbox Code Playgroud)
运算符优先级对我们起作用,AND之前绑定OR.我们需要括号.
优化的相关答案DISTINCT(如果重复是常见的):
通常,自然人的姓名不是唯一的,并且使用代理主键.但是,显然,name_player是主要的关键player.如果您只需要玩家名称,我们不需要player查询中的表格,或者:
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND date_join < date '2010-01-01'
AND (date_leave >= date '2009-01-01' OR date_leave IS NULL);
Run Code Online (Sandbox Code Playgroud)
OVERLAPS运算符
OVERLAPS自动将该对的较早值作为开始.每个时间段被认为代表半开时间间隔start <= time < end,除非start并且end相等,在这种情况下它代表单个时间瞬间.
要照顾潜在的NULL价值观,COALESCE看起来最简单:
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND (date_join, COALESCE(date_leave, CURRENT_DATE)) OVERLAPS
(date '2009-01-01', date '2010-01-01'); -- upper bound excluded
Run Code Online (Sandbox Code Playgroud)
在Postgres 9.2或更高版本中,您还可以使用实际范围类型进行操作:
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND daterange(date_join, date_leave) &&
daterange '[2009-01-01,2010-01-01)'; -- upper bound excluded
Run Code Online (Sandbox Code Playgroud)
范围类型增加了一些开销并占用更多空间.2 x date= 8字节; 1 x daterange=磁盘上的14个字节或RAM中的17个字节.但是与重叠运算符&&结合使用时,可以使用GiST索引支持查询.
此外,不需要特殊情况下的NULL值.NULL表示范围类型中的"开放范围" - 正是我们需要的.表定义甚至不需要改变:我们可以动态创建范围类型 - 并使用匹配的表达式索引支持查询:
CREATE INDEX mv_stock_dr_idx ON mv_stock USING gist (daterange(date_join, date_leave));
Run Code Online (Sandbox Code Playgroud)
有关:
为什么不在没有日期部分的情况下使用:
WHERE datefield BETWEEN '2009-10-10 00:00:00' AND '2009-10-11 00:00:00'
Run Code Online (Sandbox Code Playgroud)
或类似的东西?
| 归档时间: |
|
| 查看次数: |
13982 次 |
| 最近记录: |