在 SQL 中使用窗口函数运行“匹配”总数

Posted

技术标签:

【中文标题】在 SQL 中使用窗口函数运行“匹配”总数【英文标题】:Running total of "matches" using a window function in SQL 【发布时间】:2012-02-20 21:30:18 【问题描述】:

我想创建一个窗口函数,该函数将计算当前行中字段的值出现在当前行之前的有序分区部分中出现的次数。为了更具体,假设我们有一个这样的表:

| id| fruit  | date |
+---+--------+------+
| 1 | apple  |   1  |
| 1 | cherry |   2  |
| 1 | apple  |   3  |
| 1 | cherry |   4  |
| 2 | orange |   1  |
| 2 | grape  |   2  |
| 2 | grape  |   3  |

我们想创建一个像这样的表(为了清楚起见,省略了日期列):

| id| fruit  | prior |  
+---+--------+-------+
| 1 | apple  |   0   |
| 1 | cherry |   0   |
| 1 | apple  |   1   |
| 1 | cherry |   1   |
| 2 | orange |   0   |
| 2 | grape  |   0   |
| 2 | grape  |   1   |

请注意,对于id = 1,沿着有序分区移动,第一个条目“苹果”不匹配任何内容(因为隐含集合为空),下一个水果“樱桃”也不匹配。然后我们再次进入“apple”,这是一个匹配项,依此类推。我想象 SQL 看起来像这样:

SELECT
id, fruit, 
<some kind of INTERSECT?> OVER (PARTITION BY id ORDER by date) AS prior
FROM fruit_table; 

但我找不到任何看起来正确的东西。 FWIW,我使用的是 PostgreSQL 8.4。

【问题讨论】:

“日期”是否保证是连续的? (顺便说一句,“date”是保留字。“prior”在某些SQL实现中也是保留字) 【参考方案1】:

您可以在没有窗口函数的情况下使用 self-left joincount() 来优雅地解决这个问题:

SELECT t.id, t.fruit, t.day, count(t0.*) AS prior
FROM   tbl t
LEFT   JOIN tbl t0 ON (t0.id, t0.fruit) = (t.id, t.fruit) AND t0.day < t.day
GROUP  BY t.id, t.day, t.fruit
ORDER  BY t.id, t.day

我将日期列重命名为 day,因为 date 是 reserved word in every SQL standard and in PostgreSQL。

我更正了您的示例数据中的一个错误。他们按照你的方式拥有它,它没有检查出来。可能会使人感到困惑。


如果您的目的是使用 窗口函数,这个应该可以工作:

SELECT id, fruit, day
      ,count(*) OVER (PARTITION BY id, fruit ORDER BY day) - 1 AS prior
FROM   tbl
ORDER  BY id, day
这行得通,因为,我quote the manual:

如果省略 frame_end,则默认为当前行。

您可以有效地计算前几天有多少行具有相同的(id, fruit) - 包括当前行。这就是 - 1 的用途。

【讨论】:

以上是关于在 SQL 中使用窗口函数运行“匹配”总数的主要内容,如果未能解决你的问题,请参考以下文章

使用窗口函数获取总数

SQL窗口函数和运行总计

sql中的 开窗函数over() 聚合函数 排名函数

C#中,findWindow函数怎么不能找到??

如何计算具有窗口函数的过滤器的总数?

SQL Server 2012 Windowing 函数计算运行总计