如何在 HAVING 子句中使用函数的输出?

How can I use a function's output in HAVING clause?

从 MySQL 5.5 转换到 PostgreSQL 9.4 时,我遇到了以下查询的问题:

SELECT *, GCDist(?, ?, lat, lon) AS dist 
FROM ads 
HAVING dist < radius 
ORDER BY date_created DESC 
LIMIT ?;

其中 GCDist 计算两点之间的大圆距离。

没有 HAVING 子句,查询在 Postgres 上工作正常,但如果我想过滤掉带有 dist > radius 的行,我会收到此错误:

ERROR: column "dist" does not exist
LINE 1: ...*, GCDist(0, 0, lat, lon) AS dist FROM ads HAVING dist < 100...

是否可以在 PostgresSQL 9.4 的查询的 HAVING 子句中使用函数的输出?如果是,怎么做?

提前感谢您的任何提示。


重现错误的方法如下:

CREATE FUNCTION GCDist (
        _lat1 FLOAT,  -- Scaled Degrees north for one point
        _lon1 FLOAT,  -- Scaled Degrees west for one point
        _lat2 FLOAT,  -- other point
        _lon2 FLOAT
    ) RETURNS FLOAT
    IMMUTABLE AS
$$
    -- Hardcoded constant:
    DECLARE
        _deg2km FLOAT DEFAULT 0.0111325;
        _deg2rad FLOAT DEFAULT PI()/1800000;  -- For scaled by 1e4 to MEDIUMINT
        _rlat1 FLOAT DEFAULT _deg2rad * _lat1;
        _rlat2 FLOAT DEFAULT _deg2rad * _lat2;
    -- compute as if earth's radius = 1.0
        _rlond FLOAT DEFAULT _deg2rad * (_lon1 - _lon2);
        _m     FLOAT DEFAULT COS(_rlat2);
        _x     FLOAT DEFAULT COS(_rlat1) - _m * COS(_rlond);
        _y     FLOAT DEFAULT               _m * SIN(_rlond);
        _z     FLOAT DEFAULT SIN(_rlat1) - SIN(_rlat2);
        _n     FLOAT DEFAULT SQRT(_x * _x + _y * _y + _z * _z);
    BEGIN
        RETURN _deg2km * 2 * ASIN(_n / 2) / _deg2rad;   -- again--scaled degrees
    END;
$$
LANGUAGE plpgsql;

CREATE TABLE test (id SERIAL PRIMARY KEY, lat INTEGER NOT NULL, lon INTEGER NOT NULL);

INSERT INTO test (id, lat, lon) VALUES (DEFAULT, 10000, 10000);
INSERT INTO test (id, lat, lon) VALUES (DEFAULT, 20000, 20000);
INSERT INTO test (id, lat, lon) VALUES (DEFAULT, 50000, 50000);

SELECT *, GCDist(0, 0, lat, lon) AS dist FROM test HAVING dist < 200;

MySQL 上的输出 table 类似于以下内容:

id | lat | lon | dist
---+-----+-----+------
1  |10000|10000|157.43

首先 having 是无用的(而且是错误的),因为您没有使用 group by,在这种情况下您应该使用 where 子句。

要访问 where 子句中的列别名,您需要将查询包装在派生的 table:

select *
from (
  SELECT *, 
         GCDist(0, 0, lat, lon) AS dist 
   FROM test 
) t 
where dist < 200;

或者只是重复表达

SELECT *, GCDist(0, 0, lat, lon) AS dist 
FROM test 
WHERE GCDist(0, 0, lat, lon) < 200;

另见此处:

  • Using an Alias column in the where clause in Postgresql
  • Logical Processing Order or SQL Standard in WHERE clause

Is it possible to use the output of a function in the HAVING clause of a query in PostgresSQL 9.4? If so, how?

是的,与 WHERE 子句中的相同。请参阅 a-horse-with-no-name 显示的两个解决方案中的第二个。