【问题标题】:Is it possible to build a Oracle Function Based Index for a TIMESTAMP column cast as DATE within a view?是否可以为视图中转换为 DATE 的 TIMESTAMP 列构建基于 Oracle 函数的索引?
【发布时间】:2014-10-15 19:57:33
【问题描述】:

我在 Oracle 11g 文档和论坛中搜索了有关如何获取此视图和查询以使用基于函数的索引的示例,但到目前为止我还没有找到。感谢您的帮助。

我使用的应用程序不支持直接查询的 TIMESTAMP 数据类型,但如果它在视图中转换为 DATE,则支持该数据类型。但是,我必须使用时间戳函数查询这个转换列。这有点像往返。第 3 方应用程序发送我在 SQL*Plus/SQL Developer 中使用的相同查询,并显示在下面。

我实际上并不需要 TIMESTAMP 数据类型的小数秒粒度,只是出于数据供应商的原因需要在此数据库中使用它。

我了解在将函数应用于该列时不使用列索引。我无法完成的是构建一个适当的基于函数的索引,以避免对数百万行进行全表扫描(根据表,在 200 万到 6 亿行之间)。我不想使用提示,但在这一点上,任何事情都比全表扫描更好,我愿意接受所有建议。

这是所需的视图:

SELECT
CAST(SAMPLE_TABLE.TIMESTAMP_COLUMN as DATE) as TIMESTAMP_COLUMN
FROM TEST_USER.SAMPLE_TABLE;

这是一个带有解释计划前缀的示例查询:

explain plan for
select * FROM SAMPLE_VIEW WHERE TIMESTAMP_COLUMN = timestamp '2010-08-10 12:00:00';

