在没有时间信息的情况下,按天对一组相关的Oracle表进行分区
我有一组类似于此的表格: 时间表相对较小:在没有时间信息的情况下,按天对一组相关的Oracle表进行分区,oracle,database-partitioning,Oracle,Database Partitioning,我有一组类似于此的表格: 时间表相对较小: Time (TIMESTAMP) timeId (NUMBER) Data... (NUMBER) 表2大,每个时间约30行\u表行: timeId (NUMBER) table2Id (NUMBER) Data... (NUMBER) 表3非常大,大约每行10行,在几百天后目前为14亿行: timeId (NUMBER) table2Id (NUMBER) table3Id (NUMBER) Data..
Time (TIMESTAMP)
timeId (NUMBER)
Data... (NUMBER)
表2大,每个时间约30行\u表行:
timeId (NUMBER)
table2Id (NUMBER)
Data... (NUMBER)
表3非常大,大约每行10行,在几百天后目前为14亿行:
timeId (NUMBER)
table2Id (NUMBER)
table3Id (NUMBER)
Data... (NUMBER)
我的查询至少总是在timeId上加入,并且每个查询被分成几天,10天的读取将导致10个较小的查询。每天将新数据写入所有表。我们需要从这些表中存储和查询多年的数据
当时间信息仅通过联接已知时,如何将这些表划分为每日数据块?我应该以不依赖时间的方式来看待分区吗?这是自动完成的,还是必须手动完成
Oracle 11.2版参考分区在这里可能会有所帮助。它允许子表的分区方案由父表确定
explain plan for
select *
from table3
join time_table using (timeId)
where time = date '2000-01-02';
select * from table(dbms_xplan.display);
Plan hash value: 832465087
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 91 | 3 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE| | 1 | 91 | 3 (0)| 00:00:01 | 2 | 2 |
| 2 | NESTED LOOPS | | 1 | 91 | 3 (0)| 00:00:01 | | |
|* 3 | TABLE ACCESS FULL | TIME_TABLE | 1 | 39 | 2 (0)| 00:00:01 | 2 | 2 |
|* 4 | TABLE ACCESS FULL | TABLE3 | 1 | 52 | 1 (0)| 00:00:01 | 2 | 2 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("TIME_TABLE"."TIME"=TIMESTAMP' 2000-01-02 00:00:00')
4 - filter("TABLE3"."TIMEID"="TIME_TABLE"."TIMEID")
Note
-----
- dynamic sampling used for this statement (level=2)
- automatic DOP: skipped because of IO calibrate statistics are missing
模式
执行计划
Pstart和Pstop表明,即使分区谓词仅设置在较小的父表上,也正确地修剪了庞大的子表
explain plan for
select *
from table3
join time_table using (timeId)
where time = date '2000-01-02';
select * from table(dbms_xplan.display);
Plan hash value: 832465087
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 91 | 3 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE| | 1 | 91 | 3 (0)| 00:00:01 | 2 | 2 |
| 2 | NESTED LOOPS | | 1 | 91 | 3 (0)| 00:00:01 | | |
|* 3 | TABLE ACCESS FULL | TIME_TABLE | 1 | 39 | 2 (0)| 00:00:01 | 2 | 2 |
|* 4 | TABLE ACCESS FULL | TABLE3 | 1 | 52 | 1 (0)| 00:00:01 | 2 | 2 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("TIME_TABLE"."TIME"=TIMESTAMP' 2000-01-02 00:00:00')
4 - filter("TABLE3"."TIMEID"="TIME_TABLE"."TIMEID")
Note
-----
- dynamic sampling used for this statement (level=2)
- automatic DOP: skipped because of IO calibrate statistics are missing
警告
引用分区有一些怪癖。在11g中,它不适用于间隔分区,因此必须手动为父表定义每个分区。外键也无法禁用,这可能需要修改一些脚本。和任何很少使用的功能一样,它也有一些bug。您使用的是什么版本的Oracle?答案可能在10.2、11.2和12.1中有所不同。请显示数据类型和示例数据。@OldProgrammer所有类型都是数字,时间是时间戳。@JustinCave,我们使用的是v11.2,所以表3中每天有多少行?一天十行似乎很小。我不推荐这种解决方案,因为它需要全局索引,并且只支持2级主细节。它还需要FK,这对于仓库来说可能是一个问题。追加插入将不起作用。相反,我可以建议重新设计。什么是TimeID?它是序列值吗?为什么?时间是自然唯一的价值-使用它而不是毫无意义的生成数字。@Rusty你是对的引用分区有一些明显的缺点。即使有外键。如果只添加一列,而表已经是fat,则反规范化可能是更好的解决方案。我们以前使用时间戳作为PK,但实际上可能存在重复的时间,这是有效的数据。我还能保留TimeId,但把时间复制到其他表中吗?@SamuelO'Malley当然,这可能有道理。对于仅使用时间戳的简单情况,它使查询和分区修剪更容易。它还允许在其他查询中使用其他时间维度值。不过,这一额外列需要大约10GB的存储空间。@Jon Heller-根据Oracle规范:目标表不能定义任何触发器或引用完整性约束。因此append提示将被忽略。在保持TimeID作为PK的情况下,这会起作用吗?实际上,我们得到的行具有相同的时间戳,但ID不同。我根本不会使用从序列生成的值。它使数据无法在环境之间传输。您需要使用锁定机制来防止重复。等待时间将少于1微秒。还有一个提示如何获得始终唯一的时间戳值。返回systimestamp值-通常在Unix上的精度为6,在Windows上的精度为3。将其修剪为3位数。然后,您可以使用会话级全局变量计数器中的值或insert中的rownum作为选择操作。获取MODrownum,1000000并将该值添加到systimestamp。因此,可以在1毫秒内生成100万个唯一值。这应该足够了。示例:l_log_rec.log_time:=l_log_rec.log_time+to_dsinterval'PT0.| | | to_charg_counter'fm000000009'| | |这是一个很好的提示,但不幸的是,我正在写入的时间和ID来自一个无法修改的系统中的诊断数据。事件可以在同一时间发生,这不是偶然的,而是双重事件。
drop table time_table;
create table Time_Table
(
time TIMESTAMP,
-- timeId NUMBER, Why you need ID when you have timestamp?????
Data01 NUMBER,
constraint time_table_pk primary key (time) -- not timeID!!!
)
partition by range (time)
(
partition p1 values less than (date '2000-01-02'),
partition p2 values less than (date '2000-01-03'),
partition p3 values less than (date '2000-01-04')
);
create table table2
(
time timestamp not null,
table2ID number,
Data01 number
)
partition by range (time)
(
partition p1 values less than (date '2000-01-02'),
partition p2 values less than (date '2000-01-03'),
partition p3 values less than (date '2000-01-04')
);
create table table3
(
time timestamp not null,
table2Id number,
table3Id number,
Data01 number
)
partition by range (time)
(
partition p1 values less than (date '2000-01-02'),
partition p2 values less than (date '2000-01-03'),
partition p3 values less than (date '2000-01-04')
);