6

我正在尝试在 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)

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

4

1 回答 1

10

咒骂不会改变定义这种行为的 SQL 标准。
除非在 中指定,否则行的顺序是未定义的ORDER BY根据文档

如果未选择排序,则将按未指定的顺序返回行。这种情况下的实际顺序将取决于扫描和连接计划类型以及磁盘上的顺序,但不能依赖它。只有明确选择了排序步骤,才能保证特定的输出排序。

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

 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存在,因此m没有匹配的行对于所有当前表达式vm都具有空值。ORDER BY它们排在最后,否则会被任意排序。如果你想要一个整体上稳定的排序顺序,你也需要处理这个问题。喜欢:

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

旁白

为什么要存储双引号?似乎是代价高昂的噪音。没有他们你可能会过得更好。如果需要,您可以随时在输出中添加引号。

许多客户端无法在一个结果中多次处理相同的列名。您至少需要一个列别名idSELECT m.id AS m_id, vm.id AS vm_id .... 将说明为什么列的“id”是一种反模式。

于 2013-03-01T16:59:34.813 回答