一个非常简单的例子是:
> SELECT * FROM tab ORDER BY col USING <
但这很无聊,因为这是传统的ORDER BY col ASC 无法获得的。
此外,标准目录也没有提到奇怪的比较函数/运算符的任何令人兴奋的事情。您可以获取它们的列表:
> SELECT amoplefttype::regtype, amoprighttype::regtype, amopopr::regoper
FROM pg_am JOIN pg_amop ON pg_am.oid = pg_amop.amopmethod
WHERE amname = 'btree' AND amopstrategy IN (1,5);
您会注意到,< 和 > 函数主要用于基本类型,例如 integer、date 等,还有更多用于数组和向量等。这些运算符都不会帮助您获得自定义排序。
在大多数需要自定义排序的情况下,您可以使用... ORDER BY somefunc(tablecolumn) ... 之类的东西,其中somefunc 适当地映射值。因为这适用于每个数据库,这也是最常见的方式。对于简单的事情,您甚至可以编写表达式而不是自定义函数。
换档
ORDER BY ... USING 在几种情况下有意义:
- 这种排序非常罕见,以至于
somefunc 技巧不起作用。
- 您使用的是非原始类型(如
point、circle 或虚数),并且您不想在查询中重复进行奇怪的计算。
- 您要排序的数据集太大,需要甚至需要索引支持。
我将专注于复杂的数据类型:通常有不止一种方法可以以合理的方式对它们进行排序。一个很好的例子是point:您可以按到 (0,0) 的距离或先按 x,然后按 y 或仅按 y 或任何你想要的。
当然,PostgreSQL 有为point 预定义的运算符:
> CREATE TABLE p ( p point );
> SELECT p <-> point(0,0) FROM p;
但 没有 默认声明可用于ORDER BY(见上文):
> SELECT * FROM p ORDER BY p;
ERROR: could not identify an ordering operator for type point
TIP: Use an explicit ordering operator or modify the query.
point 的简单运算符是“下方”和“上方”运算符 <^ 和 >^。他们只是比较了y 部分。但是:
> SELECT * FROM p ORDER BY p USING >^;
ERROR: operator > is not a valid ordering operator
TIP: Ordering operators must be "<" or ">" members of __btree__ operator families.
ORDER BY USING 需要一个具有定义语义的运算符:显然它必须是一个二元运算符,它必须接受与参数相同的类型,并且必须返回布尔值。我认为它也必须是传递的(如果 a btree-index 排序也是必要的。这解释了包含对 btree 的引用的奇怪错误消息。
ORDER BY USING 不仅需要定义一个操作符,还需要定义一个操作符类和一个操作符族。虽然可以只用一个运算符来实现排序,但 PostgreSQL 试图有效地排序并最小化比较。因此,即使您只指定一个运算符,也会使用多个运算符 - 其他运算符必须遵守某些数学约束 - 我已经提到过传递性,但还有更多。
换档
让我们定义一些合适的东西:仅比较 y 部分的点运算符。
第一步是创建一个自定义的操作符族,可以被btree索引访问方法使用。 see
> CREATE OPERATOR FAMILY xyzfam USING btree; -- superuser access required!
CREATE OPERATOR FAMILY
接下来我们必须提供一个比较器函数,它在比较两个点时返回-1、0、+1。此函数将在内部调用!
> CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int
AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
CREATE FUNCTION
接下来我们为族定义操作符类。 See the manual 对数字的解释。
> CREATE OPERATOR CLASS xyz_ops FOR TYPE point USING btree FAMILY xyzfam AS
OPERATOR 1 <^ ,
OPERATOR 3 ?- ,
OPERATOR 5 >^ ,
FUNCTION 1 xyz_v_cmp(point, point) ;
CREATE OPERATOR CLASS
这一步结合了几个运算符和函数,还定义了它们的关系和含义。例如OPERATOR 1 表示:这是less-than 测试的运算符。
现在<^ 和>^ 运算符可以在ORDER BY USING 中使用:
> INSERT INTO p SELECT point(floor(random()*100), floor(random()*100)) FROM generate_series(1, 5);
INSERT 0 5
> SELECT * FROM p ORDER BY p USING >^;
p
---------
(17,8)
(74,57)
(59,65)
(0,87)
(58,91)
瞧——按y排序。
总结一下: ORDER BY ... USING 是 PostgreSQL 引擎盖下的一个有趣的外观。但是,除非您在非常特定的数据库技术领域工作,否则您很快就不需要任何东西。
可以在in the Postgres docs. 找到另一个示例,其中包含示例here 和here 的源代码。此示例还展示了如何创建运算符。