Hey!! Sky!

Aug 11, 2006

Oracle PL/SQL Programming 读书笔记(Dates and Timestamps)

Dates and Timestamps


1.Datetime Datatypes
    DATE
      存储日期、时间,没有时区,精确到秒,是 9i 之前唯一的 datetime datatype。
    TIMESTAMP [(precision)]
      除了最多精确到 billionth of a second,其他和 DATE 一样。
    TIMESTAMP [(precision)] WITH TIME ZONE
      TIMESTAMP 的基础上,保存时区信息。在内部存储时,Oracle 将时间转换为 UTC 格式进行保存,比如:2002-02-06 20:00:00:00.00 -5:00。
    TIMESTAMP [(precision)] WITH LOCAL TIME ZONE
      不保存时区信息,但会将时间转换为数据库的时区(如果是保存到数据库表的列中)或者 session 的时区(如果保存为 PL/SQL variables)。数据在不同时区间传输时,会进行转换,但不保存时区信息。
    其中 precision 表示秒的小数部分保留的位数,范围为:0~9。
   
    如何选择 Datetime Datatype
      1.如果你要精确到秒的小数位,那么使用 TIMESTAMP
      2.如果要保留 datetime 值的时区,那么使用 TIMESTAMP WITH TIME ZONE
      3.可以用 TIMESTAMP(0) 代替 DATE,但是两者的日期算法是不同的
      4.为了兼容 TIMESTAMP 出现之前的应用程序,那么使用 DATE
      5.PL/SQL 代码中的类型应该和数据库表中的类型相一致,比如将 TIMESTAMP WITH TIME ZONE 的类型存放到 DATE 类型的列中,时区就会丢失
      6.使用 9i 以前的版本,那么只能使用 DATE
      7.将 ADD_MONTHS 这种传统操作 DATE 类型的函数应用到新的 TIMESTAMP 类型上,会产生很大的不同
     


2.获得现在的时间
  Function            Time zone    Datatype returned
  CURRENT_DATE        Session      DATE
  CURRENT_TIMESTAMP   Session      TIMESTAMP WITH TIME ZONE
  LOCALTIMESTAMP      Session      TIMESTAMP
  SYSDATE             Server       DATE
  SYSTIMESTAMP        Server       TIMESTAMP WITH TIME ZONE


  注:返回的都是数据库服务器端的当时时间,单前三者会转换为 session 的时区,可以用 alter session set time_zone 改变
      9i 之前只有 SYSDATE
 


3.Interval Datatypes
    INTERVAL YEAR TO MONTH
      Allows you to define an interval of time in terms of years and months.
      INTERVAL YEAR [(year_precision)] TO MONTH
        year_precision:year 的位数,范围:0~4,默认:2
    INTERVAL DAY TO SECOND
      Allows you to define an interval of time in terms of days, hours, minutes, and seconds (including fractional seconds).
      INTERVAL DAY [(day_precision)] TO SECOND [(frac_sec_prec)]
        day_precision:day 的位数,范围:0~9,默认:2
        frac_sec_prec:秒小数部分保留的位数,范围:0~9,默认:6
       
    Month、hour、minute、second 的精度是不用设置的,Oracle 会保证他们的范围分别在 0~11、0~23、0~59、0~59



4.Datetime Conversions
    Date 范围:
      4712-01-01 B.C. —— 9999-12-31 A.D.
     
    From Strings to Datetimes
      隐式:
        根据 NLS_DATE_FORMAT 的格式写 String 的值,Oracle 会隐式转换,如果和 NLS_DATE_FORMAT 不匹配,不能转换。
      显式(使用内建函数):
        TO_DATE( string[, format_mask[, nls_language]])
        TO_DATE( number[, format_mask[, nls_language]])
          用数字表示 Julian date 转换为 Date 类型,此时 format_mask = 'J',number 表示从 January 1, 4712 B.C. 开始的天数,由于 Oracle 中最大日期是 December 31, 9999 A.D.,所以 number 的范围为:1 ~ 5373484
        TO_TIMESTAMP( string[, format_mask[, nls_language]])
        TO_TIMESTAMP_TZ( string[, format_mask[, nls_language]])
          此函数用于将 string 转换为 TIMESTAMP WITH TIME ZONE、TIMESTAMP WITH LOCAL TIME ZONE
        format_mask:
          默认为 NLS_DATE_FORMAT、NLS_TIMESTAMP_FORMAT、NLS_TIMESTAMP_TZ_FORMAT(分别对应 TO_DATE、TO_TIMESTAMP、TO_TIMESTAMP_TZ)
          对于 TIMESTAMP 类型,秒的小数部分可以用 '.FF' 或者 'XFF' 表示,比如:'mm/dd/yyyy hh:mi:ss.ff AM TZD' or 'mm/dd/yyyy hh:mi:ssxff AM TZD',其中 'X' 由 NLS_NUMERIC_CHARACTERS 的第一个字符决定。
        nls_language:
          Optionally specifies the language to be used to interpret the names and abbreviations of both months and days in the string.
        几个限制:
          1.传给 TO_DATE 中的 string 长度不能超过 220 个字符。
          2.format mask 中 Julian date element (J) 和 the day of year element (DDD) 不能同时出现。
          3.format mask 中 date/time 的某一个部分不能重复出现,比如:'YYYY-YYY-DD-MM'
          4.format mask 中 HH24 不能和 am/pm 同时出现
         
    From Datetimes to Strings
      使用 TO_CHAR,默认格式 'DD-MON-RR'(9 位),可以用 NLS_DATE_FORMAT 覆盖。
      对于 TIMESTAMP 类型,秒的小数部分可以用 FF1 ~ FF9 来表示保留几位(自动四舍五入)。
      不能将用于 TIMESTAMP 的 format_mask 用于 DATE 类型,否则会报 ORA-01821,反过来可以。
     
    Working with Time Zones
      明确表示某一个时区,应该联合使用 TZH TZM 或者 TZR TZD
        TZH:与 UTC 之间 HOUR 的偏移
        TZM:与 UTC 之间 MINUTE 的偏移
        TZR:The time zone region
        TZD:The abbreviated time zone name
        注:后两者可以查看 V$TIMEZONE_NAMES 获得
            对于时区,存入的是什么信息,显示的也是什么信息,比如用 TZH:TZM 存入和 UTC 之间的偏移,就只能显示类似 +08:00 的时区偏移,而无法显示具体哪个 time zone region
       
    二位数年份的处理
      使用 RR/RRRR 时,Oracle 自动根据现在的年份辨别输入的二位数年份:
        1.如果现在是前半世纪(0~49)
          如果输入的是前半世纪(0~49),那么返回本世纪的年份
          如果输入的是后半世纪(50~99),那么返回上世纪的年份
        2.如果现在是后半世纪(50~99)
          如果输入的是前半世纪(0~49),那么返回下世纪的年份
          如果输入的是后半世纪(50~99),那么返回本世纪的年份
      使用 YY/YYYY 时,不管输入的是什么,都返回本世纪的年份
      注:这种自动转换只适用于 String -> Date 的转换,如果是 Date -> String,那就按照 Date 存储的值来转换,此时再用 RR/RRRR 已经没有意义了,因为 Oracle 内部存储的年份是四位数的。
         


