ORDER BY子句在PostgreSQL文档中描述为:
ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...]
有人可以举例说明如何使用USING operator
吗?是否可以获得结果集的交替顺序?
答案 0 :(得分:43)
一个非常简单的例子是:
> 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 拥有<{1}}的预定义运算符:
point
但默认情况下, none 声明可用于 > 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.
需要一个具有已定义语义的运算符:显然它必须是二元运算符,它必须接受与参数相同的类型,并且必须返回boolean。我认为它也必须是可传递的(如果&lt; b和b&lt; c然后a&lt; c)。可能有更多要求。但所有这些要求对于正确的 btree - 索引排序也是必要的。这解释了包含对 btree 的引用的奇怪错误消息。
ORDER BY USING
还不仅需要定义一个运算符,还需要运算符类和运算符族。虽然一个可以只用一个运算符实现排序,但PostgreSQL会尝试有效排序并最小化比较。因此,即使只指定了一个操作符,也会使用多个操作符 - 其他操作符必须遵守某些数学约束 - 我已经提到过传递性,但还有更多操作符。
切换Gears
让我们定义一些合适的东西:一个只比较ORDER BY USING
部分的点的运算符。
第一步是创建一个可由 btree 索引访问方法使用的自定义运算符族。 see
y
接下来我们必须提供一个比较器函数,在比较两个点时返回-1,0,+ 1。这个函数将在内部调用!
> CREATE OPERATOR FAMILY xyzfam USING btree; -- superuser access required!
CREATE OPERATOR FAMILY
接下来,我们定义该族的运算符类。 See the manual有关数字的解释。
> CREATE FUNCTION xyz_v_cmp(p1 point, p2 point) RETURNS int
AS $$BEGIN RETURN btfloat8cmp(p1[1],p2[1]); END $$ LANGUAGE plpgsql;
CREATE FUNCTION
此步骤结合了多个运算符和函数,并定义了它们之间的关系和含义。例如 > 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
Voila - 按 y 排序。
总结一下: > 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)
是PostgreSQL的一个有趣的外观。但是除非你在非常特定的数据库技术领域工作,否则你很快就会需要。
可以找到in the Postgres docs.的另一个示例,其中包含示例here和here的源代码。此示例还说明了如何创建运算符。
答案 1 :(得分:4)
样品:
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
因此,它相当于desc
和asc
。但您可以使用自己的运算符,这是USING
答案 2 :(得分:1)
很好的答案,但他们没有提到一个真正有价值的USING案例。
使用非默认运算符族创建索引时,例如varchar_pattern_ops(〜&gt;〜,〜&lt;〜,〜&gt; =〜...)而不是&lt;,&gt;,&gt; = ...然后,如果您基于索引进行搜索并且想要在order by子句中使用index,则需要使用适当的运算符指定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;
他们的执行差异接近100倍,500K字DB!并且在非C语言环境中结果可能也不正确。
这怎么可能发生?
使用LIKE和ORDER 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;
答案 3 :(得分:0)
可选择添加关键字ASC(升序)或DESC 在ORDER BY子句中的任何表达式之后(降序)。如果不 指定,默认情况下采用ASC。或者,具体的 可以在USING子句中指定排序运算符名称。一个 排序运算符必须小于或大于某些成员 B树操作员家庭。 ASC通常等同于USING&lt;和DESC 通常相当于USING&gt;。
我认为它可能看起来像这样(我现在没有postgres来验证这一点,但稍后会验证)
SELECT Name FROM Person
ORDER BY NameId USING >