Oracle 如何为数据/索引建模以快速查找时间片

Oracle 如何为数据/索引建模以快速查找时间片,oracle,indexing,query-optimization,oracle12c,Oracle,Indexing,Query Optimization,Oracle12c,我们的数据库中有很多表,其中包含的数据仅在某一段时间内相关/有效。例如,合同有开始日期和结束日期。而且也不一定是整整几个月 现在,这是针对该表的典型查询类型: SELECT * FROM contracts c WHERE c.start_date <= :1 AND c.end_date >= :2 AND c.region_id = :3 不幸的是,上面的查询通常会使用区域索引,因为优化器认为这样更便宜。这背后的原因很简单:当我选择数据中间的一天时,

我们的数据库中有很多表,其中包含的数据仅在某一段时间内相关/有效。例如,合同有开始日期和结束日期。而且也不一定是整整几个月

现在,这是针对该表的典型查询类型:

SELECT
  *
FROM
  contracts c
WHERE
      c.start_date <= :1
  AND c.end_date >= :2
  AND c.region_id = :3
不幸的是,上面的查询通常会使用区域索引,因为优化器认为这样更便宜。这背后的原因很简单:当我选择数据中间的一天时,数据库会认为SturnStalk日期上的索引将不太好,因为它只过滤掉一半的数据。通过查看结束日期,同样适用。因此,优化器认为他只能过滤掉我1/4的数据。因为他不知道开始日期和结束日期通常是非常接近的,这个索引将是非常有选择性的

使用contracts\u valid\u索引的执行计划比使用contracts\u region的执行计划成本更高。但实际上,合同有效指数要好得多

我目前认为,除了删除除有效索引之外的所有合同之外,我无法通过创建更好的索引来加快查询速度。但可能我的数据模型对于查询优化器不是很好。因此,我假设其他人也有类似的需求,并希望了解他们是如何建模数据或优化数据表/索引的


有什么建议吗?

既然您表示正在使用Oracle 12c,那么将开始日期和结束日期列定义为有效的时间列可能会有所帮助,只要它们与适当的时间有效性语义相匹配,开始日期和结束日期需要是时间戳,结束日期必须大于开始日期或可能为空,有效时间段包括开始日期,但不包括结束日期,即它是一个部分关闭/打开的范围,不同于通常的表示完全关闭范围的中间运算符。例如:

ALTER TABLE contracts ADD (PERIOD FOR valid_time (start_date, end_date));
然后,您可以查询给定有效期的合同表,因此:

SELECT 
  c.* 
FROM
  contracts VERSIONS PERIOD FOR valid_time BETWEEN :1 AND :2 c
WHERE
  c.region_id = :3
这在语义上类似于:

SELECT 
  c.* 
FROM
  contracts c
WHERE
      :1 < end_date
  AND start_date <= :2
  AND c.region_id = :3
SELECT 
  c.* 
FROM
  contracts c
WHERE
      :1 BETWEEN start_date AND end_date
  and :1 <> end_date
  and c.region_id = :2
语义上类似于:

SELECT 
  c.* 
FROM
  contracts c
WHERE
      :1 < end_date
  AND start_date <= :2
  AND c.region_id = :3
SELECT 
  c.* 
FROM
  contracts c
WHERE
      :1 BETWEEN start_date AND end_date
  and :1 <> end_date
  and c.region_id = :2

我不确定start_date和end_date的空值是否分别表示时间的开始和结束,因为我目前没有R12实例可供测试。

我以前遇到过与MySQL数据库上的大型IP地址集相关的索引使用问题;这确实是同一个问题

我通过谷歌搜索发现,我并没有因为发明了地理空间索引而受到赞扬。这是专门为查找范围内的数据而设计的。大多数实现(包括mysql中的实现)都是硬连接到二维空间的,而ip地址和时间是一维的,但是将一维坐标映射到二维空间非常简单,请参阅链接以获得逐步的解释


很抱歉,我对Oracle的地理空间功能一无所知,因此我无法提供任何示例代码,但它确实支持地理空间索引,因此可以有效地解决您的查询。

您可以尝试以下查询,看看它是否能更好地工作:

WITH t1 AS (
   SELECT *
     FROM contracts c
    WHERE c.start_date <= :1
      AND c.end_date   >= :2
)
 SELECT *
   FROM t1
  WHERE c.region_id = :3
尽管这可能会阻止任何使用地区指数的可能性

或者,您可以尝试提示查询使用所需的索引:

SELECT /*+ INDEX(c contracts_valid_index) */
  *
FROM
  contracts c
WHERE
      c.start_date <= :1
  AND c.end_date >= :2
  AND c.region_id = :3
SELECT /*+ NO_INDEX(c contracts_region ) */
  *
FROM
  contracts c
WHERE
      c.start_date <= :1
  AND c.end_date >= :2
  AND c.region_id = :3
或者暗示它不要使用不需要的索引:

SELECT /*+ INDEX(c contracts_valid_index) */
  *
FROM
  contracts c
WHERE
      c.start_date <= :1
  AND c.end_date >= :2
  AND c.region_id = :3
SELECT /*+ NO_INDEX(c contracts_region ) */
  *
FROM
  contracts c
WHERE
      c.start_date <= :1
  AND c.end_date >= :2
  AND c.region_id = :3
当我自己在不使用提示的情况下进行测试时,我发现当选择接近可用日期范围开始或结束的日期时,优化器使用的是INDEX\RS\u ASC提示。将其添加到如下所示的查询中,导致我的测试使用所需的索引,即使日期范围更接近日期范围的中心:

SELECT /*+ INDEX_RS_ASC(c contracts_valid_index) */
  *
FROM
  contracts c
WHERE
      c.start_date <= :1
  AND c.end_date >= :2
  AND c.region_id = :3

我的样本数据包括10000000行,平均分布在50个地区和1000年,每个地区的有效期为30天。

这就是我要寻找的。我不喜欢中场休息的开放式,但这不应该是个问题。但我怀疑我是否能得到JPA对此的支持我在发布后做了一些额外的研究,开始和结束日期列可以是日期或时间戳,空值被视为时间的开始和结束,尽管如果你对这些值使用神奇的日期,它们将继续工作。关于开放和封闭范围,我和这两个都有过合作,而且我更喜欢开放式的。这样,当一个范围结束而下一个范围开始时,您可以使用与下一个范围开始相同的前一个范围结束日期,而不必担心重叠。如果你在开始日期和结束日期使用截断值,你不必担心一个不可信的范围检查落在一个范围结束和下一个范围开始之间。我想我接受这个答案时有点太快了。看起来不错。看起来Oracle为这个场景提供了一些东西。但这根本不起作用。我做了一张有1000万条记录的表格。每个有效期为1000年至1999年之间的一个月。因此,当我查询1天时,应该只得到0.01%的行。 但无论我如何创建索引,Oracle总是进行完整的表扫描。使用原始查询,我至少有时可以让oracle使用索引。