1

我们正在运行 Oracle 数据库 11g 版本 11.2.0.2.0 - 64 位生产/

我们有以下查询,它是用户、user_learning 和 user_group 表之间的连接。

select u.user_id, u.first_name, u.surname, u.client_company_id, u.username,   
       ul.completion_status 
from user u, user_learning ul 
where 
      u.user_id = ul.user_id (+) 
  and (ul.enrolment_status = 'E'   or ul.enrolment_status is null) 
  and upper(u.surname) like 'CART%'
  and ((u.user_id is not null  and (u.client_company_id in ('ABCDEF') )  
       and exists (select 1 
                   from user_group g
                   where g.user_id = u.user_id 
                     and g.group_id in  
                         (215479,215480,221934,39901,45709,45710,45712,
                         45713,45714,45715,45716,45717,45718)
                   ) 
       ) 
       or (u.user_id = 1209289 or u.manager_id = 1209289)
      )
  order by u.client_company_id, u.surname, u.first_name, u.user_id;

此查询每次交替运行时给出 0 个结果和 198 个结果,有时返回 0 等。

一些关于表格的背景信息以及我们迄今为止所做的尝试,

-- Gives 1184415 records
select count(1) from user; 

-- Gives 7789332 records
select count(1) from user_learning; 

-- Gives 3278032
select count(1) from user_group; 

用户表列 surname 上有一个索引,如下 CREATE INDEX IDX_USER_UPPER ON USER (UPPER("SURNAME")) USER_GROUP 包含 group_id 和 user_id 作为 PRIMARY KEY user_id 是 USER 表中的 PRIMARY KEY

我们注意到的是,当我们删除 IDX_USER_UPPER 索引并运行查询时,结果始终是一致的。但是拥有索引意味着一段时间后的结果是不一样的。我们对索引存有疑问,删除它并运行查询似乎会产生一致的结果,但这最多有一段时间,然后查询再次停止给出一致的结果。我们将 'CART%' 之类的大写(u.surname)更改为 'CAR%' 或 'CARTE%' 等......然后它慢慢开始表现得很奇怪,给出 0 条或更多条记录等。

解释计划指示何时使用索引,有时不使用索引。所以也许这个索引不是问题......??

我尝试收集索引的统计信息并重建索引

CREATE INDEX IDX_USER_UPPER ON USER (UPPER("SURNAME")) 
alter index IDX_USER_UPPER compute statistics
alter index IDX_USER_UPPER rebuild 

也运行了以下但仍然相同的效果,除了删除索引时它似乎工作了一段时间。

exec DBMS_STATS.gather_table_stats('SCHEMA', 'USER');
exec DBMS_STATS.gather_table_stats('SCHEMA', 'USER_LEARNING');
exec DBMS_STATS.gather_table_stats('SCHEMA', 'USER_GROUP');

exec DBMS_STATS.GATHER_INDEX_STATS ( 'SCHEMA', 'IDX_USER_UPPER');
-- note SCHEMA is our schema name

-- indicates Last_analysed is updated ...
select table_name, owner, to_char(last_analyzed, 'dd-mon-yyyy hh24:mi:ss') 
from   dba_tables 
where table_name IN ('USER', 'USER_LEARNING', 'USER_GROUP')

我尝试的另一个选项是设置“_no_or_expansion”标志,如下所示,

alter session set "_no_or_expansion"=true;
-- no impact to the query though

(有时看起来当索引被删除时,查询会在 'CAR%'、"CART%'、'CARTE%'、'CARTER%' 被查询时不断返回一致的数据 - 奇怪)

但是奇怪的行为仍然存在任何想法或问题或解决方案,如果你们中的任何人遇到过类似的事情,请。


运行 select * from dba_autotask_client 显示
自动优化器统计信息收集已启用

4

4 回答 4

1

听起来像是有问题的行为,但要非常小心,不要使用任何具有不确定行为的视图/函数(即以某种方式是有状态的,因此它们并不总是使用相同的输入返回相同的答案)。

我会看看您的查询正在使用的执行计划:可能是使用了不同的执行计划,并且这些计划给出了不同的结果。他们不应该这样做,除非您搞砸了并且您的查询是不确定的,但如果是这样,您将能够确定这一点并向 Oracle 投诉。

一种方法是运行您的查询,然后运行

select * from table(dbms_xplan.display_cursor(format=>'advanced'));

应该显示会话中运行的最后一件事的执行计划,但遗憾的是,它不适用于 PL/SQL 开发人员或其他静默运行查询以获取 DBMS_OUTPUT 等的工具(“最后一个”查询运行在你的会话中不会是你给它的那个)

否则做类似的事情

select * from v$sql where lower(sql_fulltext) like '%client_company_id%';

...在 like 子句中放置与您的查询文本非常有区别的内容 - 我选择了 client_company_id 作为示例,但我不知道这在您的查询中有多常见。无论哪种方式,通过一些努力,您应该能够看到 v$sql 中与您的查询相对应的行,并找到 sql_id。这是 sql 文本的哈希,因此相同的查询文本(直到空格等)将始终产生相同的 sql_id。

然后你可以做 select * from table(dbms_xplan.display_cursor(, 0, format=>'advanced');

如果您在 v$sql 中有多个具有相同 sql_id 的行,那么这是一个强有力的指标,表明已经为您的查询生成了多个执行计划。游标将具有相同的 sql_id 但不同的“child_number”。

select * from table(dbms_xplan.display_cursor(<sql id>, <child number>, format=>'advanced');

查看是否正在使用不同的执行计划。特别注意“注释”部分中的“用于此语句的基数反馈”或“用于此语句的统计反馈”。

基数反馈是指优化器知道它在生成执行计划时做出了可疑估计,因此观察运行时行为以生成校正因子,然后将其插入到该查询的下一次执行以生成一个整体新计划。

它在工作时非常聪明,但也引起了相当多的头痛。

无论哪种方式,如果您遇到了一个错误,即不同的执行计划产生不同的结果,那么这将是执行计划(以及结果)可能在执行之间发生变化的一种方式。

如果“坏”计划涉及在 UPPER(...) 上使用您的索引,则删除该索引将阻止生成这些计划,因此再次更改行为。

于 2016-08-24T16:01:29.330 回答
0

我从未见过将列参数作为字符串的基于函数的索引:

CREATE INDEX IDX_USER_UPPER ON USER (UPPER("SURNAME"))

试试这个:

CREATE INDEX IDX_USER_UPPER ON USER (UPPER(SURNAME))
于 2015-03-11T16:48:55.157 回答
0

这对我有用

SELECT COUNT(*)
FROM
(SELECT DISTINCT A, B, C FROM [TABLE_NAME])
于 2017-03-09T04:51:49.280 回答
0

注意帕维尔的回答

UPPER("SURNAME") 应该与 UPPER(SURNAME) 相同。您可以在 Oracle 中使用非大写标识符(列名、表名等) - 甚至标点符号 -只要您将标识符括在“双引号”中

如果您在 Oracle 中使用例如 DBMS_METADATA 和类似的 API,您会经常发现它在所有内容中都加了双引号,只是为了避免歧义。

于 2016-08-24T16:09:18.877 回答