我有一个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是否在没有二次排序的情况下保证这样的顺序?
答案 0 :(得分:1)
您想要的索引位于contacts(deleted, lastname, id)
。这将仅适用于lastname
作为排序键以及lastname
和id
的查询。
性能问题的原因。首先,仅使用lastname
的快速查询使用索引。另一个可能会也可能不会使用索引,但它必须使用相同的lastname
来获取所有行。然后它必须按id
对它们进行排序。毕竟,没有理由认为具有相同lastname
的索引中的前21行都具有相同的id
。
问题可能是两件事之一。首先,一个姓氏可能有许多具有相同id
的记录。第二个原因是DB2因为id
的存在而感到困惑,并决定不使用索引。
虽然它可能对查询没有帮助,但如果确实是{1},则应将id
声明为主键。