PostgreSQL中的"ORDER BY ... USING"子句

mar*_*kus 38 sql postgresql sql-order-by

ORDER BY子句在PostgreSQL文档中描述为:

ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...]
Run Code Online (Sandbox Code Playgroud)

有人可以给我一些例子如何使用USING operator?是否可以获得结果集的交替顺序?

A.H*_*.H. 43

一个非常简单的例子是:

> SELECT * FROM tab ORDER BY col USING <
Run Code Online (Sandbox Code Playgroud)

但这很无聊,因为这是传统中无法得到的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);
Run Code Online (Sandbox Code Playgroud)

您会注意到,对于诸如等等的原始类型,主要有函数<>函数,对于数组和向量等等更多.这些运营商都不会帮助您获得自定义订购.integerdate

大多数地方,需要自定义排序,你可以摆脱使用类似的情况... 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;
Run Code Online (Sandbox Code Playgroud)

但是默认情况下它们都没有被宣布可用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.
Run Code Online (Sandbox Code Playgroud)

简单的运算符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.
Run Code Online (Sandbox Code Playgroud)

ORDER BY USING需要一个具有已定义语义的运算符:显然它必须是二元运算符,它必须接受与参数相同的类型,并且必须返回boolean.我认为它也必须是传递性的(如果<b和b <c则a <c).可能有更多要求.但是所有这些要求对于正确的btree -index排序也是必要的.这解释了包含对btree的引用的奇怪错误消息.

ORDER BY USING还不仅需要定义一个运算符,还需要运算符类运算符族.虽然可以只使用一个运算符实现排序,但PostgreSQL会尝试有效排序并最小化比较.因此,即使您只指定了一个操作符,也会使用多个操作符 - 其他操作符必须遵守某些数学约束 - 我已经提到过传递性,但还有更多操作符.

切换齿轮

让我们定义一些合适的东西:一个只比较y零件的点的算子.

第一步是创建一个可由btree索引访问方法使用的自定义运算符族.看到

    > CREATE OPERATOR FAMILY xyzfam USING btree;   -- superuser access required!
    CREATE OPERATOR FAMILY
Run Code Online (Sandbox Code Playgroud)

接下来我们必须提供一个比较器函数,在比较两个点时返回-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
Run Code Online (Sandbox Code Playgroud)

接下来,我们定义该族的运算符类.有关数字的说明,请参阅手册.

    > 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
Run Code Online (Sandbox Code Playgroud)

此步骤结合了多个运算符和函数,并定义了它们之间的关系和含义.例如,OPERATOR 1意味着:这是less-than测试的运算符.

现在运算符<^和'> ^'可用于>^:

> 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)
Run Code Online (Sandbox Code Playgroud)

Voila - 按y排序.

总结一下: ORDER BY USING在PostgreSQL的引擎盖下是一个有趣的外观.但除非您在数据库技术的非常特定的领域工作,否则您不会很快就会需要.

另一个例子可以在Postgres文档中找到.这里这里使用示例的源代码.此示例还说明了如何创建运算符.