5.Date and Timestamp Literals
    这是 9i 之后 引入的 ISO SQL standard 格式,格式是固定的,不能更改,也不受环境变量影响,因此可以作为常量来使用
      DATE 'YYYY-MM-DD'
      TIMESTAMP 'YYYY-MM-DD HH:MI:SS[.FFFFFFFFF] [{+|-}HH:MI]' (HH 必须是 24 小时制的,FFFFFFFFF 可选 1~9 位,也可以没有,时区也可以使用 time zone region(EST),但这只是 Oracle 提供的格式,不是 ANSI/ISO standards)
    例子:
      DATE '2002-02-19'
      TIMESTAMP '2002-02-19 14:00:00.000000000 -5:00';
     
     
6.Interval Conversions
    Numbers to Intervals
      NUMTOYMINTERVAL ( n , 'char_expr' )
      NUMTODSINTERVAL ( n , 'char_expr' )
      char_expr:
      Name       Description
      YEAR       Some number of years, ranging from 1 through 999,999,999
      MONTH      Some number of months, ranging from 0 through 11
      DAY        Some number of days, ranging from 0 to 999,999,999
      HOUR       Some number of hours, ranging from 0 through 23
      MINUTE     Some number of minutes, ranging from 0 through 59
      SECOND     Some number of seconds, ranging from 0 through 59.999999999
      以上不区分大小写
     
    Strings to Intervals
      TO_YMINTERVAL('Y-M')
      TO_DSINTERVAL('D HH:MI:SS')
      格式是固定的,不能缺少任何一个部分
 
 
7.Interval Literals
    语法: INTERVAL 'character_representation' start_element TO end_element
           character_representation 不需要指明所有 datetime 的元素,但必须指明从 start_element 到 end_element 的所有连续元素,如果只有 start_element 只需要一个元素就行了,但 start_element 和 end_element 不能跨越 month 和 day。
           注:由于 bug 的问题,在 9i Releases 1 and 2 和 10g Release 1 中在 pl/sql 中指明部分元素会出错,比如 INTERVAL '1:02' HOUR TO MINUTE,但在 SQL 中不会。
           Oracle 自动会将 high-end value 规格化,比如:INTERVAL '72:15' HOUR TO MINUTE 会规格化为 +03 00:15:00.000000,但 INTERVAL '72:75' HOUR TO MINUTE 会报错,只有 high-end value(这里的 HOUR)会自动转换
          
          
8.CAST and EXTRACT
    两者都是 standard SQL functions
   
    CAST(Oracle8 开始)
      语法:
        CAST(var as type)
        注:在 SQL 中,type 可以指定长度,比如:varchar2(40),但在 pl/sql 中不能指定长度
      可以在 string、datetime(DATE、TIMESTAMP)之间互相转换
      由于无法指定 format,所以取决于 NLS_DATE_FORMAT、NLS_TIMESTAMP_FORMAT、NLS_TIMESTAMP_TZ_FORMAT 环境变量


    EXTRACT(Oracle9i 开始)
      语法:
        EXTRACT (component_name, FROM {datetime | interval})
        component_name(不区分大小写):
        Component name     Return datatype
        YEAR               NUMBER
        MONTH              NUMBER
        DAY                NUMBER
        HOUR               NUMBER
        MINUTE             NUMBER
        SECOND             NUMBER
        TIMEZONE_HOUR      NUMBER
        TIMEZONE_MINUTE    NUMBER
        TIMEZONE_REGION    VARCHAR2
        TIMEZONE_ABBR      VARCHAR2
       
      由于用 to_char 无法格式化 interval 的显示格式(格式是固定的),所以唯一的方法就是使用 EXTRACT 来格式化
 


9.Datetime Arithmetic
    Adding and Subtracting Intervals to/from Datetimes
      1.和 Intervals 做 +- 操作。在和 INTERVAL DAY TO SECOND 进行运算时,不用考虑任何问题,但和 INTERVAL YEAR TO MONTH 做运算时,由于它是直接 +- 原始 datetimes 的年和月,所以可能够产生 ORA-01839 错误,应该有相关的 exception 处理语句。
      2.直接和 number 做 +- 操作。number 表示天数,可以用分数小数表示:
        Value    Expression   Represents
        1/24     1/24         One hour
        1/1440   1/24/60      One minute
        1/86400  1/24/60/60   One second
        注:不建议进行约分等操作,这样使得程序一目了然。
      3.使用 ADD_MONTHS 函数。
          1.如果输入的日期是输入月份中的最后一天,那么结果也是结果月份的最后一天
            比如:
              add_months(Date '2006-02-28',1)  --> 2006-03-31
          2.如果结果月份的最后一天小于输入月份的日子,那么结果日期是结果月份的最后一天
            比如:
              add_months(Date '2006-03-30',-1)  --> 2006-02-28
         
          可以构建一个自定义函数,解决 1 的问题,让他不返回月份的最后一天
            FUNCTION my_add_months (
               date_in IN DATE, months_shift IN NUMBER)
            RETURN DATE IS
               date_out DATE;
               day_in NUMBER;
               day_out NUMBER;
            BEGIN
               date_out := ADD_MONTHS(date_in, months_shift);
               day_in := TO_NUMBER(TO_CHAR(date_in,'DD'));
               day_out := TO_NUMBER(TO_CHAR(date_out,'DD'));
               IF day_out > day_in
               THEN
                  date_out := date_out - (day_out - day_in);
               END IF;
               RETURN date_out;
            END;
      建议:
        虽然以上三者 DATE TIMESTAMP 都可以操作,但建议 TIMESTAMP 使用 1,DATE 可以使用 2、3
           
    Computing the Interval Between Two Datetimes
      两个 TIMESTAMP 相减返回的永远是 INTERVAL DAY TO SECOND
      两个 DATE 相减返回的是天数,如果有小数部分,意思是 hours, minutes, and seconds 转换为天的结果
      MONTHS_BETWEEN 函数:
        定义:
              FUNCTION MONTHS_BETWEEN (date1 IN DATE, date2 IN DATE)
                RETURN NUMBER
              尝试了一下也可以使用 TIMESTAMP,但还是建议不使用,不知道会出现什么问题。
        1.date1 > date2 返回正数,小于返回负数,相等为0
        2.如果在同一年的同一月中,结果的范围是 > -1 and < 1,1 和 -1 都是不会到达的,一月按照 31 天计算,TIME 部分也要计算
        3.如果 date1 date2 分别是相应月份中的第一天或者最后一天,则返回整数,且忽略 TIME 部分的值
        4.如果 date1 date2 在不同的月份中,且至少有一个不是该月的第一天或者最后一天,那么返回小数。小数是基于 31 天为一个月计算的,且要计算 TIME 部分。
       
    Mixing DATEs and TIMESTAMPs
      由前面可知:TIMESTAMPs 相减返回 INTERVAL DAY TO SECOND,DATEs 相减返回 numeric value
      那么:
        1.如果 DATEs 相减想返回 INTERVAL DAY TO SECOND 需要用 CAST 显示地将 DATEs 转换为 TIMESTAMPs
            CAST(a AS TIMESTAMP) - CAST(b AS TIMESTAMP)
        2.如果 DATE 和 TIMESTAMP 混合使用,Oracle 隐式地将 DATE 转换为 TIMESTAMP,因此返回的是 INTERVAL DAY TO SECOND
       
    Adding and Subtracting Intervals
      INTERVAL 相加减必须满足类型相同:两个 INTERVAL DAY TO SECOND 相加减,或者两个 INTERVAL YEAR TO MONTH 相加减,不能混合使用。
     
    Multiplying and Dividing Intervals
      DATETIMEs 是不能进行乘除运算的,但是 Intervals 可以。进行乘除运算时,每一个元素都会进行运算,如果超过该元素的范围就往上一级元素进位,如果出现小数就将小数部分转化为下一级元素,秒除外。
   
    Using Unconstrained INTERVAL Types
      由于在函数、过程中,参数不能指定精度,因此当 INTERVAL 传入时如果精度大于默认精度:YEAR(2),DAY(2),SECOND(6),会返回错误 ORA-01873: the leading precision of the interval is too small
      因此,引入两个特殊类型:
        YMINTERVAL_UNCONSTRAINED
          接受任何精度的 INTERVAL YEAR TO MONTH
        DSINTERVAL_UNCONSTRAINED
          接受任何精度的 INTERVAL DAY TO SECOND


 
