在没有时间信息的情况下,按天对一组相关的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')
);