首页 | 摘要显示 | 上一页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 下一页

DBA Archives

June 6, 2007

OR引起的性能问题, 在表上进行行过滤

    对SCOTT用户EMP表的DEPTNO和ENAME字段建了一个复合索引. 在SQL的WHERE子句中用了一个OR条件, 看Plan:

SQL> SELECT /*+ first_rows no_expand */ * FROM EMP
   2 WHERE deptNO=10 AND (ENAME IS NULL OR ENAME > 'A');

---------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes |
---------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     3 |   330 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| EMP     |     3 |   330 |
|*  2 |   INDEX RANGE SCAN          | IDX_EMP |     3 |       |
---------------------------------------------------------------

   1 - filter("ENAME" IS NULL OR "ENAME">'A')
   2 - access("DEPTNO"=10)

    将SQL语句中的OR条件去掉, 再看Plan:

SQL> SELECT /*+ first_rows no_expand */ * FROM EMP
   2 WHERE deptNO=10 AND NVL(ENAME,'B') > 'A';

---------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes |
---------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     3 |   330 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP     |     3 |   330 |
|*  2 |   INDEX RANGE SCAN          | IDX_EMP |     1 |       |
---------------------------------------------------------------

   2 - access("DEPTNO"=10)
       filter(NVL("ENAME",'B')>'A')

    其实在真实的案例中, 记录中ENAME字段已经没有空值了.

June 28, 2007

手工删除DBA_TABLESPACES中的记录后...

    最初是在ITPub上看到人家这样的误操作的, 估计谁也没有遇到过这样的事, 不好作出回答. 今天我在自已的机器上试了一下, 10g的数据库, 如下所示:

SQL> delete from dba_tablespaces where tablespace_name='USERS';

1 row deleted.

SQL> commit;

Commit complete.

SQL> alter system checkpoint;

System altered.

    决定重启一下数据库, 之前当心打不开数据库, 结果数据库还是能打开的, 这样就好多了. 当去查询这个表空间上的数据时, 出现了600错误, 如下所示:

SQL> select * from anysql.emp;
select * from anysql.emp
                     *
ERROR at line 1:
ORA-00600: internal error code, arguments: [5119], [], [], [], [], [], [], []

    然后我用AUL找回了被删除的记录, 用sqlldr装载到数据库中, 重新启动, 没有遇到任何问题, 再去查这个表空间的记录, 也没有问题. 当然也可以用Log Miner或MyLOG去找回被删除的记录.

July 4, 2007

为什么到二十一世纪还要改sys.props$呢?

    很久没有人去改sys.props$表了, 今天却出了一个, 在9i以前的话, 改错了NLS_CHARACTERSET的值是不行的, 数据库就起不来了. 但到了9i后, 是可以的, 也许有人知道了这一点所以放心地去做了. 为了慎重起见, 我重做了如下实验:

SQL> UPDATE PROPS$ SET VALUE$='WE8ISO8859PP'
  2  WHERE NAME='NLS_CHARACTERSET';

1 row updated.

    单改这一个值是可以起来的, 接下来我改其他所有的值呢?

SQL> UPDATE PROPS$ SET VALUE$='WE8ISO8859PP';

27 rows updated.

    这样改了就是起不来的, 另外eygle在同一时间测试, 发现单改错了NLS_NCHAR_CHARACTERSET就不行了.

SQL> UPDATE PROPS$ SET VALUE$='WE8ISO8859PP'
  2  WHERE NAME='NLS_NCHAR_CHARACTERSET';

1 row updated.

    难道用一下"ALTER DATABASE CHARACTER SET INTERNAL_USE xxx"语句就如此困难吗? 不就是要重起一两次吗? 说实话, 不要去学一些危险的知识, 所以我坚绝不学习如何使用bbed这样的工具, 我只读取数据文件.

July 11, 2007

通过改写SQL来优化性能, 如何做?

    网友说这个SQL语句跑了两上小时, 没有什么思路去解决.

select tbilltrace_43.bno,tbilltrace_43.SAVETIMESTAMP
from
   ( select bno,SAVETIMESTAMP
     from tbilltrace
     where  SAVETIMESTAMP>=monitor_start_date
       and SAVETIMESTAMP<=monitor_end_date and opcode=43
   ) tbilltrace_43
