【问题标题】:How to re-use result for SELECT, WHERE and ORDER BY clauses?如何重用 SELECT、WHERE 和 ORDER BY 子句的结果?
【发布时间】:2012-12-14 00:23:12
【问题描述】:

以下查询返回我们附近的场地(纬度:62.0,经度:25.0),我们落入其半径内,按距离排序:

SELECT *, 
     earth_distance(ll_to_earth(62.0, 25.0), 
     ll_to_earth(lat, lon)) AS distance 
FROM venues 
WHERE earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) <= radius 
ORDER BY earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon))

是否可以(并且建议)重复使用来自earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) 的结果,而不是为 SELECT、WHERE 和 ORDER BY 子句单独计算它?

【问题讨论】:

  • 我想如果函数标记为immutable,结果会被重用。如果我错了,希望 Postgres 专家能纠正我。
  • @MikeChristensen:是的,它通常是这样工作的。即使STABLE 也足够了,因为它在单个语句中声明了结果常量。 IMMUTABLE 需要断言恒定的结果,即使是事务之间。例如,一个函数需要在索引中使用。

标签: sql postgresql select sql-order-by where


【解决方案1】:

虽然我主要使用 MS SQL Server,但我很确定 PostgreSQL 支持 CTE。尝试类似:

WITH CTE_venues AS (
SELECT *, earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) AS distance 
FROM venues 
)
SELECT *
FROM CTE_venues 
WHERE distance <= radius 
ORDER BY distance

【讨论】:

    【解决方案2】:

    您还可以创建独立或打包的函数并在查询中使用它:

     SELECT *
       FROM ...
      WHERE distance <= your_function()  -- OR your_package_name.your_function()
     ORDER BY ...
    

    你可以在 select 中使用你的函数:

    Select your_function() 
      From your_table...
     Where  ...
    

    【讨论】:

    • 完全没用的答案。 1) 计算出的距离还需要在select 子句中,即select * 不起作用。 2)结果集仍然必须按函数结果排序。 3) 这等同于查询与最初发布的内容完全没有区别。问题是关于函数的波动性,以及查询优化器是对函数求值 3 次还是 1 次。
    • 只是想帮忙...请再看一下我的示例-可以在选择中使用函数...仅供参考...问题是关于重用结果。函数返回结果,可以重复使用...
    【解决方案3】:

    GROUP BYORDER BY 子句中,您可以引用列别名(输出列)甚至是SELECT 列表项的序号。我引用the manual on ORDER BY

    每个表达式可以是输出列的名称或序号 (SELECT 列表项),或者它可以是由以下组成的任意表达式 输入列值。

    我的大胆强调。

    但在WHEREHAVING 子句中,您只能引用基表中的列(输入列),因此您必须拼出您的函数调用。

    SELECT *, earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) AS dist
    FROM   venues 
    WHERE  earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) <= radius 
    ORDER  BY distance;
    

    如果您想知道将计算打包到CTE 或子查询中是否更快,只需使用EXPLAIN ANALYZE 进行测试。 (我怀疑。)

    SELECT *
    FROM  (
       SELECT *
             ,earth_distance(ll_to_earth(62.0, 25.0), ll_to_earth(lat, lon)) AS dist
       FROM   venues
       ) x
    WHERE  distance <= radius 
    ORDER  BY distance;
    

    @Mike commented 一样,通过声明函数STABLE(或IMMUTABLE),您可以告知查询规划器,函数调用的结果可以多次重复用于单个语句中的相同调用。我引用the manual here

    一个 STABLE 函数不能修改数据库,并保证 在给定相同参数的情况下,为 a 中的所有行返回相同的结果 单一的声明。此类别允许优化器优化 多次调用函数到一次调用

    我的大胆强调。

    【讨论】:

    • 很好的答案。明确一点:您怀疑子查询会比 CTE 更快,或者怀疑使用 CTE/子查询是否值得仅计算两次?
    • @randomguy:我希望普通表单比使用子查询或 CTE 更快。但只是测试和看看。测试>>猜测。
    • @ErwinBrandstetter :根据我的经验,CTE 始终保持不变,即使仅被引用一次(IMO 应该等同于等效的 VIEW 或 FROM (subquery) 形式)。因此,计划生成器不会对它们进行拆卸+重新组装,这可能导致次优计划。从好的方面来说:这将使范围表相对较小(并且相当不相关),这将减少可能计划数量的组合爆炸。 (我还没试过9.2)
    • @wildplasser:如果你的观察成立,CTE 的有用性对于非常大和复杂的查询会增加,其中计划开销开始消耗性能。您将不会进一步优化的子查询放入 CTE 以降低主查询的复杂性。当我有时间时,我将不得不对此进行测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-18
    • 2021-12-04
    • 2012-04-15
    • 1970-01-01
    • 2016-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多