是否可以为视图中转换为 DATE 的 TIMESTAMP 列构建基于 Oracle 函数的索引?

ORA*_*378 5 database oracle indexing casting view

我已经在 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;
Run Code Online (Sandbox Code Playgroud)

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

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]
Run Code Online (Sandbox Code Playgroud)

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

改变的观点:

SELECT
TIMESTAMP_COLUMN
FROM TEST_USER.SAMPLE_TABLE;
Run Code Online (Sandbox Code Playgroud)

相同的查询:

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]
Run Code Online (Sandbox Code Playgroud)

这是在没有强制转换函数的情况下使用的普通索引 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" ;
Run Code Online (Sandbox Code Playgroud)

这是我无法调用的基于函数的索引 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" ;
Run Code Online (Sandbox Code Playgroud)

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

谢谢你。

编辑 #1

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

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

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

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

Ale*_*ole 1

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

\n\n
select * FROM SAMPLE_VIEW\nWHERE cast(TIMESTAMP_COLUMN as timestamp) = timestamp \'2010-08-10 12:00:00\';\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n
explain plan for\nselect * FROM SAMPLE_VIEW\nWHERE TIMESTAMP_COLUMN = cast(timestamp \'2010-08-10 12:00:00\' as date);\n\nselect plan_table_output\nfrom table(dbms_xplan.display(\'plan_table\',null,\'all\'));\n\n----------------------------------------------------------------------------------------                                                                                                                                                                                                                     \n| Id  | Operation        | Name                | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                                                                                                                                     \n----------------------------------------------------------------------------------------                                                                                                                                                                                                                     \n|   0 | SELECT STATEMENT |                     |     1 |     9 |     1   (0)| 00:00:01 |                                                                                                                                                                                                                     \n|*  1 |  INDEX RANGE SCAN| TIMESTAMP_FBI_IDX   |     1 |     9 |     1   (0)| 00:00:01 |                                                                                                                                                                                                                     \n----------------------------------------------------------------------------------------                                                                                                                                                                                                                     \n\nQuery Block Name / Object Alias (identified by operation id):                                                                                                                                                                                                                                                \n-------------------------------------------------------------                                                                                                                                                                                                                                                \n\n   1 - SEL$F5BB74E1 / SAMPLE_TABLE@SEL$2                                                                                                                                                                                                                                                                     \n\nPredicate Information (identified by operation id):                                                                                                                                                                                                                                                          \n---------------------------------------------------                                                                                                                                                                                                                                                          \n\n   1 - access(CAST(INTERNAL_FUNCTION("TIMESTAMP_COLUMN") AS                                                                                                                                                                                                                                                  \n              date)=CAST(TIMESTAMP\' 2010-08-10 12:00:00.000000000\' AS date))                                                                                                                                                                                                                                 \n\nColumn Projection Information (identified by operation id):                                                                                                                                                                                                                                                  \n-----------------------------------------------------------                                                                                                                                                                                                                                                  \n\n   1 - CAST(INTERNAL_FUNCTION("TIMESTAMP_COLUMN") AS date)[DATE,7]      \n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
WHERE TIMESTAMP_COLUMN = to_date(\'2010-08-10 12:00:00\', \'YYYY-MM-DD HH24:MI:SS\');\n
Run Code Online (Sandbox Code Playgroud)\n\n

您说您正在使用的应用程序仅支持日期,因此想必您无论如何都可以传递正确的日期值;尽管我对您所说的“然后我必须使用时间戳函数查询此转换列”感到困惑,并且应用程序在它生成的查询中指定了时间戳文字,这似乎并不相关。

\n\n

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

\n\n
CREATE OR REPLACE VIEW SAMPLE_VIEW AS\nSELECT TIMESTAMP_COLUMN,\n  CAST(SAMPLE_TABLE.TIMESTAMP_COLUMN as DATE) as DATE_COLUMN\nFROM TEST_USER.SAMPLE_TABLE;\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
WHERE TIMESTAMP_COLUMN = timestamp \'2010-08-10 12:00:00\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者

\n\n
WHERE DATE_COLUMN = to_date(\'2010-08-10 12:00:00\', \'YYYY-MM-DD HH24:MI:SS\')\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

作为另一个潜在的替代方案,如果您可以更改从仅日期层发送的查询,您可以将其转换为时间戳并直接查询表:

\n\n
select * FROM SAMPLE_TABLE\nWHERE TIMESTAMP_COLUMN =\n  cast(to_date(\'2010-08-10 12:00:00\', \'YYYY-MM-DD HH24:MI:SS\') as timestamp);\n
Run Code Online (Sandbox Code Playgroud)\n\n

..但这取决于查询的构造方式以及您如何提供日期值。但在这种情况下,你不需要视图或联邦调查局。

\n