explain analyze
显示postgres会对我的查询使用索引扫描来获取行并按日期执行过滤(即2017-04-14 05:27:51.039
):
explain analyze select * from tbl t where updated > '2017-04-14 05:27:51.039';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Index Scan using updated on tbl t (cost=0.43..7317.12 rows=10418 width=93) (actual time=0.011..0.515 rows=1179 loops=1)
Index Cond: (updated > '2017-04-14 05:27:51.039'::timestamp without time zone)
Planning time: 0.102 ms
Execution time: 0.720 ms
然而运行相同的查询,但使用不同的日期过滤器'2016-04-14 05:27:51.039'显示postgres将使用seq扫描运行查询:
explain analyze select * from tbl t where updated > '2016-04-14 05:27:51.039';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Seq Scan on tbl t (cost=0.00..176103.94 rows=5936959 width=93) (actual time=0.008..2005.455 rows=5871963 loops=1)
Filter: (updated > '2016-04-14 05:27:51.039'::timestamp without time zone)
Rows Removed by Filter: 947
Planning time: 0.100 ms
Execution time: 2910.086 ms
postgres如何决定使用什么,特别是在按日期执行过滤时?
答案 0 :(得分:2)
Postgres查询计划器的决策基于成本估算和表统计。表统计信息由public void Nieuweontvangst (DateTime datumIN, string leverancier,
string levlotnr, double hoeveelheidIN,
string eenheidIN, string grondstofIN,
string stockplaatsIN, int lotnrlevID, int lotnrINID)
{
var manager = new LotnummersDBManager();
using (var conLotnummers = manager.Getconnection())
{
conLotnummers.Open();
using (var traNieuweOntvangst =
conLotnummers.BeginTransaction(IsolationLevel.ReadCommitted))
{//begin traNieuweOntvangst
//first transaction: the output parameter @newinsertedlevID
//just needs to be used in the second transaction, but not be displayed
//------------------
using (var comlevlotnrs = conLotnummers.CreateCommand())
{
comlevlotnrs.Transaction = traNieuweOntvangst;
comlevlotnrs.CommandType = CommandType.StoredProcedure;
comlevlotnrs.CommandText = "Levlotnr"; //name of first
stored procedure
var parlotleverancier = comlevlotnrs.CreateParameter();
parlotleverancier.ParameterName = "@Lotleverancier";
parlotleverancier.Value = levlotnr;
comlevlotnrs.Parameters.Add(parlotleverancier);
var parleverancier = comlevlotnrs.CreateParameter();
parleverancier.ParameterName = "@leverancier";
parleverancier.Value = leverancier;
comlevlotnrs.Parameters.Add(parleverancier);
var parlotlevID = comlevlotnrs.CreateParameter();
parlotlevID.ParameterName = "@newinsertedlevID";
parlotlevID.DbType = DbType.Int32;
parlotlevID.Direction = ParameterDirection.Output;
comlevlotnrs.Parameters.Add(parlotlevID);
}
using (var comLotnrsIN = conLotnummers.CreateCommand())
{// second transaction= output parameter @newinsertedlevID
// should be used here where now stands @lotnrlevIN.
// THIS IS WHERE I STRUGGLE
// also here I get an output parameter
// @newinsertedLotnrINID only to be used in the 3rd
// transaction, not to be displayed.
comLotnrsIN.Transaction = traNieuweOntvangst;
comLotnrsIN.CommandType = CommandType.StoredProcedure;
comLotnrsIN.CommandText = "LotnrIN";
var pardatumIN = comLotnrsIN.CreateParameter();
pardatumIN.ParameterName = "@datumIN";
pardatumIN.Value = datumIN;
comLotnrsIN.Parameters.Add(pardatumIN);
var parhoeveelIN = comLotnrsIN.CreateParameter();
parhoeveelIN.ParameterName = "@hoeveelIN";
parhoeveelIN.Value = hoeveelheidIN;
comLotnrsIN.Parameters.Add(parhoeveelIN);
var pargrondstofIN = comLotnrsIN.CreateParameter();
pargrondstofIN.ParameterName = "@grondstofIN";
pargrondstofIN.Value = grondstofIN;
comLotnrsIN.Parameters.Add(pargrondstofIN);
var parlotnrlevIN = comLotnrsIN.CreateParameter();
parlotnrlevIN.ParameterName = "@lotnrlevIN";
parlotnrlevIN.Value = lotnrlevID;
comLotnrsIN.Parameters.Add(parlotnrlevIN);
var parLotIN = comLotnrsIN.CreateParameter();
parLotIN.ParameterName = "@newinsertedLotnrINID";
parLotIN.DbType = DbType.Int32;
parLotIN.Direction = ParameterDirection.Output;
comLotnrsIN.Parameters.Add(parLotIN);
}
using (var comStockeren = conLotnummers.CreateCommand())
{
//Third transaction
// I need to use the output parameter from 2nd transaction
// @newinsertedLotnrINID where you see now @lotnrINID.
//THIS IS THE SAME STRUGGLE AS 2ND TRANSACTION
comStockeren.Transaction = traNieuweOntvangst;
comStockeren.CommandType = CommandType.StoredProcedure;
comStockeren.CommandText = "StockIN";
var parlotIN = comStockeren.CreateParameter();
parlotIN.ParameterName = "@lotnrINID";
parlotIN.Value = lotnrINID;
var paromschrStockIN = comStockeren.CreateParameter();
paromschrStockIN.ParameterName = "@omschrstockIN";
paromschrStockIN.Value = stockplaatsIN;
comStockeren.Parameters.Add(paromschrStockIN);
}
traNieuweOntvangst.Commit();
}
}
}
收集,并由其他一些实用程序命令机会性地收集。当autovacuum打开时(默认情况下),这一切都会自动发生。
由于,大多数查询只检索表中的一小部分行 限制要检查的行的
ANALYZE
子句。这样的策划者 需要估计WHERE
条款的选择性 是,WHERE
中与每个条件匹配的行的比例 条款。用于此任务的信息存储在WHERE
系统目录。pg_statistic
中的条目已更新pg_statistic
和ANALYZE
命令,并且始终是近似值 即使是刚刚更新的。
有一个行计数(在VACUUM ANALYZE
中),一个最常见的值列表等。
Postgres希望找到的行越多,它就越有可能转换为顺序扫描,这对于检索表的大部分来说更便宜。
一般来说,它的索引扫描 - >位图索引扫描 - >顺序扫描,预计将检索的行数越多。
对于您的特定示例,重要统计信息为pg_class
,这使Postgres大致了解有多少行的值大于给定值。对于人眼来说,有更方便的视图histogram_bounds
:
pg_stats
有一个dedicated chapter explaining row estimation in the manual.
答案 1 :(得分:1)
显然,优化查询很棘手。这个答案并不是为了深入了解Postgres优化器的细节。相反,它旨在为您提供有关如何决定使用索引的背景信息。
您的第一个查询估计会返回10,418行。使用索引时,会发生以下操作:
换句话说,使用索引时会有一些开销 - 初始化索引然后单独查找每个数据页。
当引擎进行全表扫描时:
没有额外的开销。此外,发动机可以预先加载"处理当前页面时要扫描的下一页。 I / O和处理的重叠是一个巨大的胜利。
我想说的是,在这两者之间取得平衡可能会非常棘手。在10,418和5,936,959之间,Postgres决定索引开销(以及随机获取页面)的成本不仅仅是扫描整个表格。