我正在尝试在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)
但是什么地狱??? !!! 为什么我必须使用解决方法?
答案 0 :(得分:9)
咒骂不会改变定义此行为的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"
在这里使用无关,顺便说一下。)
为了这个目的,不要连接,这要贵得多,并且可能无法使用索引(除非您对该精确表达式有索引)。当ORDER BY
列表中的先前表达式出现歧义时,添加另一个表达式。
此外,由于您有{strong> LEFT JOIN
,m
中不匹配的vm
中的行对所有当前ORDER BY
表达式都具有空值。它们是最后的,并且是任意排序的。如果你想要一个稳定的排序顺序,你也需要处理它。像:
ORDER BY lower(trim(vm.value)) COLLATE "C", vm.id, m.id;
为什么要存储双引号?似乎是昂贵的噪音。没有它们你可能会更好。如果需要,您可以随时在输出中添加引号。
许多客户端无法在一个结果中多次处理相同的列名称。您需要至少一个id
列的列别名:SELECT m.id AS m_id, vm.id AS vm_id ...
。去表明为什么列的“id”是一个反模式开始。