Oracle

使用 XPATH extractValue 的基於函式的索引

  • September 16, 2015
// Oracle Version 11.2.0.4  Standard Edition
// OS RedHat

我正在嘗試基於使用 XML 對像在大表上創建函式索引。

所以我做了一些事情,比如:

CREATE INDEX test_function_itemgroup_id 
ON test_function 
( 
   extractValue
   (
       RECORD, 
       '/Product/PublishingDetail/ItemGroup/ItemGroupId', 
       'xmlns="http://www.editeur.org/onix/3.0/reference"' 
   )
)

因此,當我有如下查詢時:

select PRODUCT_ID 
from TEST_FUNCTION pr
where 
   extractValue
   ( 
       RECORD, 
       '/Product/PublishingDetail/ItemGroup/ItemGroupId',
       'xmlns="http://www.editeur.org/onix/3.0/reference"' 
   ) = 'NAME-CLASS'

使用了函式索引,我知道我有正確的 xml 命名空間。

當我做以下事情時:

CREATE INDEX test_function_nbd 
ON test_function 
( 
   extractValue
   ( 
       RECORD, 
       '/Product/Extras[ExtrasIDType="RECORDCREATEDBY"]/IDValue',
       'xmlns="http://www.editeur.org/onix/3.0/reference"' 
   )
)

select PRODUCT_ID 
from TEST_FUNCTION pr 
where 
   extractValue
   ( 
       RECORD, 
       '/Product/Extras[ExtrasIDType="RECORDCREATEDBY"]/IDValue',
       'xmlns="http://www.editeur.org/onix/3.0/reference"' 
   ) = 'Other'  

系統執行全表掃描,而不是使用函式索引。

我使用了一個 XMLIndex,但是它的性能太慢了,並且似乎不允許使用基於函式的索引。

有沒有人對如何進行此操作有任何建議?

更新:

添加了完整的 xmlns,因為現在有足夠的聲譽

表上沒有其他索引。

看起來優化器似乎正在將 XPATH 轉換為兩個不同的查詢。

/Product/Extras/ExtrasIDType 和 /Product/Extras/IDValue

沒有被函式索引。

在我的特殊情況下,我不能單獨索引這些,因為它們可以有多個值,並且基於函式的索引不能使用多個值。

此外,如果我必須使用兩個基於函式的索引,系統將進行全表掃描。(根據 oracle 11 文件)

使用/*+ NO_XML_QUERY_REWRITE */提示。我的以下環境11.2.0.3.0Linux_x86-64平台上,Enterprise Edition.

create table xml_table (c1 xmltype) pctfree 99;

( PCTFREE 99,只是將表分散到許多塊上的一個簡單技巧,從而使全表掃描更加昂貴和低效。在這種情況下為 1 行/塊。)

insert into xml_table select 
xmltype('
 <stuff>
   <something>
     <x1>' || rownum || '</x1>
     <x2>' || rownum || '</x2>
   </something>
 </stuff>
')
from dual connect by level <= 10000;
commit;

所以我有一個包含 10000 行的表,其 XMLType 值如下:

<stuff>
  <something>
     <x1>1</x1>
     <x2>1</x2>
  </something>
</stuff>

<stuff>
  <something>
     <x1>2</x1>
     <x2>2</x2>
  </something>
</stuff>

...

我想執行這樣的查詢,比如說:

select * from xml_table 
where extractvalue(c1, '/stuff/something[x1=5]/x2/text()') = '5';

創建基於函式的索引並收集統計資訊:

create index xml_table_fbi1 on 
 xml_table(extractvalue(c1, '/stuff/something[x1=5]/x2/text()'));

exec dbms_stats.gather_table_stats(user, 'XML_TABLE', no_invalidate=>false);

執行查詢,看看發生了什麼:

alter session set statistics_level=all;

select * from xml_table where extractvalue(c1, '/stuff/something[x1=5]/x2/text()') = '5';

C1
--------------------
<stuff>
 <something>
   <x1>5</x1>
   <x2>5</x2>
 </something>
</stuff>

檢查計劃和執行時統計資訊:

select * from table(dbms_xplan.display_cursor(format=>'allstats last cost'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID  a78z6npnp94va, child number 0
-------------------------------------
select * from xml_table where extractvalue(c1,
'/stuff/something[x1=5]/x2/text()') = '5'

Plan hash value: 3307077916

---------------------------------------------------------------------------------------------------------
| Id  | Operation            | Name      | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |      1 |        |   226K(100)|      1 |00:00:00.43 |   10239 |
|*  1 |  FILTER              |           |      1 |        |            |      1 |00:00:00.43 |   10239 |
|   2 |   TABLE ACCESS FULL  | XML_TABLE |      1 |  10000 |  2764   (2)|  10000 |00:00:00.04 |   10239 |
|   3 |   SORT AGGREGATE     |           |  10000 |      1 |            |  10000 |00:00:00.37 |       0 |
|   4 |    NESTED LOOPS SEMI |           |  10000 |    667K|   223K  (1)|      1 |00:00:00.28 |       0 |
|   5 |     XPATH EVALUATION |           |  10000 |        |            |  10000 |00:00:00.12 |       0 |
|   6 |     XPATH EVALUATION |           |  10000 |        |            |      1 |00:00:00.14 |       0 |
---------------------------------------------------------------------------------------------------------

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

  1 - filter(SYS_XMLTYPE2SQL(SYS_XQSEQ2CON(SYS_XQEXTRACT(,'/something/x2/text()')))
             ='5')

數據庫轉換了 XPath 表達式,因此基於函式的索引不再是一個有效的候選對象,因此它是一個具有超過 10000 次緩衝區獲取的全表掃描。

在使用上述提示執行時:

select /*+ NO_XML_QUERY_REWRITE */ * from xml_table where
extractvalue(c1, '/stuff/something[x1=5]/x2/text()') = '5';

C1
--------------------
<stuff>
 <something>
   <x1>5</x1>
   <x2>5</x2>
 </something>
</stuff>

計劃和執行時統計:

select * from table(dbms_xplan.display_cursor(format=>'allstats last cost'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------
SQL_ID  gby3tfs2z7mqm, child number 0
-------------------------------------
select /*+ NO_XML_QUERY_REWRITE */ * from xml_table where
extractvalue(c1, '/stuff/something[x1=5]/x2/text()') = '5'

Plan hash value: 2259369741

---------------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |      1 |        |     2 (100)|      1 |00:00:00.01 |       2 |
|   1 |  TABLE ACCESS BY INDEX ROWID| XML_TABLE      |      1 |      1 |     2   (0)|      1 |00:00:00.01 |       2 |
|*  2 |   INDEX RANGE SCAN          | XML_TABLE_FBI1 |      1 |      1 |     1   (0)|      1 |00:00:00.01 |       1 |
---------------------------------------------------------------------------------------------------------------------


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

  2 - access("XML_TABLE"."SYS_NC00003$"='5')

好多了。

還要注意計劃中的成本列。第一個計劃的成本是 226K,但是禁用 XML 重寫後,第二個計劃的成本是 2。我沒有深入研究這個主題,但似乎這是另一種查詢轉換,不是基於成本。

引用自:https://dba.stackexchange.com/questions/114383