【问题标题】:Does Oracle have a filtered index concept?Oracle 有过滤索引的概念吗?
【发布时间】:2011-08-21 20:07:45
【问题描述】:

类似于 SQLServer,我可以在其中执行以下操作

create index TimeSeriesPeriodSs1 on TimeSeriesPeriod (validationStatus, completionStatus)
where completionStatus= N'Complete'  
and  validationStatus= N'Pending'

【问题讨论】:

  • 您不会创建一个普通索引并将 where 子句放在您的 select SQL 中吗?这样,您的普通索引将为另一个 select stmt 提供不同的 where 子句表达式命中相同的字段。虽然不熟悉 SQL Server 过滤索引,所以不清楚它的好处。
  • @tbone - 尺寸可能是一个原因
  • @tbone:过滤索引的重点是避免在只需要一小部分索引时在大表上维护索引的开销。
  • Oracle 不需要“维护”整个索引吗?如果我们谈论的是 Oracle,而基于函数的索引就是这个问题的答案,那么向前移动的任何和所有修改的行都不需要应用该函数(即使结果为 null 并且没有存储,仍然所有 DML 都需要应用功能检查)。所以读取速度可能会快一点,但 DML 可能会慢一点,不是吗?对于这种开销(以及非正统的方法 imo),您真正获得了什么?只是索引的大小?不要试图变得困难(好吧,也许有点;),但很好奇
  • @Tbone 读取速度会快很多。例如,您有数百万或已处理的行,以及非常少的未处理的行。如果要查找下一个要处理的行,则不需要包含所有这些行的索引。

标签: sql-server oracle filtered-index


【解决方案1】:

基于函数的索引的一种潜在替代/改进方法是使用虚拟列。

create table TimeSeriesPeriod (
  --...
  pendingValidation as (
    case when completionStatus = N'Complete' and validationStatus= N'Pending'
      then 1
    else null
  ) virtual
);
create index TimeSeriesPeriodSs1 on TimeSeriesPeriod (pendingValidation);

select * from TimeSeriesPeriod where pendingValidation = 1;

请注意,与常规列一样,为虚拟列/基于函数的索引收集统计信息,因此它们确实具有非零成本。考虑在可能的情况下将多个过滤器折叠到一个虚拟列中

create table TimeSeriesPeriod (
  --...
  incompleteValidationStatus as (
    case when completionStatus = N'Complete' and validationStatus != N'Complete'
      then validationStatus
    else null
  ) virtual
);
create index TimeSeriesPeriodSs1 on TimeSeriesPeriod (incompleteValidationStatus);

select * from TimeSeriesPeriod where incompleteValidationStatus = N'Pending';
select * from TimeSeriesPeriod where incompleteValidationStatus = N'Failed Validation';

【讨论】:

    【解决方案2】:

    这是贾斯汀和亚历克斯答案的一个小变体,可能会节省更多的索引空间并使修改后的查询更具可读性 IMO:

    CREATE INDEX TimeSeriesPeriodSs1
        ON TimeSeriesPeriod( 
              (CASE WHEN completionStatus = 'Complete' AND validationStatus = 'Pending'
                    THEN 1
                    ELSE NULL
               END);
    
    SELECT * FROM TimeSeriesPeriod
      WHERE 1 = (CASE WHEN completionStatus = 'Complete' AND validationStatus = 'Pending'
                    THEN 1
                    ELSE NULL
                 END)
    

    【讨论】:

      【解决方案3】:

      您也许可以为此使用基于函数的索引,尽管在这种情况下它不是很愉快:

      create index TimeSeriesPeriodSs1 on TimeSeriesPeriod (
          case when validationStatus= N'Pending' and completionStatus= N'Complete' then validationStatus else null end,
          case when validationStatus= N'Pending' and completionStatus= N'Complete' then completionStatus else null end);
      

      您必须使查询的 where 子句完全匹配才能使其使用索引。

      select <fields>
      from TimeSeriesPeriod
      where case when validationStatus= N'Pending' and completionStatus= N'Complete' then validationStatus else null end = N'Pending'
      and case when validationStatus= N'Pending' and completionStatus= N'Complete' then completionStatus else null end = N'Complete';
      

      如果您可以定义(确定性)函数来执行case,这将更加简洁。有关更多信息和示例,请参阅here。或this,来自快速谷歌。

      【讨论】:

      • +1 用于包含 where 子句以使用基于函数的索引。这是使用 SQL Server 的过滤索引与使用 Oracle 的基于函数的索引的一大区别。
      【解决方案4】:

      您可以在 Oracle 中创建基于函数的索引,该索引利用了 NULL 值不存储在 b-tree 索引中的事实。类似的东西

      CREATE INDEX TimeSeriesPeriodSs1
          ON TimeSeriesPeriod( 
                (CASE WHEN completionStatus = 'Complete' AND validationStatus = 'Pending'
                      THEN validationStatus
                      ELSE NULL
                  END),
                (CASE WHEN completionStatus = 'Complete' AND validationStatus = 'Pending'
                      THEN completionStatus
                      ELSE NULL
                  END)
             );
      

      【讨论】:

      • alex poole 的答案更好,因为它建议您使用用户定义的函数,以避免在查询的 where 子句中编写相同的复杂“case when”以允许 oracle 使用索引
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-14
      • 1970-01-01
      相关资源
      最近更新 更多