为 DISTINCT 调用为 PostgreSQL 类型(点)创建自定义 "equality operator"
Creating custom "equality operator" for PostgreSQL type (point) for DISTINCT calls
在我的一个 table 中,我有一个定义为 PostgreSQL 类型 point
. I use this for the earthdistance
模块的列——具体来说,<@>
距离运算符。 (是的,我知道 PostGIS,但它比我的需要复杂得多,它只是给出一个 table 和 lat/long 对,按距离排序 table提供 lat/long.)
但是,point
似乎没有实现相等性,因此对 table 的任何 DISTINCT
调用(如 SELECT DISTINCT * FROM mytable
)都会导致以下错误:
ERROR: could not identify an equality operator for type point
虽然通常不建议修补内置类型,但在这种情况下我不介意这样做,我尝试为 point
创建自己的 =
运算符:
CREATE OR REPLACE FUNCTION compare_points_equality(point1 POINT, point2 POINT)
RETURNS BOOLEAN AS $$
SELECT point1[0] = point2[0] AND point1[1] = point1[1];
$$ LANGUAGE SQL IMMUTABLE;
CREATE OPERATOR = (
LEFTARG = POINT,
RIGHTARG = POINT,
PROCEDURE = compare_points_equality,
COMMUTATOR = =,
NEGATOR = !=,
HASHES,
MERGES
);
但是即使在创建这个之后,我也会得到同样的错误。如果不创建 =
,我应该如何创建 "equality operator"?
到select 不同的值 Postgres 必须能够对列进行排序。
您需要创建一个完整的 btree operator class for type point, i.e. five operators (<
, <=
, =
, >=
, >
) and a function comparing two points and returning integer, as it is described in the documentation.
对于运算符 =
您可以使用现有函数 point_eq(point, point)
:
create operator = (leftarg = point, rightarg = point, procedure = point_eq, commutator = =);
运算符的示例定义 <
:
create function point_lt(point, point)
returns boolean language sql immutable as $$
select [0] < [0] or [0] = [0] and [1] < [1]
$$;
create operator < (leftarg = point, rightarg = point, procedure = point_lt, commutator = >);
以类似的方式定义运算符 <=
、=>
和 >
。拥有所有五个运算符,创建一个函数:
create function btpointcmp(point, point)
returns integer language sql immutable as $$
select case
when = then 0
when < then -1
else 1
end
$$;
最后:
create operator class point_ops
default for type point using btree as
operator 1 <,
operator 2 <=,
operator 3 =,
operator 4 >=,
operator 5 >,
function 1 btpointcmp(point, point);
使用 class point_ops
定义,您可以 select 不同的点值并按点类型的列对行进行排序,例如:
with q(p) as (
values
('(1,1)'::point),
('(1,2)'::point),
('(2,1)'::point),
('(1,1)'::point))
select distinct *
from q
order by 1 desc;
p
-------
(2,1)
(1,2)
(1,1)
(3 rows)
您还可以在点列上创建(唯一)索引。
更新。
Where the function point_eq(point, point)
comes from? Why does it already exist?
Postgres 有 2800 多个辅助函数,支持运算符、索引、标准函数等。您可以通过查询 pg_proc
, 列出它们,例如:
select format('%s(%s)', proname, pg_get_function_arguments(oid))
from pg_proc
where pronamespace::regnamespace = 'pg_catalog'
and proname like 'point%'
函数point_eq(point, point)
用于实现一些geometric functions and operators.
在我的一个 table 中,我有一个定义为 PostgreSQL 类型 point
. I use this for the earthdistance
模块的列——具体来说,<@>
距离运算符。 (是的,我知道 PostGIS,但它比我的需要复杂得多,它只是给出一个 table 和 lat/long 对,按距离排序 table提供 lat/long.)
但是,point
似乎没有实现相等性,因此对 table 的任何 DISTINCT
调用(如 SELECT DISTINCT * FROM mytable
)都会导致以下错误:
ERROR: could not identify an equality operator for type point
虽然通常不建议修补内置类型,但在这种情况下我不介意这样做,我尝试为 point
创建自己的 =
运算符:
CREATE OR REPLACE FUNCTION compare_points_equality(point1 POINT, point2 POINT)
RETURNS BOOLEAN AS $$
SELECT point1[0] = point2[0] AND point1[1] = point1[1];
$$ LANGUAGE SQL IMMUTABLE;
CREATE OPERATOR = (
LEFTARG = POINT,
RIGHTARG = POINT,
PROCEDURE = compare_points_equality,
COMMUTATOR = =,
NEGATOR = !=,
HASHES,
MERGES
);
但是即使在创建这个之后,我也会得到同样的错误。如果不创建 =
,我应该如何创建 "equality operator"?
到select 不同的值 Postgres 必须能够对列进行排序。
您需要创建一个完整的 btree operator class for type point, i.e. five operators (<
, <=
, =
, >=
, >
) and a function comparing two points and returning integer, as it is described in the documentation.
对于运算符 =
您可以使用现有函数 point_eq(point, point)
:
create operator = (leftarg = point, rightarg = point, procedure = point_eq, commutator = =);
运算符的示例定义 <
:
create function point_lt(point, point)
returns boolean language sql immutable as $$
select [0] < [0] or [0] = [0] and [1] < [1]
$$;
create operator < (leftarg = point, rightarg = point, procedure = point_lt, commutator = >);
以类似的方式定义运算符 <=
、=>
和 >
。拥有所有五个运算符,创建一个函数:
create function btpointcmp(point, point)
returns integer language sql immutable as $$
select case
when = then 0
when < then -1
else 1
end
$$;
最后:
create operator class point_ops
default for type point using btree as
operator 1 <,
operator 2 <=,
operator 3 =,
operator 4 >=,
operator 5 >,
function 1 btpointcmp(point, point);
使用 class point_ops
定义,您可以 select 不同的点值并按点类型的列对行进行排序,例如:
with q(p) as (
values
('(1,1)'::point),
('(1,2)'::point),
('(2,1)'::point),
('(1,1)'::point))
select distinct *
from q
order by 1 desc;
p
-------
(2,1)
(1,2)
(1,1)
(3 rows)
您还可以在点列上创建(唯一)索引。
更新。
Where the function
point_eq(point, point)
comes from? Why does it already exist?
Postgres 有 2800 多个辅助函数,支持运算符、索引、标准函数等。您可以通过查询 pg_proc
, 列出它们,例如:
select format('%s(%s)', proname, pg_get_function_arguments(oid))
from pg_proc
where pronamespace::regnamespace = 'pg_catalog'
and proname like 'point%'
函数point_eq(point, point)
用于实现一些geometric functions and operators.