在数据仓库中的时间维度中,我们有很多带有布尔标志的列,例如:
在所有这些列上创建部分索引是否是一个很好的索引策略?类似的东西:
CREATE INDEX tdim_is_current_month
ON calendar (is_current_month)
WHERE is_current_month;
我们的时间维度有136列,7 000行,53列,带有布尔指示符。
为什么我们使用标记而不是从current_date
推导出所需的日期范围?
的Ad1)
加入time
维度后(几乎总是在分析数据仓库中的任何事实表时),编写where is_current_year
而不是where extract(year from time_date) = extract(year from current_date)
Ad2的)
示例:弄清楚年初至今(YTD)听起来很简单。我们可以从time_date between date_trunc('year', current_date) and current_date
开始。但有些人实际上会排除current_date
(这是有道理的,因为今天还没有完成)。在这种情况下,我们将使用:time_date between date_trunc('year', current_date) and (current_date - 1)
。更进一步 - 如果由于某种原因DW几天没有更新会发生什么。也许那时你希望YTD链接到你最后完成所有源系统数据的日子。当你对YTD的含义有一个共同的定义,而不是降低不同含义的风险。
广告3) 我认为根据基于实时计算表达式的过滤器,基于索引的布尔标志过滤数据应该更快。
广告4)
有些标志不容易创建 - 例如我们有标记is_first_workday_in_month, is_last_workday_in_month
。
广告5) 在某些工具中,使用现有列比使用SQL表达式更容易。例如,当为OLAP多维数据集创建维度时,将表列添加为层次结构级别比使用SQL表达式构造此类级别更容易。
测试布尔标志的索引
我测试了所有索引标志,并使用一个事实表和时间维度(命名日历)运行explain analyze
进行简单查询:
select count(*) from fact_table join calendar using(time_key)
对于大多数标志,我得到索引扫描:
"Aggregate (cost=4022.80..4022.81 rows=1 width=0) (actual time=38.642..38.642 rows=1 loops=1)"
" -> Hash Join (cost=13.12..4019.73 rows=1230 width=0) (actual time=38.640..38.640 rows=0 loops=1)"
" Hash Cond: (fact_table.time_key = calendar.time_key)"
" -> Seq Scan on fact_table (cost=0.00..3249.95 rows=198495 width=2) (actual time=0.006..17.769 rows=198495 loops=1)"
" -> Hash (cost=12.58..12.58 rows=43 width=2) (actual time=0.054..0.054 rows=43 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 2kB"
" -> Index Scan using cal_is_qtd on calendar (cost=0.00..12.58 rows=43 width=2) (actual time=0.014..0.049 rows=43 loops=1)"
" Index Cond: (is_qtd = true)"
"Total runtime: 38.679 ms"
对于某些标志,我将位图堆扫描与位图索引扫描相结合:
"Aggregate (cost=13341.07..13341.08 rows=1 width=0) (actual time=100.972..100.973 rows=1 loops=1)"
" -> Hash Join (cost=6656.54..13001.52 rows=135820 width=0) (actual time=5.729..86.972 rows=198495 loops=1)"
" Hash Cond: (fact_table.time_key = calendar.time_key)"
" -> Seq Scan on fact_table (cost=0.00..3249.95 rows=198495 width=2) (actual time=0.012..22.667 rows=198495 loops=1)"
" -> Hash (cost=6597.19..6597.19 rows=4748 width=2) (actual time=5.706..5.706 rows=4748 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 158kB"
" -> Bitmap Heap Scan on calendar (cost=97.05..6597.19 rows=4748 width=2) (actual time=0.440..4.971 rows=4748 loops=1)"
" Filter: is_past_quarter"
" -> Bitmap Index Scan on cal_is_past_quarter (cost=0.00..95.86 rows=3249 width=0) (actual time=0.395..0.395 rows=4748 loops=1)"
" Index Cond: (is_past_quarter = true)"
"Total runtime: 101.013 ms"
只有两个标志才能进行seq扫描:
"Aggregate (cost=17195.33..17195.34 rows=1 width=0) (actual time=122.108..122.108 rows=1 loops=1)"
" -> Hash Join (cost=9231.13..16699.10 rows=198495 width=0) (actual time=23.960..108.018 rows=198495 loops=1)"
" Hash Cond: (fact_table.time_key = calendar.time_key)"
" -> Seq Scan on fact_table (cost=0.00..3249.95 rows=198495 width=2) (actual time=0.012..22.153 rows=198495 loops=1)"
" -> Hash (cost=9144.39..9144.39 rows=6939 width=2) (actual time=23.935..23.935 rows=6939 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 231kB"
" -> Seq Scan on calendar (cost=0.00..9144.39 rows=6939 width=2) (actual time=17.427..22.908 rows=6939 loops=1)"
" Filter: is_eoq"
"Total runtime: 122.138 ms"
答案 0 :(得分:1)
如果is_current_month = true
代表超过百分之几的行,则不会使用索引。 7,000行太少甚至不会打扰。
答案 1 :(得分:1)
也许这更像是评论而不是答案......
鉴于查询规划器/优化器获得了基数和连接类型正确,任何涉及事实表和时间维度之间的连接的查询的执行时间将由事实表的大小决定。
您的时间维度将始终处于缓存中,或者在几毫秒内完全读取。根据当前的负载,您将有更大的变化!其余的执行时间与时间维度无关。
话虽如此,我全都在使用包中的每一个技巧来帮助查询规划器/优化器提供足够好的估计。有时这意味着创建或禁用约束并创建不必要的索引。