PostgreSQL:ORDER BY和LIMIT / OFFSET的奇怪碰撞
我试图在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:
由于您没有为这两个对等方定义订单(按您的排序顺序): 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”无关,顺便说一下.) 此外,由于您在那里有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”是一个反模式开始. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |