0

我有一个表A:

Col1 Col2
12    a
12    c
12    b

如果我编码:Select * from A; 结果是:

Col1 Col2
12    a
12    b
12    d

我要获取的数据是:

Col1 Col2
12    a
12    c
12    b

如何获取数据不排序?

在此处输入图像描述

4

3 回答 3

3

要理解的关键是 SQL 表没有排序。当您SELECT没有ORDER BYonly 时,您看到的行的顺序保持不变,因为数据库按该顺序获取它们比其他顺序更快。当您对表进行顺序扫描时,PostgreSQL 只会按此顺序返回行;如果它可以使用索引进行查询,那么您通常会以其他顺序获取行。

您可能会发现我之前写的这个答案很有用

在 PostgreSQL 中,UPDATEs to rows 可以将它们移动到表中的不同位置,从而更改它们返回的顺序。后台 autovacuum 进程和各种其他操作(如VACUUM和)也可以CLUSTER

所以你绝不能依赖任何东西的“默认”排序。如果你想给行某种顺序,它们必须有一个你可以对它们进行排序的键。

如果您创建了一个没有键的表,但现在意识到它应该有一个键,那么您可以使用ctid系统列从这种情况中恢复过来。不要依赖此用于生产用途,它是系统内部的列,用户只能看到它用于紧急恢复和诊断目的。首先,查看物理磁盘排序是否实际上是您想要的顺序:

SELECT row_number() OVER () AS mytable_id, *
FROM mytable
ORDER BY ctid;

如果是,您可以添加一个新的键列,该列预先设置为以磁盘行顺序应用的自动递增键。有两种方法可以做到这一点。最安全的是:

BEGIN;
LOCK TABLE mytable IN ACCESS EXCLUSIVE MODE;
ALTER TABLE mytable RENAME TO mytable_old;

CREATE TABLE mytable (id SERIAL PRIMARY KEY, LIKE mytable_old INCLUDING ALL);

INSERT INTO mytable
SELECT row_number() OVER () AS id, *
FROM mytable_old
ORDER BY ctid;

SELECT setval('mytable_id_seq', (SELECT max(id)+1 FROM mytable));

COMMIT;

那么一旦你确定你对结果感到满意,DROP TABLE mytable_old;. 看这个演示:http ://sqlfiddle.com/#!12/2cb99/2

一种快速简单但不太安全的方法是只创建列并依赖 PostgreSQL 从头到尾重写表:

ALTER TABLE mytable ADD COLUMN mytable_id SERIAL PRIMARY KEY;

绝对不能保证PostgreSQL 会按顺序分配 ID,尽管实际上它会这样做。请参阅此 SQLFiddle 演示

请注意,当您使用 a SEQUENCE(这是SERIAL列创建的内容)时,您可能不会想到一些行为。当您一次插入多行时,这些行可能不一定按照您期望的确切顺序获得分配的 ID,并且它们可能以与分配 ID 和插入的顺序不同的顺序“出现”(变得可见)此外,如果事务回滚,则生成的 ID 将被永远丢弃,因此您会在编号中出现空白。如果您希望数据库快速,这非常好,但如果您想要无间隙编号,这并不理想。如果这是您需要的,请搜索“postgresql gapless sequence”。

于 2013-07-20T12:30:55.950 回答
2

为了补充 Craig Ringer 给出的非常全面的答案,您可能需要考虑重新构建问题:为什么要以特定顺序显示行?显然,该顺序具有某些特殊含义,并且“它们恰好按该顺序插入”是让数据库的技术性主导应用程序,而不是相反。

例如,这些行可能代表某种事件,而您希望按照事件发生的顺序检索它们。在这种情况下,适当的排序列将是时间戳;就像一个自动递增序列,你可以给它一个插入行的默认值(但可以通过显式插入列来覆盖它,或者稍后更新值,如有必要):

ALTER TABLE some_table ADD COLUMN event_date TIMESTAMP NOT NULL DEFAULT ( NOW() );

或者,也许它实际上基于将在 UI 上显示的显示标签,在这种情况下,您只需要ORDER BY该显示标签,并确保数据库使用正确的collation.

最后,也许它是一个完全任意的显示顺序,可以对其进行调整以将常见或重要的项目带到菜单的顶部。在这种情况下,显示顺序本身就是被建模项目的属性,因此表中应该有自己的列。由于order是 SQL 关键字,所以我经常调用这样的列order_hint,这样我可以说ORDER BY order_hint不用担心专门引用列名。

于 2013-07-20T13:21:01.653 回答
1

a_horse_with_no_name 在评论中给出了正确答案。这是为了解决如何添加自动增量列。

创建表时,请使用以下内容:

create table A (
    Aid int not null auto_increment primary key,
    col1 int,
    col2 varchar(255)
)

当您插入其中时,显式列出列:

insert into A(col1, col2)
    select 12, 'A';

现在您拥有所需格式的数据,列Aid提供“插入顺序”。然后你可以这样做:

select col1, col2
from A
order by id;

这将按“插入顺序”返回数据。请注意,由于更新和删除,数据在页面上的实际排列顺序可能与输入顺序不同。但是order by,明确地重新排序数据。

能够看到插入顺序是我总是在所有表中使用自动递增主键的原因之一。

于 2013-07-20T10:53:26.307 回答