PostgreSQL:first_value(unique_column)OVER()有啥问题?

Posted

技术标签:

【中文标题】PostgreSQL:first_value(unique_column)OVER()有啥问题?【英文标题】:PostgreSQL: what's wrong with first_value(unique_column) OVER ()?PostgreSQL:first_value(unique_column)OVER()有什么问题? 【发布时间】:2018-09-07 14:35:48 【问题描述】:

根据PostgreSQL: detecting the first/last rows of result set,我有理由怀疑这样的条款是危险的或不合适的,并希望更好地理解这一点。采取:

SELECT last_value(unique_column) OVER (), * FROM mytable;

unique_column 是唯一的且不为空。那么以这种方式使用OVER ()有什么问题呢?它危险/不可靠吗?次优?据我所知,这应该返回结果集中最后一行的值——至少,当我尝试过它时。有人告诉我“最后”没有排序就没有意义,但显然有最后一行被返回。我还被告知OVER () 的意思是“任何事情都会发生”,这表明结果是不可靠的,但到目前为止,每次我运行这种查询时,我一直从最后得到值结果集。

现在如果我使用ORDER BY,我已经发现了一个问题:

SELECT last_value(unique_column) OVER (), * FROM mytable ORDER BY something_else;

但是,我的解决方案是子查询:

SELECT last_value(unique_column) OVER (), * FROM (SELECT * FROM mytable ORDER BY something_else) sub;

好像OVER () 表示分析函数(如first_value()last_value())根据引擎发生读取表/子查询的顺序运行。而且,据我所知,您可以充分控制引擎读取表/子查询的顺序(无需进行不必要的排序)。

我在 Debian 9.5 环境中运行 PostgreSQL 9.6。

【问题讨论】:

你只是走运了。永远不能保证您的数据每次都以相同的顺序从磁盘中提取。这不仅仅是“从表或子查询中提取引擎”这实际上是从驱动器上的数据块中提取的 postgres 实例。如果您考虑整个数据库堆栈将长期保持一致,那么您就是在玩火。在 SQL 中明确表达也是一种很好的做法。你知道你想要的顺序,你的数据库不知道。告诉它。 如果您不关心订单,您也可以使用first_value() 或者同样,如果您不关心select myfield from mytable limit 1,我们实际上是在谈论仅获得一条记录时可以忽略不计的性能。 真棒@Opux 如果您不关心订单,那么您就是黄金。如果你真的想要结果集中的最后一项,那是 100% 完美的。我无法想象这样的场景是有意义的,但它在逻辑上是一致的,并且可以执行您希望它执行的操作:) 窗口函数在检索结果集后对其进行操作(这是处理的最后一步),所以这将随心所欲地工作。 @JNevill 正如我试图在链接中解释的那样,我需要那些分析函数来获取结果集中的第一条和最后一条记录来标识自己,所以我使用的表达式更接近last_value(unique_column) OVER () = unique_column last_row_in_result_set .这是我们过去在 C++ 中处理的事情(很简单:只需在检索行的循环的开头和结尾标记它),但我们正试图将此类功能从 C++ 移动到 SQL。因此,直到我明白为什么结果集中间的一行可以被标记为最后一行,我才会这样做。谢谢。 【参考方案1】:

您应该在OVER 子句中提供ORDER BY

SELECT *,
 last_value(unique_column) 
 OVER (ORDER BY sth_else ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM mytable

【讨论】:

排序很昂贵。不做会有什么后果? @Opux OVER() 没有 ORDER BY 对 ROW_NUMBER/RANK/LAST_VALUE/FIRST_VALUE 有点感觉 但是会发生什么?有没有可能last_value() 返回的值不是该结果集中的最后一个值?什么可能导致这种情况?比赛条件?锁定不会阻止这种事情在查询中发生变化吗? @Opux 让我换一种说法。如果没有明确的 ORDER BY 顺序,则永远无法保证。如果您不关心订单,请获取任何价值。您应该使用并行执行并检查执行之间是否获得一致的结果。【参考方案2】:

我应该指出,在过去的几个月里,这个解决方案运行得相当好,而且我还没有看到替代方案,所以我将继续使用它。但是,我应该指出,如果您进行某些更改并且不考虑分析,它会很挑剔并且可能会失败。 (毫无疑问,我在滥用该功能,它不是为此目的而开发的)。所以我会利用这个空间来记录我发现的问题。

    如果您订购结果,就会遇到问题,但我已经在问题中解释了这一点。 我尝试在外部连接中使用它。由于这导致结果集中的字段为空(即使它们取自不能为空的表中的字段),这导致OVER() 返回 NULL。我有一些关于如何解决这个问题的想法,但它们会使查询非常变得丑陋并且可能非常低效。

【讨论】:

以上是关于PostgreSQL:first_value(unique_column)OVER()有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

postgresql里有没有像oracle中的那类分析函数

错误:关系列不存在 PostgreSQL,无法运行插入查询

BigQuery、FIRST_VALUE 和 null

(REDSHIFT) 垂直合并 / FIRST_VALUE() 作为聚合

OVER 函数和 first_value

Oracle分析函数-first_value()和last_value()