where not exists
(select bno
   from ( select bno from tbilltrace
          where SAVETIMESTAMP>=monitor_start_date  
            and SAVETIMESTAMP<=monitor_end_date and opcode=50
          union all
          select bno from tbilltrace
          where SAVETIMESTAMP>=monitor_start_date
            and SAVETIMESTAMP<=monitor_end_date and opcode=51
        ) tbilltrace_51_50
where tbilltrace_51_50.bno=tbilltrace_43.bno);  

    SQL语句的性能和数据量有关, 首先需要知道第一个子查询会返回多少条记录, 其次中间的那个UNION ALL应当折开成两个Not Exists, 我认为在opcode或SAVETIMESTAMP列上的索引应当不是很有效的, 而bno列上的索引应当是很有效的. 因此改写后的SQL应当如下:

select bno,SAVETIMESTAMP
     from tbilltrace tbilltrace_43
     where  SAVETIMESTAMP>=monitor_start_date
       and SAVETIMESTAMP<=monitor_end_date and opcode=43
where not exists
  (select 1 from tbilltrace tbilltrace_50
          where SAVETIMESTAMP>=monitor_start_date  
            and SAVETIMESTAMP<=monitor_end_date and opcode=50
            and bno = tbilltrace_43.bno)
  and not exists
  (select 1 from tbilltrace tbilltrace_51
          where SAVETIMESTAMP>=monitor_start_date  
            and SAVETIMESTAMP<=monitor_end_date and opcode=51
            and bno = tbilltrace_43.bno)

    如果不考虑空间的情况, 则建以下的两个索引:

Index 1 (opcode, savetimestamp, bno)
Index 2 (bno, opcode, savetimestamp)

    这样的Tuning其实很简单的.

July 17, 2007

问题解答 -- 如何得知一张表的纪录有变化?

    网上有一个问题, 要想知道那些表记录发生了变化? 对于一些更新不是很多(一秒种有几百条上下的)的表, 我会建议用实体化视图日志(Materialize View Log/Snapshot Log)去实现. 其基本原理是能过一个内部(Internal)的内核(Kernal)一级的行级触发器(Row Level Trigger), 将变化过的记录的ROWID或主键值保存到另一个表中去. 实体化视图的增量刷新(Fast Refresh)正是基于这个技术.

    假设我们需要监控的表名为T_OBJECTS, 主键为OBJECT_ID, 如果没有主键则用ROWID, 则可以用如下语句来创建实体化视图日志.

-- Using Primary Key
CREATE MATERIALIZED VIEW LOG ON T_OBJECTS
  TABLESPACE users WITH PRIMARY KEY, SEQUENCE;

-- Using ROWID
CREATE MATERIALIZED VIEW LOG ON T_OBJECTS
  TABLESPACE users WITH ROWID, SEQUENCE;

    这时会在Oracle中生成一个MLOG$_T_OBJECTS的表, 包含如下有用字段:

SEQUENCE$$ -- Operation Order
DMLTYPE$$  -- Operation Type, U=Update/I=Insert/D=Delete
M_ROW$$    -- ROWID of affected row,
OBJECT_ID  -- The primary key values in this case

    我用ROWID类型的做了实验, 先做了一条Insert, 再做了一条Delete, 然后查询日志表记录:

SQL> SELECT SEQUENCE$$, M_ROW$$, DMLTYPE$$ FROM MLOG$_T_OBJECTS;

SEQUENCE$$ M_ROW$$              D
---------- -------------------- -
         1 AAACYhAAEAAAEFvAAA   I
         2 AAACYhAAEAAACOMAAA   D

    更复杂的例子就自已去设计了. 如果搞懂了这些, 还可以自已写角本从Oracle向其他的数据库进行数据复制呢.

上一页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 下一页

当前分类: DBA

Creative Commons License
本站版权: 共用创作 CC
署名-非商业性-相同方式分享
本站基于MT-3.36免费版
(©)版权所有, 2004 - 2008, www.AnySQL.net, 保留所有权利.
MSN: loufangxin(a)msn.com, Mail: anysql(at)126.com/support(at)iamdba.com, Skype ID:anysql