select plan_table_output
from table(dbms_xplan.display('plan_table',null,'all'));
-----------------------------------------------------------------------------------------------------
| Id  | Operation            | Name         | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |              |   200K|  1562K|   169K  (1)| 00:33:56 |       |       |
|   1 |  PARTITION LIST ALL  |              |   200K|  1562K|   169K  (1)| 00:33:56 |     1 |     6 |
|   2 |   PARTITION RANGE ALL|              |   200K|  1562K|   169K  (1)| 00:33:56 |     1 |    49 |
|*  3 |    TABLE ACCESS FULL | SAMPLE_TABLE |   200K|  1562K|   169K  (1)| 00:33:56 |     1 |   294 |
-----------------------------------------------------------------------------------------------------     
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------    
   1 - SEL$F5BB74E1
   3 - SEL$F5BB74E1 / SAMPLE_TABLE@SEL$2  
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter(INTERNAL_FUNCTION(CAST(INTERNAL_FUNCTION("TIMESTAMP_COLUMN") AS DATE))=TIMESTAMP_COLUMN' 
              2010-08-10 12:00:00.000000000')  
Column Projection Information (identified by operation id):
-----------------------------------------------------------   
   1 - "TIMESTAMP_COLUMN"[TIMESTAMP WITH LOCAL TIME ZONE,11]
   2 - "TIMESTAMP_COLUMN"[TIMESTAMP WITH LOCAL TIME ZONE,11]
   3 - "TIMESTAMP_COLUMN"[TIMESTAMP WITH LOCAL TIME ZONE,11]

当然,只要我从视图中删除 Cast 函数,索引就会按预期使用:

改变视图:

SELECT
TIMESTAMP_COLUMN
FROM TEST_USER.SAMPLE_TABLE;

相同的查询:

explain plan for
select * FROM SAMPLE_VIEW WHERE TIMESTAMP_COLUMN = timestamp '2010-08-10 12:00:00';

select plan_table_output
from table(dbms_xplan.display('plan_table',null,'all'));
------------------------------------------------------------------------------------
| Id  | Operation        | Name              | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                   |     2 |    22 |     3   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| TIMESTAMP_COL_IDX |     2 |    22 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------ 
Query Block Name / Object Alias (identified by operation id):
------------------------------------------------------------- 
   1 - SEL$F5BB74E1 / SAMPLE_TABLE@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------   
   1 - access("TIMESTAMP_COLUMN"=TIMESTAMP' 2010-08-10 12:00:00.000000000')
Column Projection Information (identified by operation id):
-----------------------------------------------------------
   1 - "TIMESTAMP_COLUMN"[TIMESTAMP WITH LOCAL TIME ZONE,11]

这是在没有强制转换函数的情况下使用的普通索引 DDL (TIMESTAMP_COL_IDX)。我使用 SQL Developer 的 GUI 构建了它:

CREATE INDEX "TEST_USER"."TIMESTAMP_COL_IDX" ON "TEST_USER"."SAMPLE_TABLE" ("TIMESTAMP_COLUMN") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "TEST" ;

这是我无法调用的基于函数的索引 DDL,可能写得不正确:

CREATE INDEX "TEST_USER"."TIMESTAMP_FBI_IDX" ON "TEST_USER"."SAMPLE_TABLE" (CAST("TIMESTAMP_COLUMN" AS DATE)) 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "TEST" ;

我已经收集了视图所基于的表的统计信息,但在 11g 中,我认为在构建索引后这不是“必需的”。它没有改变执行计划。

谢谢。

编辑#1

当我说:“但是,我必须使用时间戳函数查询这个转换列。这有点像往返。”...

...我应该澄清我需要使用的整个工作流程,这是导致这种情况的原因。以下是按时间顺序排列的工作流程:

  1. 数据以 TIMESTAMP 格式传送到 Oracle 数据库,并且必须按此格式存储。
  2. Web 发布应用程序不支持 TIMESTAMP 但允许 DATE,因此使用 Cast。
  3. Web 查询应用程序必须查询为 TIMESTAMP 或使用 DATE 的时间戳功能。

这就是我所说的数据“往返”的意思;数据库和 Web 应用程序都使用 TIMESTAMP,但发布到 Web 应用程序的中间件不能,从而导致这种昂贵的转换场景,导致全表扫描。

【问题讨论】:

  • 那么您需要使用时间戳和日期数据类型来查询视图吗?并且视图列必须与两个查询使用的完全相同?
  • 如果您使用日期查询但您在此处提供时间戳,那么基于函数的索引应该可以工作,因此它又是不同的数据类型...

标签: database oracle view casting indexing


【解决方案1】:

正如 Jokke Heikkilä 指出的那样,您正在使用时间戳值进行查询。这会导致比较的左侧(即视图的日期列)隐式转换为与常量相同的数据类型。你正在有效地做:

select * FROM SAMPLE_VIEW
WHERE cast(TIMESTAMP_COLUMN as timestamp) = timestamp '2010-08-10 12:00:00';

...正如您已经指出的那样,将函数应用于列时不使用索引。

如果您将右侧设为日期,则将使用索引:

explain plan for
select * FROM SAMPLE_VIEW
WHERE TIMESTAMP_COLUMN = cast(timestamp '2010-08-10 12:00:00' as date);

select plan_table_output
from table(dbms_xplan.display('plan_table',null,'all'));

----------------------------------------------------------------------------------------                                                                                                                                                                                                                     
| Id  | Operation        | Name                | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                                                                                                                                     
----------------------------------------------------------------------------------------                                                                                                                                                                                                                     
|   0 | SELECT STATEMENT |                     |     1 |     9 |     1   (0)| 00:00:01 |                                                                                                                                                                                                                     
|*  1 |  INDEX RANGE SCAN| TIMESTAMP_FBI_IDX   |     1 |     9 |     1   (0)| 00:00:01 |                                                                                                                                                                                                                     
----------------------------------------------------------------------------------------                                                                                                                                                                                                                     

Query Block Name / Object Alias (identified by operation id):                                                                                                                                                                                                                                                
-------------------------------------------------------------                                                                                                                                                                                                                                                

   1 - SEL$F5BB74E1 / SAMPLE_TABLE@SEL$2                                                                                                                                                                                                                                                                     

Predicate Information (identified by operation id):                                                                                                                                                                                                                                                          
---------------------------------------------------                                                                                                                                                                                                                                                          

   1 - access(CAST(INTERNAL_FUNCTION("TIMESTAMP_COLUMN") AS                                                                                                                                                                                                                                                  
              date)=CAST(TIMESTAMP' 2010-08-10 12:00:00.000000000' AS date))                                                                                                                                                                                                                                 

Column Projection Information (identified by operation id):                                                                                                                                                                                                                                                  
-----------------------------------------------------------                                                                                                                                                                                                                                                  

   1 - CAST(INTERNAL_FUNCTION("TIMESTAMP_COLUMN") AS date)[DATE,7]      

你不必投右边,它只需要一个日期;如果您正在寻找午夜,那么您可以使用日期文字,否则您可以使用 to_date:

WHERE TIMESTAMP_COLUMN = to_date('2010-08-10 12:00:00', 'YYYY-MM-DD HH24:MI:SS');

你有点说你正在使用的应用程序只支持日期,所以大概你可以传递一个适当的日期值;虽然我对你说“我必须使用时间戳函数查询这个强制转换列”感到困惑,并且应用程序在它生成的查询中指定了一个时间戳文字,这似乎没有关系。

如果您需要能够按日期或时间戳数据类型进行查询,那么您可以将两者都包含在您的视图中:

CREATE OR REPLACE VIEW SAMPLE_VIEW AS
SELECT TIMESTAMP_COLUMN,
  CAST(SAMPLE_TABLE.TIMESTAMP_COLUMN as DATE) as DATE_COLUMN
FROM TEST_USER.SAMPLE_TABLE;

然后在相关列中查询您拥有的值的数据类型:

WHERE TIMESTAMP_COLUMN = timestamp '2010-08-10 12:00:00'

WHERE DATE_COLUMN = to_date('2010-08-10 12:00:00', 'YYYY-MM-DD HH24:MI:SS')

第一个将使用timestamp_col_idx,第二个将使用timestamp_fbi_idx。当然,您的选择列表也需要更改才能获得正确的列。

作为另一种可能的选择,如果您可以更改从仅日期层发送的查询,您可以将其转换为时间戳并直接查询表:

select * FROM SAMPLE_TABLE
WHERE TIMESTAMP_COLUMN =
  cast(to_date('2010-08-10 12:00:00', 'YYYY-MM-DD HH24:MI:SS') as timestamp);

.. 但这取决于查询的构建方式以及您提供日期值的方式。不过,在这种情况下,您不需要视图或 FBI。

【讨论】:

  • 感谢您的详细回复...这实际上已经很有帮助了。我将编辑我的原始问题,以澄清我对迄今为止的时间戳的含义,然后作为时间戳进行查询。
  • @ORA-55378 - 更新了对视图的调整,这可能会使具有竞争要求的两个层更容易做到这一点。
猜你喜欢
  • 1970-01-01
  • 2013-12-04
  • 1970-01-01
  • 2023-03-06
  • 2021-03-26
  • 2012-05-22
  • 2021-05-30
  • 1970-01-01
相关资源
最近更新 更多