【发布时间】:2014-05-14 23:51:43
【问题描述】:
我有一个具有以下模式的 DB2 表:
CREATE TABLE "CONTACTS" (
"ID" CHAR(36) NOT NULL,
"DELETED" SMALLINT DEFAULT 0,
"FIRST_NAME" VARCHAR(200),
"LAST_NAME" VARCHAR(200)
);
CREATE INDEX "IDX_CONTACTS_DEL_LAST" ON "CONTACTS"
("DELETED" ASC,
"LAST_NAME" ASC)
MINPCTUSED 0 ALLOW REVERSE SCANS PAGE SPLIT SYMMETRIC COMPRESS YES;
CREATE INDEX "IDX_CONTACT_LASTNAME" ON "CONTACTS"
("LAST_NAME" ASC,
"DELETED" ASC)
MINPCTUSED 0 ALLOW REVERSE SCANS PAGE SPLIT SYMMETRIC COMPRESS YES;
CREATE INDEX "IDX_CONT_LAST_FIRST" ON "CONTACTS"
("LAST_NAME" ASC,
"FIRST_NAME" ASC,
"DELETED" ASC)
MINPCTUSED 0 ALLOW REVERSE SCANS PAGE SPLIT SYMMETRIC COMPRESS YES;
CREATE INDEX "IDX_ID_DEL" ON "CONTACTS"
("ID" ASC,
"DELETED" ASC)
MINPCTUSED 0 ALLOW REVERSE SCANS PAGE SPLIT SYMMETRIC COMPRESS YES;
CREATE UNIQUE INDEX "CONTACTSPK" ON "CONTACTS"
("ID" ASC)
MINPCTUSED 0 ALLOW REVERSE SCANS PAGE SPLIT SYMMETRIC COMPRESS YES;
ALTER TABLE "CONTACTS" ADD CONSTRAINT "CONTACTSPK" PRIMARY KEY ("ID");
此查询工作正常(快速):
SELECT * FROM (SELECT contacts.id, contacts.first_name, contacts.last_name
FROM contacts WHERE contacts.deleted=0
ORDER BY contacts.last_name ASC)
LIMIT 21 OPTIMIZE FOR 21 ROWS
然而,这在相当大(数百万行)的数据库上几乎慢了 1000 倍:
SELECT * FROM (SELECT contacts.id, contacts.first_name, contacts.last_name
FROM contacts WHERE contacts.deleted=0
ORDER BY contacts.last_name ASC, contacts.id ASC)
LIMIT 21 OPTIMIZE FOR 21 ROWS
现在,我假设一旦 last_name 被索引并具有足够的基数(确实如此),添加二次排序应该无关紧要。然而,事实证明这很重要——它使查询慢了一千倍。我的问题是为什么-DB2 不应该只从last_name/deleted 索引中获取前21 行,这应该非常快,按ID 对它们进行排序然后完成它?然而,它看起来会进行全表扫描,或者至少是一些非常昂贵的东西。所以我的问题是为什么?
第二个问题是是否有办法添加二级排序字段而不会造成这种影响。原因是字段将被添加到contacts 他们会有自己的索引,但是将id 添加到每个索引看起来很浪费。 OTOH,某些字段可以有很多具有相同last_name 或其他值的记录,因此为这些行设置稳定的顺序会很有用,尤其是在分页时。 DB2 是否保证这样的顺序没有二次排序?
【问题讨论】:
-
也许您可以先查看
EXPLAIN的两个查询的输出? -
@IanBjorhovde 不幸的是,虽然 EXPLAIN 显示慢速不使用索引,但它没有解释为什么不使用索引以及如何使用索引。
标签: sql performance select db2 sql-order-by