10.Date/Time Functions
    对于传统的 DATE 函数,比如 ADD_MONTHS 建议不要用于 TIMESTAMP,Oracle 会将他们隐式地转为 DATE,这样会丢失秒的小数部分,还会将时区修改为 SESSION 的时区。因此,对于 TIMESTAMP 应该使用 INTERVAL 来操作。

Aug 04, 2006

9i Performance Tuning Guide 读书笔记一

Introduction to the Optimizer


Overview of SQL Processing
  1. parser 进行语意和语法分析
  2. Optimizer 利用RBO,CBO等方法决定产生查询结果的最有效路径
  3. Row Source Generator 从2中接受优化后的方案,并产生SQL的Execution Plan
  4. SQL Execution Engine运行该执行计划,并产生查询结果


Features that Require the CBO
  1. Partitioned tables and indexes
  2. Index-organized tables
  3. Reverse key indexes
  4. Function-based indexes
  5. SAMPLE clauses in a SELECT statement
  6. Parallel query and parallel DML
  7. Star transformations and star joins
  8. Extensible optimizer
  9. Query rewrite with materialized views
  10. Enterprise Manager progress meter
  11. Hash joins
  12. Bitmap indexes and bitmap join indexes
  13. Index skip scans
  即使OPTIMIZER_MODE=rule,这些features仍然会使用CBO


Components of the CBO
  Query Transformer
    数据来自于parse后的SQL,是一系列的query block。其目标是测定SQL的形式是否有利于产生好的query plan。有以下4种:
      1. View Merging
          SQL 中的view 被扩展到单独的query block中。Optimizer会单独分析view query block,这样通常会导致在整体优化上得不到最优的结果。因此query transformer会将大部分view和其他的query block 合并,这样可以在整体上统一优化。
      2. Predicate Pushing
          针对没有被merge的view, push the relevant predicates from the containing query block into the view query block, which can be used either to access indexes or to act as filters
      3. Subquery Unnesting
          子查询是nested在主查询中的,这样很难得到好的优化结果。所以将他们unnested,变成join
      4. Query Rewrite with Materialized Views
          如果查询与某个物化视图符合的化,则会按照物化视图重写这个查询,因为物化视图的结果都是事先计算好的。


  Estimator
  产生 3 种度量标准:
    1. Selectivity
        表示有多少 rows 可以通过谓词被选择出来,大小介于 0.0~1.0,0 表示没有 row 被选择出来。
        如果没有 statistics,estimator 会使用一个默认的 selectivity 值,这个值根据谓词的不同而异。比如 '=' 的 selectivity 小于 '<'。
        如果有 statistics,比如对于 last_name = 'Smith',estimator 使用 last_name 列的 distinct 值的倒数(注:是指表中所有 last_name 的 distinct 值),作为 selectivity。
