【问题标题】:"ORDER BY ... USING" clause in PostgreSQLPostgreSQL 中的“ORDER BY ... USING”子句
【发布时间】:2020-02-15 06:57:58
【问题描述】:

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

ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...]

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

【问题讨论】:

  • “交替顺序”是什么意思?

标签: sql postgresql sql-order-by


【解决方案1】:

一个非常简单的例子是:

> 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);

您会注意到,&lt;&gt; 函数主要用于基本类型,例如 integerdate 等,还有更多用于数组和向量等。这些运算符都不会帮助您获得自定义排序。

大多数需要自定义排序的情况下,您可以使用... ORDER BY somefunc(tablecolumn) ... 之类的东西,其中somefunc 适当地映射值。因为这适用于每个数据库,这也是最常见的方式。对于简单的事情,您甚至可以编写表达式而不是自定义函数。

换档

ORDER BY ... USING 在几种情况下有意义:

  • 这种排序非常罕见,以至于 somefunc 技巧不起作用。
  • 您使用的是非原始类型(如 pointcircle 或虚数),并且您不想在查询中重复进行奇怪的计算。
  • 您要排序的数据集太大,需要甚至需要索引支持。

我将专注于复杂的数据类型:通常有不止一种方法可以以合理的方式对它们进行排序。一个很好的例子是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 的简单运算符是“下方”和“上方”运算符 &lt;^&gt;^。他们只是比较了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 测试的运算符。

现在&lt;^&gt;^ 运算符可以在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. 找到另一个示例,其中包含示例herehere 的源代码。此示例还展示了如何创建运算符。

【讨论】:

  • 我正在讨论实现一个运算符类和一个比较器函数来对模型版本进行排序(“1.4.0”
  • 这是我过去 2 天一直在寻找的。使用此功能,可以比较包含不同类型值的 jsonb 单元格。它消除了这种情况下对动态 SQL 语句的需求。
【解决方案2】:

样品:

CREATE TABLE test
(
  id serial NOT NULL,
  "number" integer,
  CONSTRAINT test_pkey PRIMARY KEY (id)
)

insert into test("number") values (1),(2),(3),(0),(-1);

select * from test order by number USING > //gives 3=>2=>1=>0=>-1

select * from test order by number USING < //gives -1=>0=>1=>2=>3

所以,它相当于descasc。但是你可以使用自己的操作符,这是USING的本质特征

【讨论】:

  • 你能给我一个使用自定义运算符的例子吗?
  • 我也很好奇。这听起来像是 Postgres 的一个非常棒的功能,
  • 好吧,简单的Create function op_func ... => Create operator === (procedure = op_func => order by === 把我扔了ERROR: operator === is not a valid ordering operator LINE 1: select * from test order by number USING === ^ HINT: Ordering operators must be "&lt;" or "&gt;" members of btree operator families.。我对运算符类和族不太熟悉,所以还不能举出例子。我会调查它,但实际上我不是 PostgreSQL 大师......
  • 嗯,在 google 上找到东西并不容易。这看起来像是一个相当内部的功能......
【解决方案3】:

很好的答案,但他们没有提到一个真正有价值的“使用”案例。

当您使用非默认运算符系列创建索引时,例如 varchar_pattern_ops~&gt;~~&lt;~~&gt;=~、...)而不是 &lt;&gt;&gt;=那么如果您基于索引进行搜索,并且您想按子句顺序使用索引,则需要使用适当的运算符指定USING

这可以用这样的例子来说明:

CREATE INDEX index_words_word ON words(word text_pattern_ops); 

让我们比较一下这两个查询:

SELECT * FROM words WHERE word LIKE 'o%' LIMIT 10;

SELECT * FROM words WHERE word LIKE 'o%' ORDER BY word LIMIT 10;

在 500K 字的数据库中,它们的执行次数相差近 100 倍!在非 C 语言环境中,结果也可能不正确。

这怎么会发生?

当您使用 LIKEORDER BY 子句进行搜索时,您实际上是在进行此调用:

SELECT * FROM words WHERE word ~>=~ 'o' AND word ~<~'p' ORDER BY word USING < LIMIT 10;

您的索引是使用~&lt;~ 运算符创建的,因此PG 不能在给定的ORDER BY 子句中使用给定的索引。为了把事情做好,必须将查询重写为这种形式:

SELECT * FROM words WHERE word ~>=~ 'o' AND word ~<~'p' ORDER BY word USING ~<~ LIMIT 10;

SELECT * FROM words WHERE word LIKE 'o%' ORDER BY word USING ~<~ LIMIT 10;

【讨论】:

    【解决方案4】:

    可以选择添加关键字 ASC(升序)或 DESC (降序)在 ORDER BY 子句中的任何表达式之后。如果不 指定时,默认假定为 ASC。或者,一个特定的 排序运算符名称可以在 USING 子句中指定。一个 排序运算符必须是一些小于或大于的成员 B-tree 算子家族。 ASC 通常等同于 USING .

    PostgreSQL 9.0

    我认为它可能看起来像这样(我现在没有 postgres 来验证这一点,但稍后会验证)

    SELECT Name FROM Person
    ORDER BY NameId USING >
    

    【讨论】:

    • 你漏掉的那一行也很有趣:(But the creator of a user-defined data type can define exactly what the default sort ordering is, and it might correspond to operators with other names.)
    • 我认为 OP 已经知道这一点;他们要求使用示例
    • 我已经看过了,但是除了“使用”,你能举出其他例子吗?
    • select * from test order by number USING &gt; 1 在 PostgreSQL 9.0 中不工作。但是select * from test order by number USING &gt; 有效。所以它应该是一个运算符,而不是比较。
    猜你喜欢
    • 2011-10-12
    • 2019-05-31
    • 1970-01-01
    • 1970-01-01
    • 2017-12-07
    • 2011-09-07
    • 2018-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多