在我的数据库中,我们将审计信息存储在与当前记录相同的表中。因此,像product\u customer这样的表有4列product\u id、customer\u id、start\u date、end\u date,其中product\u id、customer\u id和start date是主键列。每当我们必须结束与客户的产品关联时,我们都会更新该记录的结束日期,如果购买了新产品,我们会插入一个新记录,其中包含相应的客户和产品id值,开始日期为购买日期,结束日期为空。
这张表格现在有近6000万条记录可以追溯到20年前,其中大约65%是审计信息。我建议将至少有15年历史的数据移动到存档表中,但这与某些保留策略有关,因此必须得到很多人的批准。我目前的oracledb版本是10g,但它将在几周内迁移到12c。
有一个新的要求,我们需要返回与特定产品相关联的客户计数,但问题是有些产品拥有数百万客户,因此我编写的查询需要花费大量时间(有时20分钟)来执行。
SELECT count(customer_id)
FROM product_customer
WHERE product_id = i_product_id AND end_date IS NULL;
既然我们正在迁移到oracle12c,有没有办法加快这个查询(我听说b-tree索引不索引空值,位图索引索引索引索引空值,但是由于这个表上经常运行dml语句,所以位图索引在这里不起作用)。
你能帮忙吗。
3条答案
按热度按时间holgip5t1#
您的问题是在一个表中存储记录的历史和实际版本。
基本上,这个概念是好的,因为它可以很容易地制定涵盖实际数据和历史数据的查询。对于有限的数据,它也有很好的表现,但在某种程度上,您应该注意,实际数据的查询不会扫描历史数据
oracle开发了分区选项来解决这个问题。
您需要定义一个具有两个分区的表—一个用于实际数据,另一个用于历史记录。
使用
LIST
分区如下:重要的是,每次客户产品更改后,您都需要更新
end_date
(来自null
所以你必须允许行移动TechNicAll该行将从活动分区中删除并插入到历史分区中。
现在您的查询将
full table scan
但仅在活动分区上。例子
活动分区将只有21m行(60m的35%),因此该查询将在几秒钟内返回。
如果你认为完全扫描很慢,我需要一个索引,这很可能在这里不起作用。
请记住,索引访问是您当前的问题(至少我猜是1/6),使用相同的索引访问可以将运行时间缩短到7分钟(从20分钟减少35%)。
为什么?在大多数情况下,你有数以百万计的客户,但只有成千上万的产品。这意味着您需要访问活动分区的很大一部分才能获得所有客户,而完全扫描比索引访问更好。
不管怎样,如果你有一个不同的设置,有少量的客户和大量的(个别的)产品,你可能会从索引中获利
请注意
LOCAL
意味着您将只使用活动分区上的索引。另外,product\u id和customer\u id都在索引中,因此您根本不需要访问该表。以下是预期计划:
yshpjwxd2#
您的查询应该能够利用上的索引
product_customer(product_id, end_date)
.但是,即使扫描6000万行的表也不需要20分钟。其他事情可能会干扰,例如:
系统上的其他进程锁定记录/表。
使用视图而不是表。
我还将指出,我所看到的许多系统——特别是oracle——使用的是“无限”结束日期,而不是
NULL
在这种情况下。DATE '4195-01-01'
似乎很受欢迎;我不知道这是地方主义还是更普遍的真理。oprakyz73#
您可以尝试以下索引:
自
product_id
如果不为空,则每个表记录都将始终有一个索引项,即使对于结束日期为空的记录也是如此。压缩使索引更紧凑,占用磁盘空间更少,读取速度更快。
别忘了收集新索引的统计数据