【发布时间】:2016-07-07 06:17:52
【问题描述】:
在 Postgres 中描述 SELECT TOP ... 查询的大多数资源都说您应该改用 LIMIT,如果您需要按某种顺序选择顶部元素,则可能使用 ORDER BY 子句。
如果您需要从没有排序的递归查询中选择前 N 个元素,并且查询可能返回少于 N 行而不进行递归(因此 TOP 部分是否需要确保结果集至少 N 行,而LIMIT 可以允许更少的行)?
我的具体用例是对dynamic SQL pattern for selecting a random subsample of a table 的修改。
这是我修改的link to the sql source。最简单的方法是查看那里定义的最终函数_random_select。它非常接近上述链接,但已被修改为在输入表和输出结果集中是多态的,并且正确地考虑了只返回输入表中已经存在的列的需要(另一个动态 SQL破解以从最终结果集中排除临时 row_number 结果)。
这是一个令人眼花缭乱的东西,但它是我所拥有的最接近可重现示例的东西。如果您使用_random_select 并尝试从大于 4500 行的表中获取大约 4500 行的内容,您开始看到较小的结果集的可能性很高,并且随着您增加所需样本的大小,它只会变得更糟(因为随着您所需的样本变大,重复出现的情况会变得更糟)。
请注意,在我的修改中,我没有使用此链接中的 _gaps 技巧,这意味着如果某个索引列中存在间隙,则过度采样以抵消采样效率低下的问题。那部分与这个问题无关,在我的例子中,我使用row_number 来确保有一个整数列没有可能的间隙。
CTE 是递归的,以确保如果 CTE 的第一个非递归部分没有给您足够的行(因为 UNION 删除了重复项),那么它将通过另一轮返回CTE 的递归调用,并继续处理更多结果,直到你得到足够的结果。
在链接的示例中,使用了LIMIT,但我发现这不起作用。该方法返回的结果较少,因为LIMIT 只是最多N 行 保证。
您如何获得至少 N 行的保证?选择TOP N 行似乎是执行此操作的自然方式(因此递归 CTE 必须继续运行,直到获得足够的行以满足TOP 条件),但这在 Postgres 中不可用。
【问题讨论】:
-
说,您的目标是前 5 个类别,但您的商店只有 3 个。额外的 2 个类别应该显示什么——“未来类别 1”和“未来类别 2”?
-
这是一个递归 CTE,所以这实际上是不可能的。它会生成随机行,并且可以在需要时继续这样做。查询在到达已经少于表中可用行数的样本之前就被缩短了。
-
TOP是 SQL Server、Sybase 和其他一些数据库使用的关键字。它的工作原理与 Postgres 中的LIMIT几乎相同。我不知道有任何数据库会具有您想要的行为(如果您从只能提供少于 N 行的数据流中请求 N 行,那么所需的行为是它应该“挂起”)。也许,你最好专注于你的实际问题,而不是你的解决方案。如果您想从表中选择一个随机子样本,请询问有关该问题的问题,而不是有关LIMIT的问题。TOP/LIMIT只限制结果集,从不扩展它。 -
接下来的问题是如何继续从递归 CTE 请求行,直到返回最小的行集。我正在处理的示例问题是随机抽样而不重复,但它可能是您需要来自无限数据流的 N 行的任何问题。什么时候可以提早返回的规则是什么?人们会认为没有明确停止标准的递归 CTE 将继续产生结果,直到
LIMIT停止它。但是,在加入递归调用之前,可能会返回中间结果。它是如何工作的? -
这个问题的一个更简单的版本可能是生成一个包含前 N 个斐波那契数列的列,方法是将第 i 步的计算与第 i+1 步的递归调用合并,然后允许
LIMIT成为递归终止的机制。 This link 讨论了它,即使与像 Haskell 列表这样的惰性流进行了相同的比较。但是通过LIMIT(或其他东西)将其扩展为使用更通用的“拉到足够”,这正是问题所在。
标签: sql postgresql common-table-expression recursive-query sql-limit