【问题标题】:how to model data / indexes to find timeslices fast如何对数据/索引进行建模以快速找到时间片
【发布时间】:2017-03-16 02:57:03
【问题描述】:

我们的数据库中有很多表,其中的数据仅在特定时间段内相关/有效。例如合同,它们有一个 start_date 和一个 end_date。而且不一定是整月。

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

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

由于我们的表中有 20 年的数据(约 7000 天),因此日期是非常好的过滤标准,尤其是当 :1 和 :2 是同一天时。 region_id 不是一个很好的过滤条件,因为没有那么多(~50)。在此示例中,我们的表上有(除其他外)2 个索引:

contracts_valid_index (start_date, end_date)
contracts_region (region_id)

不幸的是,上述查询通常会使用contracts_region 索引,因为优化器认为它更便宜。这背后的原因很简单:当我在数据中间选择一天时,数据库会认为超过 start_date 的索引实际上并不好,因为它只会过滤掉一半的数据。通过查看 end_date ,同样适用。所以优化器认为他只能过滤掉我数据的1/4。因为他不知道 start_date 和 end_date 通常非常接近,而且这个索引会非常有选择性。

使用contracts_valid_index 的执行计划的成本高于使用contracts_region 的执行计划。但实际上contracts_valid_index要好得多。

我目前认为我不能通过制作更好的索引来加快查询速度(除了删除contracts_valid_index 之外的所有索引)。但也许我的数据模型对于查询优化器来说不是很好。所以我假设其他人也有类似的需求,并且很想知道他们如何建模数据或优化数据表/索引。

有什么建议吗?

【问题讨论】:

    标签: oracle indexing query-optimization oracle12c


    【解决方案1】:

    由于您表示您使用的是 Oracle 12c,因此将您的 Start_Date 和 End_Date 列定义为 temporal 有效时间列可能会有所帮助,前提是它们匹配适当的时间有效性语义(start_date 和 end_date 需要是时间戳,end_date 必须是 > start_date或者可能为空且有效的时间段包括开始日期但不包括结束日期,也就是说,它是一个部分关闭/打开的范围,与通常表示完全关闭范围的 between 运算符不同。例如:

    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 AS OF PERIOD FOR valid_time :1 c
    WHERE
      c.region_id = :2
    

    在语义上类似于:

    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 的 null 值是否分别表示时间的开始和结束,因为我目前没有要测试的 R12 实例。

    【讨论】:

    • 这就是我想要的。我不喜欢间隔的开放式结束,但这不应该是一个问题。但我怀疑我是否会为此获得 JPA 支持
    • 我在发布后做了一些额外的研究,开始和结束日期列可以是日期或时间戳,空值被视为时间的开始和结束,但如果你使用魔术日期对于这些价值观,它们将继续发挥作用。
    • 关于开放式和封闭式范围,我都使用过,并且更喜欢开放式。这样,当一个范围结束而下一个范围开始时,您可以使用与下一个开始相同的日期作为先验结束日期,而不必担心重叠。如果您对开始日期和结束日期使用截断值,则不必担心未截断范围检查落​​在一个范围结束和下一个范围开始之间的间隙中。
    • 我想我在接受这个答案时有点太快了。看起来不错。看起来 Oracle 为这种情况提供了一些东西。但这根本行不通。我制作了一张包含 1000 万个条目的表格。每个有效期为 1000 年到 1999 年之间的一个月。所以当我查询 1 天时,我应该只得到 0.01% 的行。但无论我如何创建索引,Oracle 总是会进行全表扫描。使用原始查询,我至少有时可以让 oracle 使用索引。
    【解决方案2】:

    我之前遇到过与 MySQL 数据库上的大量 IP 地址相关的索引使用问题(请耐心等待;这确实是同样的问题)。

    The solution 我发现(通过大量谷歌搜索,我没有把发明它的功劳归功于)是使用地理空间索引。这是专门为查找范围内的数据而设计的。大多数实现(包括 mysql 中的实现)都硬连接到二维空间,而 ip 地址和时间是一维的,但是将一维坐标映射到二维空间是微不足道的(请参阅链接以获取分步说明) .

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

    【讨论】:

      【解决方案3】:

      您可以尝试以下查询,看看它是否效果更好:

      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
      

      虽然它可能会阻止使用contracts_region索引的任何可能性。

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

      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_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
      

      我的样本数据包含 10,000,000 行,平均分布在 50 个区域和 1000 年中,每个区域的有效范围为 30 天。

      【讨论】:

        猜你喜欢
        • 2010-10-25
        • 2019-11-27
        • 2018-09-26
        • 2012-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-05
        相关资源
        最近更新 更多