我在PostgreSQL 9.1中做了简单的实验.我创建test了如下表:
CREATE TABLE test
(
id serial NOT NULL,
CONSTRAINT id PRIMARY KEY (id )
)
CREATE INDEX id_idx
ON test
USING btree
(id );
Run Code Online (Sandbox Code Playgroud)
然后我添加一些数据:
insert into test values(DEFAULT);
insert into test values(DEFAULT);
insert into test values(DEFAULT);
...many times :)
Run Code Online (Sandbox Code Playgroud)
现在我有test10'000排的桌子.我的第一个实验是获取行id:
explain select * from test where id = 50;
Index Scan using id_idx on test (cost=0.00..8.27 rows=1 width=4)
Index Cond: (id = 50)
Run Code Online (Sandbox Code Playgroud)
好的,这里没什么奇怪的.让我们按值范围进行查询:
explain select * from test where id >= 50;
Seq Scan on test (cost=0.00..170.00 rows=9951 width=4)
Filter: (id >= 50)
Run Code Online (Sandbox Code Playgroud)
我们通过顺序扫描获得了9951行,但是如果我只想获得第一行呢?
explain select * from test where id >= 50 limit 1;
Limit (cost=0.00..0.02 rows=1 width=4)
-> Seq Scan on test (cost=0.00..170.00 rows=9951 width=4)
Filter: (id >= 50)
Run Code Online (Sandbox Code Playgroud)
我希望看到索引扫描rows=1,但我再次进行顺序扫描(扫描后有限制).有什么办法可以用SQL查询实现这种行为吗?
注意:我有类似的MongoDB查询:
> db.test.find({'dt':{$gte:ISODate("2013-07-20T00:00:00.00Z")}}).count()
10000
> db.test.find({'dt':{$gte:ISODate("2013-07-20T00:00:00.00Z")}}).limit(1).explain()
{
"cursor" : "BtreeCursor dt_1",
"isMultiKey" : false,
"n" : 1,
"nscannedObjects" : 1,
"nscanned" : 1,
"nscannedObjectsAllPlans" : 1,
"nscannedAllPlans" : 1,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 13,
"indexBounds" : {
"dt" : [
[
ISODate("2013-07-20T00:00:00Z"),
ISODate("0NaN-NaN-NaNTNaN:NaN:NaNZ")
]
]
},
"server" : "******:27017"
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,MongoDB使用索引扫描仅扫描了1个文档,这很棒.
首先要做的是 - 额外的索引是不必要的
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "id" for table "test"
Run Code Online (Sandbox Code Playgroud)
第二:始终运行解释分析而不是简单的解释,并获得真正的价值.在这种情况下,表可能很热(已经在内存中),所以SEQ SCAN真的非常快!
第三:你没有指定任何排序,所以Postgresql可以随意给你任何随机排序,因此任何一个随机ID> = 50.这很可能不是你想要的.PostgreSQL在一个页面上存储了许多元组; 在这种情况下知道数据的分布很可能在第一页上你有一个id> = 50的元组,所以这种情况下的SEQ SCAN方法是正确的,也是最快的.
第四:VACUUM ANALYZE在进行任何实际分析之前运行.
之后VACUUM ANALYZE使用EXPLAIN ANALYZE,我得到:
# EXPLAIN ANALYZE SELECT * FROM test WHERE id >= 50 ORDER BY id LIMIT 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..0.03 rows=1 width=4) (actual time=0.028..0.029 rows=1 loops=1)
-> Index Scan using id on test (cost=0.00..416.47 rows=12241 width=4) (actual time=0.027..0.027 rows=1 loops=1)
Index Cond: (id >= 50)
Total runtime: 0.059 ms
(4 rows)
Run Code Online (Sandbox Code Playgroud)