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测试要好得多。