我有一张表(在Oracle 12c中),目前已有2200万条记录,每天还有10,000条记录。我们需要基于此表的计数,例如:
select col1, col2, count(*) cnt from my_table group by col1, col2;
此查询将返回少于30行,col1
,col2
的组合将是唯一的。
我们的应用程序需要经常检查CNT
值,但CNT
的近似值足够好。这意味着我们可以创建物化视图并每10-20分钟刷新一次。
物化视图是否适合此要求,还是应该为它创建常规表?
提前致谢!!!
答案 0 :(得分:1)
至少有三种不同的方法可以实现这一目标:
快速刷新物化视图快速刷新物化视图可能是理想的解决方案。插入10,000行将产生少量开销,但不需要重建任何东西;每次提交后立即可以使用新的总计,并且检索新的总计将非常快。缺点是快速刷新物化视图很难设置,并且有很多奇怪的陷阱。它们适用于您的示例模式,但可能无法在更复杂的情况下使用。
示例架构
drop table my_table;
create table my_table(
id number not null,
col1 number not null,
col2 number not null,
constraint my_table_pk primary key (id)
);
insert into my_table
select level, mod(level, 30), mod(level+1, 30)
from dual
connect by level <= 100000;
begin
dbms_stats.gather_table_stats(user, 'MY_TABLE');
end;
/
创建物化视图日志和物化视图
create materialized view log on my_table with rowid(col1, col2) including new values;
create materialized view my_table_mv
refresh fast on commit
enable query rewrite as
select col1, col2, count(*) total
from my_table
group by col1, col2;
查询重写
静默修改示例查询以使用小的物化视图而不是大表。
explain plan for
select col1, col2, count(*) cnt
from my_table
group by col1, col2;
select * from table(dbms_xplan.display);
Plan hash value: 786752524
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 30 | 300 | 3 (0)| 00:00:01 |
| 1 | MAT_VIEW REWRITE ACCESS FULL| MY_TABLE_MV | 30 | 300 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
压缩B *树索引如果只有30个唯一值,则索引应该压缩得很好并且不会占用太多空间。然后可以使用索引 一个快速的完整索引扫描,就像一个瘦的表。此方法至少需要一个值不为null。如果两者都可以为null,那么基于函数 索引在这里很有用。
create index my_table_idx on my_table(col1, col2) compress;
位图索引当存在少量不同的值时,位图索引很小且很快。然而,他们可能会引入灾难性的锁定问题 对于某些类型的DML。
create bitmap index my_table_idx on my_table(col1, col2);
答案 1 :(得分:0)
根据您的方法的近似程度,您还可以尝试SAMPLE子句:
select col1,
col2,
count(*) cnt
from my_table sample(1)
group by col1, col2;
根据值的数据分布,这可能会给出合理的估计。你可以测试你需要多少数字来得到一个合理的结果,但除非使用block子句,否则它很少超过4或5:
select col1,
col2,
count(*) cnt
from my_table sample block(10)
group by col1, col2;