加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

PostgreSQL:ORDER BY和LIMIT / OFFSET的奇怪碰撞

发布时间:2020-12-13 16:04:52 所属栏目:百科 来源:网络整理
导读:我试图在PostgreSQL 9.1中这样做: SELECT m.id,vm.id,vm.valueFROM mLEFT JOIN vm ON vm.m_id = m.id and vm.variation_id = 1ORDER BY lower(trim(vm.value)) COLLATE "C" ASC LIMIT 10 OFFSET 120 结果是: id | id | value----+-----+---------------504
我试图在PostgreSQL 9.1中这样做:

SELECT m.id,vm.id,vm.value
FROM m
LEFT JOIN vm ON vm.m_id = m.id and vm.variation_id = 1
ORDER BY lower(trim(vm.value)) COLLATE "C" ASC LIMIT 10 OFFSET 120

结果是:

id |  id | value
----+-----+---------------
504 | 511 | "andr-223322"
506 | 513 | "andr-322223"
824 | 831 | "angHybrid"
866 | 873 | "Another thing"
493 | 500 | "App update required!"
837 | 844 | "App update required!"
471 | 478 | "April"
905 | 912 | "Are you sure you want to delete this thing?"
 25 |  29 | "Assignment"
196 | 201 | "AT ADDRESS"

好吧,让我们用OFFSET 130执行相同的查询:

id |  id | value
----+-----+---------------
196 | 201 | "AT ADDRESS"
256 | 261 | "Att Angle"
190 | 195 | "Att Angle"
273 | 278 | "Att Angle:"
830 | 837 | "attAngle"
475 | 482 | "August"
710 | 717 | "Averages"
411 | 416 | "AVG"
692 | 699 | "AVG SHAPE"
410 | 415 | "AVGs"

我们再次看到了我们的AT ADDRESS项目,但最开始!

事实是vm表包含以下两项:

id | m_id | value
----+------+---------------
201 |  196 | "AT ADDRESS"
599 |  592 | "At Address"

我通过解决方法解决了这种情况:

(lower(trim(vm.value)) || vm.id)

但是什么地狱??? !!!
为什么我必须使用解决方法?

解决方法

发誓不会改变定义此行为的SQL标准.
除非在ORDER BY中指定,否则行的顺序是未定义的. Per documentation:

If sorting is not chosen,the rows will be returned in an unspecified
order. The actual order in that case will depend on the scan and join
plan types and the order on disk,but it must not be relied on. A
particular output ordering can only be guaranteed if the sort step is explicitly chosen.

由于您没有为这两个对等方定义订单(按您的排序顺序):

id | m_id | value
----+------+---------------
201 |  196 | "AT ADDRESS"
599 |  592 | "At Address"

..你得到任意的订单 – 对Postgres来说很方便. LIMIT的查询通常使用不同的查询计划,可以解释不同的结果.

固定:

ORDER BY lower(trim(vm.value)) COLLATE "C",vm.id;

或者(可能更有意义 – 可能还调整到现有索引):

ORDER BY lower(trim(vm.value)) COLLATE "C",vm.value,vm.id;

(这与使用COLLATE“C”无关,顺便说一下.)
不要为此目的进行连接,这样会更昂贵并且可能使得无法使用索引(除非您对该精确表达式有索引).添加另一个表达式,当ORDER BY列表中的先前表达式出现歧义时,该表达式将启动.

此外,由于您在那里有LEFT JOIN,因此在mm中没有匹配的m中的行对于所有当前ORDER BY表达式都具有空值.它们是最后的,并且是任意排序的.如果你想要一个稳定的排序顺序,你也需要处理它.喜欢:

ORDER BY lower(trim(vm.value)) COLLATE "C",m.id;

旁白

为什么要存储双引号?似乎是昂贵的噪音.没有它们你可能会更好.如果需要,您始终可以在输出中添加引号.

许多客户端无法在一个结果中多次处理相同的列名称.您需要至少一个id列的列别名:SELECT m.id AS m_id,vm.id AS vm_id ….去显示为什么列的“id”是一个反模式开始.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读