Sql 选择运动员在过去3个项目中未首先完成的项目
假设我有一个运动会结果数据库,其模式如下Sql 选择运动员在过去3个项目中未首先完成的项目,sql,postgresql,window-functions,Sql,Postgresql,Window Functions,假设我有一个运动会结果数据库,其模式如下 DATE,NAME,FINISH_POS 我想做一个查询,选择运动员至少参加过三个项目但未获胜的所有行。例如,使用以下示例数据 2013-06-22,Johnson,2 2013-06-21,Johnson,1 2013-06-20,Johnson,4 2013-06-19,Johnson,2 2013-06-18,Johnson,3 2013-06-17,Johnson,4 2013-06-16,Johnson,3 2013-06-15,Johnso
DATE,NAME,FINISH_POS
我想做一个查询,选择运动员至少参加过三个项目但未获胜的所有行。例如,使用以下示例数据
2013-06-22,Johnson,2
2013-06-21,Johnson,1
2013-06-20,Johnson,4
2013-06-19,Johnson,2
2013-06-18,Johnson,3
2013-06-17,Johnson,4
2013-06-16,Johnson,3
2013-06-15,Johnson,1
以下行:
2013-06-20,Johnson,4
2013-06-19,Johnson,2
将是匹配的。我只设法从以下存根开始:
select date,name FROM table WHERE ...;
我一直在想where从句,但我甚至不能开始
; with CTE as
(
select row_number() over (partition by athlete order by date) rn
, *
from Table1
)
select *
from CTE cur
where not exists
(
select *
from CTE prev
where prev.place = 1
and prev.athlete = cur.athlete
and prev.rn between cur.rn - 3 and cur.rn
)
这里有一个替代公式,它在两次扫描中不使用子查询:
SELECT
"date", athlete, place
FROM (
SELECT
"date",
place,
athlete,
1 <> ALL (array_agg(place) OVER w) AS include_row
FROM Table1
WINDOW w AS (PARTITION BY athlete ORDER BY "date" ASC ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)
) AS history
WHERE include_row;
见:
这里的逻辑基本上是这个问题的直译。获取最后四个位置-当前位置和前三个位置-并返回运动员在其中任何一个位置都没有首先完成的任何行
因为窗口框是定义要考虑的历史行数的唯一位置,所以您可以将该变量参数化,不像以前的工作已经过时了,所以它对于任何n都适用于最后n。它也比上次试的效率高很多
当在一个非平凡大小的数据集上执行这个vs@Andomar查询时,我真的很感兴趣。在这个小数据集上,它们几乎完全相同。要在大型数据集上以最佳方式执行此操作,需要在Table1athlete、date上建立索引。我认为这可能更简单/更快:
SELECT day, place, athlete
FROM (
SELECT *, min(place) OVER (PARTITION BY athlete
ORDER BY day
ROWS 3 PRECEDING) AS best
FROM t
) sub
WHERE best > 1
将聚合函数min用作窗口函数,以获取最后三行加上当前行的最小位置。
由于窗口函数是在WHERE子句之后应用的,因此必须在下一个查询级别上对no win best>1进行简单的检查。因此,对于窗口函数结果的条件,至少需要一个子选择
有关的详细信息。特别是:
如果省略帧_end,则默认为当前行
如果place finishing_pos可以为空,请改用此选项:
WHERE best IS DISTINCT FROM 1
min忽略空值,但如果帧中的所有行都为空,则结果为空
不要使用类型名和保留字作为标识符,我用day代替了日期
这假设每天最多有1次竞争,否则您必须定义如何在时间线中处理同级竞争,或者使用时间戳而不是日期
已经提到了使这一速度加快的索引。你是如何得到这两行的?2013-06-15的韩元因此忽略接下来的三个条目。然后抓取2013-16-19,2013-06-20,忽略2013-06-22,因为这只是最后一次胜利之后的一个事件。简单提示一下:日期是一个糟糕的列名;它是一个类型名,因此您必须在某些地方对其进行双引号引用,以避免语法错误。避免使用类型名或关键字作为列名。请始终在问题中包含您的PostgreSQL版本。在本例中,Andomar和我都使用了8.4版之后才有的功能,可能是更新的。有趣的方法。Pg使用散列反连接而不是嵌套循环来执行它,这一事实给我留下了深刻的印象。+1有趣的是,你发现min aggregate的性能最好:关于min的使用,DVery good point;这比使用array_agg和contains测试要好得多。