如果 last_name 列上有 histogram,则使用 histogram 根据 last_name 值的分布情况产生的 selectivity 作为 selectivity。Histogram 在当列有数据倾斜时可以大大帮助 CBO 产生好的 selectivity。


    2. Cardinality
        表示一个 row set 的行数。
        Base cardinality:base table 的行数。如果表分析过了,则直接使用分析的统计信息。如果没有,则使用表 extents 的数量来估计。
        Effective cardinality:有效行集,指从基表中选择出来的行数。是 Base cardinality 和表上所有谓词的组合 Selectivity 的乘积。如果表上没有谓词,那么 Effective cardinality = Base cardinality。
        Join cardinality:两表 join 后产生的行数。是两表 cardinality 的乘积(Cartesian)乘以 Join 谓词的 selectivity。
        Distinct cardinality:列上 distinct 值的行数。
        Group cardinality:GROUP BY 操作之后 row set 的行数。由 grouping columns 的 distinct cardinality 和整个 row set 的行数决定。
          group cardinality lies between max ( dist. card. colx , dist. card. coly )
                                               and min ( (dist. card. colx * dist. card. coly) ,
                                                              num rows in row set )
                              
    3. Cost
        Cost 表现了 Disk I/O, CPU usage, Memory usage 资源单位的使用数量(units of work or resource used)。
        Access path 决定从 base table 获得数据所需的 units of work 的数量。也就是说Access path 决定 Cost 的值。Access path 可以是 table scan, fast full index scan, index scan。


        Clustering Factor:
          Index 的一种属性,表示被索引的行在数据块中的分布情况,表征表中数据的存储顺序和某索引字段顺序的符合程度。直接影响使用 rowid 找到 row 的cost。大小介于 block 数和 rownum 之间。
         (以下来自biti_rainy http://blog.itpub.net/post/330/2970
          Oracle 按照索引块中存储的 rowid 来识别相临的索引中记录在表 block 中是否为相同块,如果索引中存在记录 rowid a,b,c,d……,若b 和 a 是同一个block,则比较 c 和 b,若这时不是同一个block,则 clustering_factor + 1 ,然后比较 d 和 c,若还不是同一个 block,则clustering_factor + 1……
若 clustering_factor 接近表 block 数量,则说明表中数据具有比较好的跟索引字段一样排序顺序的存储,通过索引进行 range scan 的代价比较小(需要读取的表块可能比较少),若 clustering_factor 接近 row 数量,则说明表中数据和索引字段排序顺序差异很大,杂乱无张。则通过索引进行 range scan 的代价比较大(需要读取的表块可能更多)。
当然,在 oracle 920 开始,对于cluster_factor 比较接近表块数量的根据索引的大范围查询做了特别的处理,不再是读一个索引记录去搜索一个表记录了,而是成批处理(通过索引块一批 rowid 一次去表块中获得一批记录),这样就大大节约了读的成本(consistent gets)。


        Join Cost:
        表征了做 join 的两个 row sets 分别 cost 的组合。
          Nested loop join:
            outer row set 的每行遍历 inner row set 的所有行,寻找匹配的行。
            cost = outer access cost + (inner access cost * outer cardinality)
          Sort merge join:
            做 join 的两个 row sets 根据 join keys 进行排序,如果他们不是按照 join keys 的顺序的话。
            cost = outer access cost + inner access cost + sort costs (outer and inner, if sort is used)
          Hash join:
            cost = (outer access cost * # of hash partitions) + inner access cost
            首先理解 hash table 的数据结构:
              可以把 hash table 看做一个 2 维数组 a[200][100],现有 1000 个无序数字用来被查询。我们考虑把这 1000 个数字除以 200,根据其余数放在 a[200][100] 中,余数就是数组的第一维下标。这样平均一个 a[i] 只放5个数字。当查询的时候,对数字除以 200(这就是一个简单的 hash 算法),根据余数 i 去 a[i] 中查找,大约遍历 5 次就能找到。
              Inner row(小表)被 hash 在内存中,并且通过 join key 建立 hash table(作为第一个下标),然后 scan outer table,到 hash table 中查找 joined rows(通过 hash 算法)。
              hash table 会按照 multiblock_IO 决定分成几个 partitions。如果 hash table 太大超出了 hash_area_size,则将超出部分的 partitions 放到 temporary segments 中。
可以通过 10104 events 查看 hash join 的 statistics:
          ALTER SESSION SET EVENTS '10104 trace name context forever, level 10'; 比如:
          Total number of partitions: 
          Number of partitions which could fit in memory: 
          如果后者大于前者,则说明一些 partitions 因为超出了 hash_area_size,要被放置到临时表空间中。


  Plan Generator
    作用是尝试各种可能的执行计划,选择 cost 最低的一种。
    Plan Generator 会先为 nested subqueries and nonmerged views 产生 subplans,并且从 innermost query block 开始往外优化。
    Plan Generator 会使用 internal cutoff 来减少 plan 的尝试数量。internal cutoff 基于现有的最优计划的 cost。如果很大,那么会尝试较多的计划;如果很小,那么会很快结束尝试。
    如果 plan generator 从一个接近最优的 initial join order 开始,那么 internal cutoff 可以很好的工作。Plan Generator 根据 join items 的 effective cardinality 来确定 initial join order,小的在前面,大的在后面。


Understanding Execution Plans
  Execution Plan:
  为了执行 SQL 语句,Oracle 会执行很多步骤,这些步骤的综合叫做 execution plan,包括 access path 和 join order。



Understanding Access Paths for the CBO
  Access paths:
    从数据库获得数据的方式。
 
  Full Table Scans
    表中所有在 HWM 以下的 blocks 都被扫描一遍,确定符合 where 条件的行。
    块的读取范围从 1 到 DB_FILE_MULTIBLOCK_READ_COUNT。Multiblock reads 可以提高执行效率,当访问表中大量块时 full table scans 比 index range scans 效率高。
    优化器选择 Full Table Scans 的情况:
      Lack of Index
      Large Amount of Data
      Small Table               --大小表的区分由 _small_table_threshold 隐含参数决定,默认为 db_cache_size 的 2%。 http://www.eygle.com/archives/2006/05/oracle_long_short_table.html
      High Degree of Parallelism
    Full Table Scan Hints:/*+ FULL(table alias) */
   
  Rowid Scans
    获得一行数据的最快方法。
    一般要先通过 index scan 获得 Rowid,如果需要的列不在 index 中,再进行 Rowid Scans 获得相应的行,如果在 index 中,则不需要 Rowid Scans。
 
  Index Scans
    Index Unique Scans
      最多返回一个 rowid,用于 Unique Index 且 index cols 在条件中使用“等于”。
    Index Range Scans
      返回的数据按照 index columns 升序排列,index column 为同一个值的多行按照行 rowid 的升序排列。如果 order by/group by 的顺序和 Index Range Scans 返回的 row set 的顺序相同就不需要再 sort 了,否则还需要再对 row set 进行 sort。
      Unique index 中的 < > 条件,以及 nonunique indexe 的 < = > 条件,都会引起 Index Range Scans。如果进行 wild-card searches,% 不能放最前面,否则不会进行 Index Range Scans。
      如果某列上有索引,该列有 skewed distribution,且具有 histograms,但当时用 bind variable 时,Oracle 不知道该变量具体是什么值,而无法使用 histograms,导致选择 full table scan,这种情况下可以通过使用 hints 进行调整。
    Index Range Scans Descending
      和 Index Range Scans 相同,只是用于降序返回结果,或者返回小于某特定值的结果。
      HINT:INDEX_DESC(table_alias index_name)
    Index Skip Scans
      用于前导列没有出现在查询中(skiped)时使用索引。它将 composite index 拆分成若干个小的逻辑子索引。子索引的个数由前导列的 distinct 值决定。适用于前导列 distinct 值很少(子索引就少了),非前导列 distinct 值很多的情况。
    Full Scans
      适用于:
        1.A predicate references one of the columns in the index. The predicate does not need to be an index driver.
        2.No predicate,并且:
            A.查询中引用的列都在 index 中
            B.只少有一个索引列不为空。
      它是先定位到索引的 root block,然后到 branch block(如果有的话),再定位到第一个 leaf block,然后根据 leaf block 的双向链表顺序读取。它所读取的块都是有顺序的,也是经过排序的。
    Fast Full Index Scans
      和 Full Scans 适用于:查询的所有列都在索引中出现,且至少有一个索引列具有 NOT NULL 约束。区别在于 它是从段头开始,读取包含位图块,root block,所有的branch block,leaf block,读取的顺序完全有物理存储位置决定,并采取多块读,没次读取db_file_multiblock_read_count个块。(更详细的说明参见汪海的《Index Full Scan vs Index Fast Full Scan 》 http://www.dbanotes.net/Oracle/Index_full_scan_vs_index_fast_full_scan.htm
      Fast Full Index Scans 可以利用多块读和并行读,只能用于 CBO,不能在 bitmap indexes 上使用。
      当需要排序时,Oracle 会使用 Full Index Scans,因为他的结果已经排好序;当不排序时,会使用 Fast Full Index Scans,因为能使用多块读,速度更快。
      在 rebuild index 时使用的就是 Fast Full Index Scans,所以 rebuild index 需要排序。(参见汪海的 《Rebuild Index与 Sort》http://www.dbanotes.net/Oracle/Rebuild_Index_vs_Sort.htm
      HINT:INDEX_FFS(table_alias index_name)
      Fast Full Index Scan 限制:
        索引列中至少有一列有 NOT NULL 约束
        如果要用并行 fast full index scan 必须在创建索引时单独指定 parallel clause,不能从索引所在的表上继承
        必须分析索引,否则优化器可能不会使用它
    Index Joins
      他是一个有若干个 indexes 组成的 hash join,包含查询中需要的所有列。
      他无法 eliminate a sort operation,必须在 CBO 中使用。
      HINT:INDEX_JOIN
    Bitmap Joins
      A bitmap join uses a bitmap for key values and a mapping function that converts each bit position to a rowid. Bitmaps can efficiently merge indexes that correspond to several conditions in a WHERE clause, using Boolean operations to resolve AND and OR conditions.
      只能使用于 CBO,且需要企业版。
     
  Cluster Scans
    用于从存放于 indexed cluster 中的表中获得相同 cluster key 值的数据。具有相同 cluster key 值的所有数据存放于同一个 BLOCK。通过扫描 cluster index 获得相应的 rowid,再通过 rowid 定位到所需的行。


  Hash Scans
    用于从存放于 hash cluster 中的表中获得相同 hash value 值的数据。具有相同 hash value 值的所有数据存放于同一个 BLOCK。通过将 hash function 应用于 cluster key 值上,获得 hash value,再通过 hash value 定位到所需的行上。
   
  Sample Table Scans
    从表中获得 a random sample of data。
    SAMPLE clause:从表中随机获得指定百分比的行数据。
    SAMPLE BLOCK clause:从表中随机获得指定百分比的块数据。
    限制:
      查询不能包含 a join or a remote table
      需要使用 CBO
     
  How the CBO Chooses an Access Path
    1.语句所有可用的 access paths
    2.使用每种 access paths 或者 combination of paths 时,估计的 cost 值
    影响 CBO 的因素:
      1.Optimizer Hints
        hints 会覆盖可用的 access paths,除非包含 SAMPLE or SAMPLE BLOCK
      2.Old Statistics
        影响 cost 的估计
       
       
Understanding Joins
  How the CBO Executes Join Statements
    决定一下几个参数:
      Access Paths
      Join Method(nested loop, sort merge, cartesian, and hash joins)
      Join Order
     
  How the CBO Chooses the Join Method
    估计每个 join method 的 cost,选择 cost 最少的那种 join method。
    如果 join 返回大量行(一般来说,大于 10000 行),考虑以下因素:
      1.nested loop join  是低效的,优化器不会使用它
        nested loop join cost= access cost of A + (access cost of B * number of rows from A)
      2.CBO 中,hash join 是最高效的
        hash join cost= (access cost of A * number of hash partitions of B) + access cost of B
      3.RBO 中,merge join 是最高效的
        merge join cost= access cost of A + access cost of B +(sort cost of A + sort cost of B)
        (如果数据是预先排序好的,sort cost 为 0)
       
  How the CBO Chooses Execution Plans for Join Types
    CBO,RBO 都适用的:
      1.优化器通过 UNIQUE and PRIMARY KEY 约束找到最多返回一行的表,如果这样的表存在,就把它放在连接顺序的第一位,再处理连接中的其他表。
      2.For join statements with outer join conditions, the table with the outer join operator must come after the other table in the condition in the join order. The optimizer does not consider join orders that violate this rule.
    CBO:
      估计各种 join orders, join methods, and available access paths 的 cost,选择 cost 最低的
    其他因素:
      1.较小的 sort area size 会增加 sort merge join 的 cost,因为需要更多的 CPU time and I/O
      2.较大的多块读相对于 nested loop join 来说,会减少 sort merge join 的 cost。
    CBO 中 join orders 的选择会被 ORDERED hint 覆盖,但如果 HINT 和 outer join 的规则冲突,那么 HINT 会被忽略。


  Nested Loop Joins
    当 small subsets of data 被连接,并且 join condition 对于 inner table 来说是一种高效的访问方式时,nested loop joins 是非常有用的。
    必须保证 inner table is driven from (dependent on) the outer table,否则性能很差,这种情况适用 hash joins 更好。
    HINT:USE_NL(table1_alias table2_alias)
   
  Hash Joins
    Hash joins 适用于连接 large data sets。优化器选择较小的 data source 通过 join key 建立一个 hash table 存放在内存中。通过扫描大表,查询 hash table 找到所需的数据。
    当 hash table 太大无法放到内存中,他会被拆分,一部分放到 temporary segments 中,这时 temporary extent sizes 影响 I/O 性能。这种情况下,性能很差。
    当连接使用 equijoin,并且以下任何一种情况下,会使用 hash join:
      1. A large amount of data needs to be joined.
      2. A large fraction of the table needs to be joined.
    Execution Expain 中在上面的是先被扫描的小表,用作建立 hash table,在下的是大表。
    HINT:USE_HASH(table1_alias table2_alias)
    当使用 Hash Joins 时碰到问题时,应该注意 HASH_AREA_SIZE and HASH_JOIN_ENABLED 参数。
   
  Sort Merge Joins
    Sort Merge Join 没有驱动表的概念,他的执行步骤:
      1.Sort join operation: 两个表分别按照 join key 排序 (如果已经按照 join key 排序,这步省略)
      2.Merge join operation: 将排好序的结果 merge together.
    性能比较:
      Sort Merge Join 适用于两个 independent sources。一般来说 Hash join 比 Sort Merge Join 性能好,除非以下两个条件同时满足
        1.row sources 已经被排序
        2.排序操作不需要再进行
      如果 Sort Merge Join 选择了 slower access method,比如全表扫描,他的优势就失去了。
      当两个表通过 <, <=, >, or >= 连接时,Sort merge join 是很有用的,在进行两个大 data sets 连接时,他比 nested loop join 性能好,hash join 适合用 = 连接。
    当两个大数据量的 data sets 连接时,优化器何时选择 sort merge join 而不选择 hash join:
      1.连接条件不是 equi-join,而是 <, <=, >, or >= (but not a nonequality)
      2.OPTIMIZER_MODE is set to RULE,hash join 需要 CBO
      3.HASH_JOIN_ENABLED is false.
      4.连接的表已经排好序
      5.评估 HASH_AREA_SIZE and SORT_AREA_SIZE 大小之后,优化器认为 hash join 的 cost 较高
    HINT:USE_MERGE(table1_alias table2_alias) 可能还需要指定相应的 access path
   
  Cartesian Joins
    当两个表连接时,没有指定连接条件,就会导致 Cartesian Join,一般是由于 poor SQL 造成的。
    HINT:ORDERED,By specifying a table before its join table is specified, the optimizer does a Cartesian join.
   
  Outer Joins
    返回一个表满足连接条件的所有行以及另一个表的全部或者部分行,不管这些行符不符合连接条件。
   
    Nested Loop Outer Joins
      返回 outer (preserved) table 的所有行,即使 inner (optional) table 没有符合条件的行。
      Nested Loop Outer Join 时,外部表永远是驱动表,不根据 cost 来决定哪个是驱动表。
      在以下条件下,优化器选择 Nested Loop Outer Join
        1.外部表驱动内部表是可能的
        2.数据量小到使得 nested loop 效率较高
       
    Hash Join Outer Joins
      当连接的表的数据量大到使用 hash join 效率更高,或者外部表无法驱动内部表时,优化器选择 Hash Join Outer Joins。
      表的连接顺序也不按照 cost 来决定,外部表先进行处理,用它构建 hash table。
     
    Sort Merge Outer Joins
      当外表不能驱动内表,无法使用 hash join or nested loop joins 时,那么使用 Sort Merge Outer Joins。
      由于数据量或者表已经经过排序操作,使得 Sort Merge Outer Joins 效率更高时,优化器选择 Sort Merge Outer Joins。
     
    Full Outer Joins
      Left and right outer joins 的联合。
     
     
Setting Cost-Based Optimizer Parameters
  Enabling CBO Features
    OPTIMIZER_FEATURES_ENABLE
      后面跟版本号,设置 Oracle 允许哪些 CBO 相关的特征被使用,只允许被设置了的版本的 CBO 特征,其他的不允许,在升级版本之后,为了执行计划的稳定性和向后兼容可以使用,否则不需要设置。
    Peeking of User-Defined Bind Variables
      在第一次 invocation of a cursor 的时候,Oracle peeks at 用户定义绑定变量的值,来决定所有 where 条件的 selectivity,即使没有使用绑定变量。在这之后的 invocations of the cursor 就不用再 peek 了,cursor 被共享,即使绑定变量的值不同。
      使用绑定变量假设 cursor sharing 是故意的,并且假设不同的 invocations 使用相同的执行计划,如果没有使用相同的执行计划,那么绑定变量的使用可能不正确。
     
  Controlling the Behavior of the CBO
    CURSOR_SHARING
      将语句中的 literal values 转为绑定变量,提高 cursor sharing 并且影响语句的执行计划,生成的执行计划将基于绑定变量而不是 actual literal values。
    DB_FILE_MULTIBLOCK_READ_COUNT
      Full table scan or index fast full scan 时,一次 I/O 读取的块数。用于估计 full table scans and index fast full scans 的 cost。大的 DB_FILE_MULTIBLOCK_READ_COUNT 值使得 full table scans 的 cost 较低,从而选择 full table scans 而不是 index scan。
    HASH_AREA_SIZE
      用于 hash joins 的内存大小(bytes),越大 hash join 的 cost 越低。
    HASH_JOIN_ENABLED
      是否使用 hash joins
    OPTIMIZER_INDEX_CACHING
      控制 an index probe in conjunction with a nested loop 的 cost。范围 0~100,表明索引块在 buffer cache 中的百分比。他影响优化器对 index caching for nested loops and IN-list iterators 的假设。100 表示 100% 的索引块在 buffer cache 中能找到,这将影响优化器对 an index probe or nested loop cost 的调整。
    OPTIMIZER_INDEX_COST_ADJ
      用于调整 index probe 的 cost。范围 0~10000,默认值 100,表示 indexe 是基于 normal costing model 的 access path;如果设为 10,表示 index 的 cost 是一般 index access path 的 cost 的 1/10。
    OPTIMIZER_MAX_PERMUTATIONS
      用于控制 CBO 对带有连接的 SQL 语句产生的执行计划数。范围 4 to 80000,80000 相当于无限。当将他设置成小于 1000 可以保证 parse times 降到几秒甚者更少。这个参数可以用来减少多表连接语句的 parse times,但是可能会丢失最优的执行计划。
    OPTIMIZER_MODE
      设置优化器的模式:RULE, CHOOSE, ALL_ROWS, FIRST_ROWS_n, and FIRST_ROWS
    PARTITION_VIEW_ENABLED
      是否使用 partition view pruning。如果设置成 true,CBO 只扫描需要的 partitions,根据 view predicates or filters。
    QUERY_REWRITE_ENABLED
      是否使用 query rewrite 的特性。Query rewrite 是和 materialized views 一起工作的。如果设置为 true,Oracle 会考虑使用 query rewrite 来查询 materialized views 而不是原始的大表(参见 D.C.B.A 对 query rewrite 的解释 http://www.anysql.net/2005/12/queryrewrite01.html)。另外该参数还用来控制是否使用 function-based indexes。
    SORT_AREA_SIZE
      执行 sort 所使用的内存大小(bytes)。如果 sort 的数据超过该值,那么超过的部分会放到 temporary tablespace 中。CBO 用该值估量 sort 的 cost,包括 sort merge joins。
    STAR_TRANSFORMATION_ENABLED
      This parameter, if set to true, enables the CBO to cost a star transformation for star queries. The star transformation combines the bitmap indexes on the various fact table columns rather than using a Cartesian approach.(D.C.B.A 对该功能的测试:http://www.anysql.net/2006/03/dw_star_transform.html


     
Overview of the Extensible Optimizer
  The extensible optimizer is part of the CBO. It allows the authors of user-defined functions and domain indexes to control the three main components that the CBO uses to select an execution plan: statistics, selectivity, and cost evaluation.

Aug 03, 2006

两位大师的网站被黑

    忙了一上午,吃饭前去大师 eygle 的网站去转了转,看到了 eygle 昨天晚上发布的文章《黑色星期三 本站公告》,才知道大师的网站竟然被黑了。攻击者极其狠毒,删除了数据库中所有数据,虽然现在已经恢复大部分数据,而据 Fenng 的相关文章 上说,由于没有最新的备份,只恢复到 3 月份的状态。

    同时,受牵连的还有另一位大师 D.C.B.A,由于他的网站也放在 eygle 的服务器上,也就一起遭了殃。据他所说,损失了 100 篇文章。不过看着好笑的是他老婆问他为什么不用他自己编的 AUL 进行恢复,真是让人哭笑不得。

    时下个人网站越来越多,但大多数人对个人网站的安全考虑的却不多,即使像两位大师一样的数据库方面的专家,也没有及时备份自己网站的数据。虽然一些虚拟主机提供商在服务条款上都会说每天提供数据备份,但真的出了问题之后,又有多少是得到了这些服务的呢?如 Fenng 所说的,专注观察天上的星星,而没有注意脚下的坑,不知两位大师现在心情如何,虽然两人都不约而同的在 blog 上说可能关闭个人网站,不过作为很多数据库爱好者来说,还是希望他们继续下去,这从 eygle 网站上的众多网友留言可以看出。

    想到自己现在的站点都有点害怕,服务提供商都联系不到了,更别说什么服务了,好在自己每天都做备份,心里才安心一些,只希望快到年底换空间了。

Oracle 的时区问题

    Oracle 9i 开始多了 3 个关于时间的数据类型:TIMESTAMP [(precision)] TIMESTAMP [(precision)] WITH TIME ZONE TIMESTAMP [(precision)] WITH LOCAL TIME ZONE,其中 TIMESTAMP [(precision)] WITH TIME ZONE 保存了时区信息。


1. Oracle 的时区设置


    Oracle 的时区可以分为两种,一种是数据库的时区,一种是 session 时区,也就是客户端连接时的时区(经过实验,连接以后再修改客户端的时区,session 的时区不会更改)。


    数据库的时区在创建数据库时可以通过在 create database 语句中加上 SET TIME_ZONE = ' { { + | - } hh : mi | time_zone_region } ' 来指定,如果,不指定,默认是按照数据库所在的操作系统时区来设定的。创建之后,可以通过 alter database 来修改。其中 time_zone_region 参数可以通过查询 V$TIMEZONE_NAMES 动态视图来获得所有支持的值。修改之后,需要重启数据库才能生效。经常有人会碰到无法修改的情况:


SQL> alter database set time_zone='+06:00';
alter database set time_zone='+06:00'
*
ERROR at line 1:
ORA-02231: missing or invalid option to ALTER DATABASE

    TOM 对此问题有过解释,TIME_ZONE 的设定主要是为了 WITH LOCAL TIME ZONE,当 session 的时区和数据库的时区不同时,oracle 根据时区的差距转换到数据库的时间,再保存到数据库的 WITH LOCAL TIME ZONE 类型中,他是不保存时区的,所以需要 TIME_ZONE 来进行各种时区之间时间的转换(WITH TIME ZONE 类型保存了原始的时区,所以不需要 TIME_ZONE 的设置也可以进行各种时区之间的转换)。但数据库中一旦有了该类型,就不能通过 alter database 修改时区了,会得到上面的错误,可以通过下面的语句获得所有包含该类型的表,将他们删除之后,再修改。


select u.name || '.' || o.name || '.' || c.name TSLTZcolumn
  from sys.obj$ o, sys.col$ c, sys.user$ u
where c.type# = 231
   and o.obj# = c.obj#
   and u.user# = o.owner#;

    Session 的时区是根据客户端的时区来决定的,当然连接以后也可以通过 alter session 来改变。WITH LOCAL TIME ZONE 类型会根据 TIME_ZONE 的设置,自动把时间转换为 session 所在时区的时间显示出来,而 WITH TIME ZONE 因为保存了时区,不需要根据 TIME_ZONE 的设置来转换。


2. 查看时区


    可以分别使用 SESSIONTIMEZONE / DBTIMEZONE 内建函数查看 session 和数据库时区:



SYS@SKYDB> select dbtimezone from dual;


DBTIME
------
+08:00


SYS@SKYDB> select sessiontimezone from dual;


SESSIONTIMEZONE
---------------------------------------------
+09:00



    另外可以用 TZ_OFFSET 查询某时区和 UTC 之间的差值。



TZ_OFFSET ( { 'time_zone_name'
                        | '{ + | - } hh : mi'
                        | SESSIONTIMEZONE
                        | DBTMEZONE  }
                      )


SELECT TZ_OFFSET('US/Eastern') FROM DUAL;


TZ_OFFS
-------
-04:00

SELECT TZ_OFFSET(DBTIMEZONE) FROM DUAL;

TZ_OFFSET(DBTI
--------------
+08:00


    其中 time_zone_name 也可以从 V$TIMEZONE_NAMES 获得。


3. 几个内建时间函数的比较


    sysdate/systimestamp 都是返回数据库的时间并且使用数据库的时区,他们返回的是操作系统的时间。sysdate 返回的是 date 类型,没有时区信息,操作系统上是什么时间就返回什么时间;systimestamp 返回 TIMESTAMP WITH TIME ZONE 类新,有时区信息:



SYS@SKYDB> select sysdate from dual;


SYSDATE
-------------------
2006-08-03 10:01:31

SYS@SKYDB> select systimestamp from dual;


SYSTIMESTAMP
-----------------------------------------------
03-AUG-06 10.02.21.093000 AM +08:00

SYS@SKYDB> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.

修改操作系统时区为 +02:00

SYS@SKYDB> startup
ORACLE instance started.


Total System Global Area   89202456 bytes
Fixed Size                   454424 bytes
Variable Size              62914560 bytes
Database Buffers           25165824 bytes
Redo Buffers                 667648 bytes
Database mounted.
Database opened.

SYS@SKYDB> select sysdate from dual;


SYSDATE
-------------------
2006-08-03 04:03:37


SYS@SKYDB> select systimestamp from dual;


SYSTIMESTAMP
----------------------------------------------
03-AUG-06 04.04.15.687000 AM +02:00

    注:这是我单位机子上实验的结果,由于建了多个数据库,不知道为什么不能通过 ipc 来连接本地数据了,登陆时使用 sqlplus "/@skydb as sysdba",也就是使用了监听器来连接,但在家里做相同的实验,通过 ipc 连接 sqlplus "/as sysdba",修改时区后,sysdate 依然显示修改前的时间,而 systimestamp 却正确,不知道是什么原因:

SQL> select sysdate from dual;

SYSDATE
-------------------
2006-02-08 22:21:40

SQL> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 10.22.38.578000 PM +08:00

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
修改时区为 +09:00
SQL> startup
ORACLE instance started.

Total System Global Area  131145064
bytes

Fixed Size                   453992
bytes

Variable Size             109051904
bytes

Database Buffers           20971520
bytes

Redo Buffers                 667648

bytes

Database mounted.
Database opened.
SQL> select sysdate from dual;

SYSDATE
---------
02-AUG-06

SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';

Session altered.

SQL> select sysdate from dual;

SYSDATE
-------------------
2006-08-02 22:32:59              <- 还是之前的时间

SQL> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 11.35.05.171000 PM +09:00          <- 时间正确

    另外,有个初始化参数 fixed_date,可以设置 sysdate 返回指定的时间:



alter system set fixed_date='2005-04-04-11-00-00'


this fixed_date is normally used, in oracle, for dubugging purpose.


once finishing it, you can set it back:


alter system set fixed_date=none


    Eygle 的关于这个参数的相关文章:Why sysdate is fixed

    current_timestamp/current_date 也会返回数据库的时间,但转换为 session 的时区进行显示,可以使用 alter session set time_zone 改变 session 时区。


4. 四个日期时间类型的实验


SQL> select dbtimezone from dual;

DBTIME
------
+06:00

SQL> select sessiontimezone from dual;

SESSIONTIMEZONE
---------------------------------------------------------------------------
+08:00

SQL> ed
Wrote file afiedt.buf

  1  create table tztest(a date,
  2  b timestamp(0),
  3  c timestamp(0) with time zone,
  4* d timestamp(0) with local time zone)
SQL> /

Table created.

SQL> alter session set nls_date_format ='yyyy-dd-mm hh24:mi:ss';

Session altered.

SQL> select sysdate from dual;

SYSDATE
-------------------
2006-02-08 22:21:40

SQL> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 10.22.38.578000 PM +08:00

SQL> select current_date from dual;

CURRENT_DATE
-------------------
2006-02-08 22:23:50

SQL> select current_timestamp from dual;

CURRENT_TIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 10.24.04.031000 PM +08:00

SQL> insert into tztest
  2  values(sysdate,systimestamp,systimestamp,systimestamp);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from tztest;

A
-------------------
B
---------------------------------------------------------------------------
C
---------------------------------------------------------------------------
D
---------------------------------------------------------------------------
2006-02-08 22:25:59
02-AUG-06 10.25.59 PM
02-AUG-06 10.25.59 PM +08:00
02-AUG-06 10.25.59 PM

SQL> exit
Disconnected from Oracle9i Enterprise Edition Release 9.2.0.3.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.3.0 - Production

修改了客户端操作系统的时区

C:\Documents and Settings\Administrator>sqlplus sky/xxxx

SQL*Plus: Release 9.2.0.3.0 - Production on Wed Aug 2 23:28:01 2006

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.3.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.3.0 - Production

SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';

Session altered.

SQL> select sysdate from dual;

SYSDATE
-------------------
2006-08-02 22:28:49        <-数据库没有重启,时间依然是修改前的

SQL> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
02-AUG-06 11.29.33.609000 PM +09:00  <- 这里却已经改变了,有时区信息,自动转换了?

SQL> select * from tztest;

A
-------------------
B
---------------------------------------------------------------------------
C
---------------------------------------------------------------------------
D
---------------------------------------------------------------------------
2006-08-02 22:25:59                       <- 没变
02-AUG-06 10.25.59 PM                  <- 没变
02-AUG-06 10.25.59 PM +08:00      <- 保存时区信息
02-AUG-06 11.25.59 PM                  <-自动转换为 session 的时区

只是重启之后,sysdate 还是没有变,前面已经提到,真是奇怪。


-----------------------------------------------------------------------------------------
2006.08.04 Update:
    昨天晚上又回去试了一下,找到了 sysdate 不改变的原因,和是否使用监听器没有关系,关闭数据库后把服务也关了,再修改操作系统时间,启动数据库后 sysdate 就和操作系统的同步了,不过在单位的机子上试验却不用关闭服务。

Aug 02, 2006

AIX 上 ora-600 [unable to load XDB library] 解决方法一则

    Ken 在 QQ 上说,碰到 drop user 时返回 “ORA-03113 通信通道的文件结束”的错误。ORA-03113 被戏称为经典错误,引起该错误的原因很多,没有一个固定的解决办法,Fenng 做过一个总结:Ora-03113 错误分析


    由于不在现场,我只是让他把 alert 文件传过来看了下,里面有如下一些类似的错误:


Wed Aug  2 17:52:44 2006
Errors in file /home/oracle/app/oracle/admin/txcz/udump/txcz_ora_340454.trc:
ORA-07445: exception encountered: core dump [] [] [] [] [] []
ORA-00600: internal error code, arguments: [unable to load XDB library], [], [], [], [], [], [], []


    再看看 txcz_ora_340454.trc 中,开头包含这样的错误:



/home/oracle/app/oracle/admin/txcz/udump/txcz_ora_340454.trc
Oracle9i Enterprise Edition Release 9.2.0.7.0 - 64bit Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.7.0 - Production
ORACLE_HOME = /home/oracle/app/oracle/product/920
System name: AIX
Node name: p570a_lp1
Release: 3
Version: 5
Machine: 00C7E23A4C00
Instance name: txcz
Redo thread mounted by this instance: 1
Oracle process number: 14
Unix process pid: 340454, image: oracle@p570a_lp1 (TNS V1-V3)


*** 2006-08-02 17:52:43.961
*** SESSION ID:(37.58704) 2006-08-02 17:52:43.957
Dynamic link error:  0509-022 Cannot load module /home/oracle/app/oracle/product/920/lib32/libxdb.so.
 0509-103   The module has an invalid magic number.
*** 2006-08-02 17:52:43.961
ksedmp: internal or fatal error
ORA-00600: internal error code, arguments: [unable to load XDB library], [], [], [], [], [], [], []



    Google 了下 ORA-600 [unable to load XDB library],大部分结果是要正确设置 LD_LIBRARY_PATH 和 LIBPATH 环境变量,metalink 文档号 351650.1 中提到(225897.1 中也有环境变量的设置方法):



Cause
These errors may be the result if the database was started and the environment variable LD_LIBRARY_PATH was not set, or was set to an incorrect version of $ORACLE_HOME/lib (e.g. to a different $ORACLE_HOME). If during the user level or full database export we are unable to locate the correct version of the libxdb.so / libxdb.sl library, the internal errors may occur.

Solution
1. Stop the database and stop the listener.


2. Set LD_LIBRARY_PATH so the first directory referenced is $ORACLE_HOME/lib
Example (replace $ORACLE_HOME with the full path of the Oracle home directory):


csh:$ setenv LD_LIBRARY_PATH=$ORACLE_HOME/lib$ setenv LIBPATH=$ORACLE_HOME/lib
ksh:$ export LD_LIBRARY_PATH=$ORACLE_HOME/lib$ export LIBPATH=$ORACLE_HOME/lib
For Oracle9i and Oracle10g on AIX: set environment variable LIBPATH and afterwards, run /usr/sbin/slibclean as root
For Oracle9i and Oracle10g on HP-UX, Linux, Solaris, and Tru64: set environment variable LD_LIBRARY_PATH


3. Re-start the database and the listener.

    询问了下他的环境变量设置为:

/home/oracle/app/oracle/product/920/lib32:/home/oracle/app/oracle/product/920/lib:/home/oracle/app/oracle/product/920/network/lib
    貌似设置没有问题,而且前面的错误提示也表明路径已经识别出来了:

Dynamic link error:  0509-022 Cannot load module /home/oracle/app/oracle/product/920/lib32/libxdb.so.
 0509-103   The module has an invalid magic number.

    只是这个 0509-13 错误是什么意思呢?继续 google,在 itpub 上找到下文:
    http://www.itpub.net/546629,2.html
    其中提到 32 位和 64 位数据库上环境变量应指向不同的 lib 文件夹(全文在 metalink 上,文档号:109621.1):


5. WHICH VARIABLE SHOULD BE SET FOR 64 BIT ORACLE?
   -----------------------------------------------

A 64 bit install of Oracle includes both 32 bit executables (such as svrmgrl
and sqlplus) and 64 bit executables (such as oracle).

The installation contains two 'lib' areas:
   Prior to 9i:
      $ORACLE_HOME/lib     - 32 bit libs
      $ORACLE_HOME/lib64   - 64 bit libs
   9i onwards:
      $ORACLE_HOME/lib32   - 32 bit libs
      $ORACLE_HOME/lib     - 64 bit libs

Both of these directories contain libraries of the same name. To ensure
that 32 bit executables search the 32 bit dir, and 64 bit executables
search the 64 bit dir, set the following:

Prior to 9i:
$LD_LIBRARY_PATH - includes $ORACLE_HOME/lib64, but not $ORACLE_HOME/lib
$SHLIB_PATH      - includes $ORACLE_HOME/lib, but not $ORACLE_HOME/lib64

9i onwards:
$LD_LIBRARY_PATH - includes $ORACLE_HOME/lib, but not $ORACLE_HOME/lib32
$SHLIB_PATH      - includes $ORACLE_HOME/lib32, but not $ORACLE_HOME/lib


  再看了下 trc 文件中提到的版本信息:


Oracle9i Enterprise Edition Release 9.2.0.7.0 - 64bit Production
  果然是 64 位的,而 LD_LIBRARY_PATH 中第一个路径指向 lib32,在 9i 之后,64 位的应该使用 lib 下的文件,因此造成 0509-103 The module has an invalid magic number 的错误。将 lib 路径放在环境变量最前面,应该可以解决问题了。