11

我创建了一个 Oracle Text 索引,如下所示:

create index my_idx on my_table (text) indextype is ctxsys.context; 

然后我可以执行以下操作:

select * from my_table where contains(text, '%blah%') > 0;

但是假设我们在这个表中有另一列,比如group_id,我想改为执行以下查询:

select * from my_table where contains(text, '%blah%') > 0 and group_id = 43;

使用上述索引,Oracle 将不得不搜索所有包含 的项目,'blah'然后检查它们的所有group_ids。

理想情况下,我宁愿只搜索带有 的项目group_id = 43,所以我想要一个这样的索引:

create index my_idx on my_table (group_id, text) indextype is ctxsys.context; 

有点像普通索引,因此可以为每个group_id.

有没有办法在 Oracle 中做这样的事情(如果这很重要,我正在使用 10g)?

编辑(澄清)

考虑一个有 100 万行和以下两列的表,A以及B,都是数字。假设有 500 个不同的值A和 2000 个不同的值B,并且每一行都是唯一的。

现在让我们考虑select ... where A = x and B = y

据我所知,一个单独的索引在 上A进行索引搜索,这将返回 500 个不同的行,然后对这些行进行连接/扫描。在任何情况下,至少要查看 500 行(除了数据库很幸运并尽早找到所需的行。BB

虽然索引(A,B)更有效,但它会在一次索引搜索中找到一行。

放置单独的索引group_id和我认为的文本只会给查询生成器留下两个选项。

(1) 使用group_id索引,并扫描所有结果行中的文本。
(2) 使用文本索引,并扫描所有结果行以查找group_id.
(3) 使用两个索引,并进行连接。

而我想要:

(4) 使用(group_id, "text")索引查找特定下的文本索引,group_id并扫描该文本索引以查找我需要的特定行/行。不需要扫描和检查或加入,就像在(A,B).

4

4 回答 4

8

甲骨文文本

1 - 您可以通过使用FILTER BY创建 CONTEXT 索引来提高性能:

create index my_idx on my_table(text) indextype is ctxsys.context filter by group_id;

在我的测试中,这filter by确实提高了性能,但在 group_id 上使用 btree 索引仍然稍微快一些。

2 - CTXCAT 索引使用“子索引”,看起来与多列索引类似。这似乎是您正在寻找的选项 (4):

begin
  ctx_ddl.create_index_set('my_table_index_set');
  ctx_ddl.add_index('my_table_index_set', 'group_id');
end;
/

create index my_idx2 on my_table(text) indextype is ctxsys.ctxcat
    parameters('index set my_table_index_set');

select * from my_table where catsearch(text, 'blah', 'group_id = 43') > 0

这可能是最快的方法。对类似于 A 和 B 场景的 120MB 随机文本使用上述查询只需要 18 次一致的获取。但不利的一面是,创建 CTXCAT 索引花费了将近 11 分钟并使用了 1.8GB 的​​空间。

(注意:Oracle Text 在这里似乎可以正常工作,但我对 Text 不熟悉,我无法判断这不是像@NullUserException 所说的那样不恰当地使用这些索引。)

多列索引与索引连接

对于您在编辑中描述的情况,通常在 (A,B) 上使用索引和在 A 和 B 上加入单独的索引之间没有显着差异。我使用类似于您描述的数据和索引构建了一些测试join 只需要 7 个一致的获取,而多列索引需要 2 个一致的获取。

这样做的原因是因为 Oracle 以块的形式检索数据。一个块通常是 8K,并且一个索引块已经排序,因此您可能可以将 500 到 2000 个值放在几个块中。如果您担心性能,通常读取和写入块的 IO 是唯一重要的事情。Oracle 是否必须将几千行连接在一起是一个无关紧要的 CPU 时间。

但是,这不适用于 Oracle Text 索引。您可以将 CONTEXT 索引与 btree 索引(“位图和”?)连接起来,但性能很差。

于 2011-09-16T04:35:18.917 回答
1

我会放一个索引group_id,看看这是否足够好。您没有说我们正在谈论多少行或您需要什么性能。

请记住,处理谓词的顺序不一定是您在查询中编写它们的顺序。除非您有真正的理由,否则不要试图智取优化器。

于 2011-09-10T14:53:07.067 回答
1

简短版:没有必要这样做。查询优化器足够聪明,可以决定选择数据的最佳方式。只需在 上创建一个 btree 索引group_id,即:

CREATE INDEX my_group_idx ON my_table (group_id); 

长版:我创建了一个testperf.sql插入 136 行虚拟数据的脚本 ( )。

DESC my_table;

Name     Null     Type      
-------- -------- --------- 
ID       NOT NULL NUMBER(4) 
GROUP_ID          NUMBER(4) 
TEXT              CLOB      

上有一个 btree 索引group_id。为确保实际使用索引,请以 dba 用户身份运行:

EXEC DBMS_STATS.GATHER_TABLE_STATS('<YOUR USER HERE>', 'MY_TABLE', cascade=>TRUE);

这是每个有多少行group_id和相应的百分比:

GROUP_ID               COUNT                  PCT                    
---------------------- ---------------------- ---------------------- 
1                      1                      1                      
2                      2                      1                      
3                      4                      3                      
4                      8                      6                      
5                      16                     12                     
6                      32                     24                     
7                      64                     47                     
8                      9                      7         

请注意,查询优化器仅在认为这是一个好主意时才会使用索引——也就是说,您要检索的行数达到一定百分比。因此,如果您要求它提供以下查询计划:

SELECT * FROM my_table WHERE group_id = 1;
SELECT * FROM my_table WHERE group_id = 7;

您会看到,对于第一个查询,它将使用索引,而对于第二个查询,它将执行全表扫描,因为在group_id = 7.

现在,考虑一个不同的条件 - WHERE group_id = Y AND text LIKE '%blah%'(因为我不是很熟悉ctxsys.context)。

SELECT * FROM my_table WHERE group_id = 1 AND text LIKE '%ipsum%';

查看查询计划,您会看到它将使用group_id. 请注意,条件的顺序并不重要:

SELECT * FROM my_table WHERE text LIKE '%ipsum%' AND group_id = 1;

生成相同的查询计划。如果您尝试在 上运行相同的查询group_id = 7,您将看到它返回到全表扫描:

SELECT * FROM my_table WHERE group_id = 7 AND text LIKE '%ipsum%';

请注意,Oracle 每天都会自动收集统计信息(计划在每晚和周末运行),以不断提高查询优化器的效率。简而言之,Oracle 会尽最大努力优化优化器,因此您不必这样做。

于 2011-09-11T16:49:59.070 回答
0

我手头没有要测试的 Oracle 实例,也没有使用 Oracle 中的全文索引,但我在使用内联视图时通常具有良好的性能,这可能是您想到的那种索引的替代方案。当涉及contains()时,以下语法是否合法?

此内联视图为您提供第 43 组中的行的 PK 值:

             (
             select T.pkcol
             from T
             where group = 43
             )

如果 group 有一个正常的索引,并且没有低基数,那么获取这个 set 应该很快。然后,您将再次使用 T 内部加入该集合:

           select * from T
           inner join
            (
             select T.pkcol
             from T
             where group = 43
             ) as MyGroup

           on T.pkcol = MyGroup.pkcol
           where contains(text, '%blah%') > 0

希望优化器能够使用 PK 索引来优化连接,然后将包含谓词仅应用于组 43 行。

于 2011-09-10T12:41:20.553 回答