时间维度中布尔列的索引
Posted
技术标签:
【中文标题】时间维度中布尔列的索引【英文标题】:Index on boolean columns in time dimension 【发布时间】:2014-02-11 15:13:52 【问题描述】:在我们数据仓库的时间维度中,我们有很多带有布尔标志的列,例如:
is_ytd(年初至今) is_mtd(本月至今) is_current_date is_current_month is_current_year在所有此类列上创建部分索引会是一个好的索引策略吗?比如:
CREATE INDEX tdim_is_current_month
ON calendar (is_current_month)
WHERE is_current_month;
我们的时间维度有 136 列、7000 行、53 列,带有布尔指示符。
为什么我们使用标志而不是从 current_date
导出所需的日期范围?
-
让生活更轻松
强制一致性
加速查询
提供不容易导出的指标
更轻松地使用其他工具
广告1)
一旦你加入了time
维度(这几乎总是在分析数据仓库中的任何事实表时),写where is_current_year
而不是where extract(year from time_date) = extract(year from current_date)
要容易得多
广告2)
示例:弄清楚年初至今(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"
【问题讨论】:
奇特的设计。您不只是从系统时间中得出当前年份吗? @DavidAldridge 添加了一些解释为什么我们在时间维度中使用标志。 【参考方案1】:如果is_current_month = true
代表超过百分之几的行,则不会使用索引。 7,000 行太少了,连麻烦都没有。
【讨论】:
【参考方案2】:也许这更像是一个评论而不是一个答案......
鉴于查询计划器/优化器获得了正确的基数和连接类型,任何涉及事实表和时间维度之间连接的查询的执行时间将取决于事实表的大小。
您的时间维度要么一直在缓存中,要么在几毫秒内完全读取。根据当前负载,您将有比这更大的变化!其余的执行时间与时间维度无关。
话虽如此,我完全赞成使用包中的每一个技巧来帮助查询规划器/优化器提出足够好的估计。有时这意味着创建或禁用约束以及创建不必要的索引。
【讨论】:
以上是关于时间维度中布尔列的索引的主要内容,如果未能解决你的问题,请参考以下文章