- 浏览: 11445 次
- 性别:
文章分类
最新评论
oracle 反向索引
Oracle中的动态SQL
oracle查询所有用户表的表名、主键名称、索引、外键等
1、查找表的所有索引(包括索引名,类型,构成列):
2、查找表的主键(包括名称,构成列):
3、查找表的唯一性约束(包括名称,构成列):
4、查找表的外键(包括名称,引用表的表名和对应的键名,下面是分成多步查询):
5、查询外键约束的列名:
6、查询引用表的键的列名:
实例:
7、查询没有建立主键的表
8、查询表记录中有空值的索引字段
FORALL语句、FORALL语句自动回滚
FORALL语句的一个关键性改进,它可大大简化代码,并且对于那些要在PL/SQL程序中更新很多行数据的程序来说,它可显著提高其性能。
1:
用FORALL来增强DML的处理能力
Oracle为Oracle8i中的PL/SQL引入了两个新的数据操纵语言(DML)语句:BULK COLLECT和FORALL。这两个语句在PL/SQL内部进行一种数组处理
;BULK COLLECT提供对数据的高速检索,FORALL可大大改进INSERT、UPDATE和DELETE操作的性能。Oracle数据库使用这些语句大大减少了
PL/SQL与SQL语句执行引擎的环境切换次数,从而使其性能有了显著提高。
使用BULK COLLECT,你可以将多个行引入一个或多个集合中,而不是单独变量或记录中。下面这个BULK COLLECT的实例是将标题中包含
有"PL/SQL"的所有书籍检索出来并置于记录的一个关联数组中,它们都位于通向该数据库的单一通道中。
DECLARE
TYPE books_aat
IS TABLE OF book%ROWTYPE
INDEX BY PLS_INTEGER;
books books_aat;
BEGIN
SELECT *
BULK COLLECT INTO book
FROM books
WHERE title LIKE '%PL/SQL%';
...
END;
类似地,FORALL将数据从一个PL/SQL集合传送给指定的使用集合的表。下面的代码实例给出一个过程,即接收书籍信息的一个嵌套表,并将该
集合(绑定数组)的全部内容插入该书籍表中。注意,这个例子还利用了Oracle9i的FORALL的增强功能,可以将一条记录直接插入到表中。
BULK COLLECT和FORALL都非常有用,它们不仅提高了性能,而且还简化了为PL/SQL中的SQL操作所编写的代码。下面的多行FORALL INSERT相当
清楚地说明了为什么PL/SQL被认为是Oracle数据库的最佳编程语言。
CREATE TYPE books_nt
IS TABLE OF book%ROWTYPE;
/
CREATE OR REPLACE PROCEDURE add_books (
books_in IN books_nt)
IS
BEGIN
FORALL book_index
IN books_in.FIRST .. books_in.LAST
INSERT INTO book
VALUES books_in(book_index);
...
END;
不过在Oracle数据库10g之前,以FORAll方式使用集合有一个重要的限制:该数据库从IN范围子句中的第一行到最后一行,依次读取集合的内容
。如果在该范围内遇到一个未定义的行,Oracle数据库将引发ORA-22160异常事件:
ORA-22160: element at index [N] does not exist
对于FORALL的简单应用,这一规则不会引起任何麻烦。但是,如果想尽可能地充分利用FORALL,那么要求任意FORALL驱动数组都要依次填充可
能会增加程序的复杂性并降低性能。
在Oracle数据库10g中,PL/SQL现在在FORALL语句中提供了两个新子句:INDICES OF与VALUES OF,它们使你能够仔细选择驱动数组中该由扩展
DML语句来处理的行。
当绑定数组为稀疏数组或者包含有间隙时,INDICES OF会非常有用。该语句的语法结构为:
FORALL indx IN INDICES
OF sparse_collection
INSERT INTO my_table
VALUES sparse_collection (indx);
VALUES OF用于一种不同的情况:绑定数组可以是稀疏数组,也可以不是,但我只想使用该数组中元素的一个子集。那么我就可以使用VALUES
OF来指向我希望在DML操作中使用的值。该语句的语法结构为:
FORALL indx IN VALUES OF pointer_array
INSERT INTO my_table
VALUES binding_array (indx);
不用FOR循环而改用FORALL
假定我需要编写一个程序,对合格员工(由comp_analysis.is_eligible函数确定)加薪,编写关于不符合加薪条件的员工的报告并写入
employee_history表。我在一个非常大的公司工作;我们的员工非常非常多。
对于一位PL/SQL开发人员来说,这并不是一项十分困难的工作。我甚至不需要使用BULK COLLECT或FORALL就可以完成这项工作,如清单 1所示
,我使用一个CURSOR FOR循环和单独的INSERT及UPDATE语句。这样的代码简洁明了;不幸地是,我花了10分钟来运行此代码,我的"老式"方法
要运行30分钟或更长时间。
清单 1:
CREATE OR REPLACE PROCEDURE give_raises_in_department (
dept_in IN employee.department_id%TYPE
, newsal IN employee.salary%TYPE
)
IS
CURSOR emp_cur
IS
SELECT employee_id, salary, hire_date
FROM employee
WHERE department_id = dept_in;
BEGIN
FOR emp_rec IN emp_cur
LOOP
IF comp_analysis.is_eligible (emp_rec.employee_id)
THEN
UPDATE employee
SET salary = newsal
WHERE employee_id = emp_rec.employee_id;
ELSE
INSERT INTO employee_history
(employee_id, salary
, hire_date, activity
)
VALUES (emp_rec.employee_id, emp_rec.salary
, emp_rec.hire_date, 'RAISE DENIED'
);
END IF;
END LOOP;
END give_raises_in_department;
好在我公司的数据库升级到了Oracle9i,而且更幸运的是,在最近的Oracle研讨会上(以及Oracle技术网站提供的非常不错的演示中)我了解
到了批量处理方法。所以我决定使用集合与批量处理方法重新编写程序。写好的程序如清单 2所示。
清单 2:
1 CREATE OR REPLACE PROCEDURE give_raises_in_department (
2 dept_in IN employee.department_id%TYPE
3 , newsal IN employee.salary%TYPE
4 )
5 IS
6 TYPE employee_aat IS TABLE OF employee.employee_id%TYPE
7 INDEX BY PLS_INTEGER;
8 TYPE salary_aat IS TABLE OF employee.salary%TYPE
9 INDEX BY PLS_INTEGER;
10 TYPE hire_date_aat IS TABLE OF employee.hire_date%TYPE
11 INDEX BY PLS_INTEGER;
12
13 employee_ids employee_aat;
14 salaries salary_aat;
15 hire_dates hire_date_aat;
16
17 approved_employee_ids employee_aat;
18
19 denied_employee_ids employee_aat;
20 denied_salaries salary_aat;
21 denied_hire_dates hire_date_aat;
22
23 PROCEDURE retrieve_employee_info
24 IS
25 BEGIN
26 SELECT employee_id, salary, hire_date
27 BULK COLLECT INTO employee_ids, salaries, hire_dates
28 FROM employee
29 WHERE department_id = dept_in;
30 END;
31
32 PROCEDURE partition_by_eligibility
33 IS
34 BEGIN
35 FOR indx IN employee_ids.FIRST .. employee_ids.LAST
36 LOOP
37 IF comp_analysis.is_eligible (employee_ids (indx))
38 THEN
39 approved_employee_ids (indx) := employee_ids (indx);
40 ELSE
41 denied_employee_ids (indx) := employee_ids (indx);
42 denied_salaries (indx) := salaries (indx);
43 denied_hire_dates (indx) := hire_dates (indx);
44 END IF;
45 END LOOP;
46 END;
47
48 PROCEDURE add_to_history
49 IS
50 BEGIN
51 FORALL indx IN denied_employee_ids.FIRST .. denied_employee_ids.LAST
52 INSERT INTO employee_history
53 (employee_id
54 , salary
55 , hire_date, activity
56 )
57 VALUES (denied_employee_ids (indx)
58 , denied_salaries (indx)
59 , denied_hire_dates (indx), 'RAISE DENIED'
60 );
61 END;
62
63 PROCEDURE give_the_raise
64 IS
65 BEGIN
66 FORALL indx IN approved_employee_ids.FIRST .. approved_employee_ids.LAST
67 UPDATE employee
68 SET salary = newsal
69 WHERE employee_id = approved_employee_ids (indx);
70 END;
71 BEGIN
72 retrieve_employee_info;
73 partition_by_eligibility;
74 add_to_history;
75 give_the_raise;
76 END give_raises_in_department;
扫一眼清单1 和清单2 就会清楚地认识到:改用集合和批量处理方法将增加代码量和复杂性。但是,如果你需要大幅度提升性能,这还是值得
的。下面,我们不看这些代码,我们来看一看当使用FORALL时,用什么来处理CURSOR FOR循环内的条件逻辑。
定义集合类型与集合
在清单 2中,声明段的第一部分(第6行至第11行)定义了几种不同的集合类型,与我将从员工表检索出的列相对应。我更喜欢基于employee%
ROWTYPE来声明一个集合类型,但是FORALL还不支持对某些记录集合的操作,在这样的记录中,我将引用个别字段。所以,我还必须为员工ID、
薪金和雇用日期分别声明其各自的集合。
接下来为每一列声明所需的集合(第13行至第21行)。首先定义与所查询列相对应的集合(第13行至第15行):
employee_ids employee_aat;
salaries salary_aat;
hire_dates hire_date_aat;
然后我需要一个新的集合,用于存放已被批准加薪的员工的ID(第17行):
approved_employee_ids employee_aat;
最后,我再为每一列声明一个集合(第19行至第21行),用于记录没有加薪资格的员工:
denied_employee_ids employee_aat;
denied_salaries salary_aat;
denied_hire_dates hire_date_aat;
深入了解代码
数据结构确定后,我们现在跳过该程序的执行部分(第72行至第75行),了解如何使用这些集合来加速进程。
retrieve_employee_info;
partition_by_eligibility;
add_to_history;
give_the_raise;
我编写此程序使用了逐步细化法(也被称为"自顶向下设计")。所以执行部分不是很长,也不难理解,只有四行,按名称对过程中的每一步进
行了描述。首先检索员工信息(指定部门的所有员工)。然后进行划分,将要加薪和不予加薪的员工区分出来。完成之后,我就可以将那些不
予加薪的员工添加至员工历史表中,对其他员工进行加薪。
以这种方式编写代码使最终结果的可读性大大增强。因而我可以深入到该程序中对我有意义的任何部分。
有了已声明的集合,我现在就可以使用BULK COLLECT来检索员工信息(第23行至第30行)。这一部分有效地替代了CURSOR FOR循环。至此,数
据被加载到集合中。
划分逻辑(第32行至第46行)要求对刚刚填充的集合中的每一行进行检查,看其是否符合加薪条件。如果符合,我就将该员工ID从查询填充的
集合复制到符合条件的员工的集合。如果不符合,则复制该员工ID、薪金和雇用日期,因为这些都需要插入到employee_history表中。
初始数据现在已被分为两个集合,可以将其分别用作两个不同的FORALL语句(分别从第51行和第66行开始)的驱动器。我将不合格员工的集合
中的数据批量插入到employee_history(add_to_history)表中,并通过give_the_raise过程,在employee表中批量更新合格员工的信息。
最后再仔细地看一看add_to_history(第48行至第61行),以此来结束对这个重新编写的程序的分析。FORALL语句(第51行)包含一个IN子句
,它指定了要用于批量INSERT的行号范围。在对程序进行第二次重写的说明中,我将把用于定义范围的集合称为"驱动集合"。但在
add_to_history的这一版本中,我简单地假定: 使用在denied_employee_ids中定义的所有行。在INSERT自身内部,关于不合格员工的三个集
合都会被用到;我将把这些集合称为"数据集合"。可以看到,驱动集合与数据集合无需匹配。在学习Oracle数据库10g的新特性时,这是一个关
键点。
结果,清单 2 的行数大约是清单 1行数的2倍,但是清单 2 中的代码会在要求的时间内运行。在使用Oracle数据库10g之前,在这种情况下,
我只会对能够在这一时间内运行代码并开始下一个任务这一点感到高兴。
不过,有了Oracle数据库10g中最新版的PL/SQL,现在我就可以在性能、可读性和代码量方面作出更多的改进。
将VALUES OF用于此过程
在Oracle数据库10g中,可以指定FORALL语句使用的驱动集合中的行的子集。可以使用以下两种方法之一来定义该子集:
将数据集合中的行号与驱动集合中的行号进行匹配。你需要使用INDICES OF子句。
将数据集合中的行号与驱动集合中所定义行中找到的值进行匹配。这需要使用VALUES OF子句。
在对give_raises_in_department进行第二次和最后一次改写中我将使用VALUES OF子句。清单 3 包含这个版本的全部代码。我将略过这一程序
中与前一版本相同的部分。
从声明集合开始,请注意我不再另外定义集合来存放合格的和不合格的员工信息,而是在清单 3 (第17行至第21行)中声明两个"引导"集合:
一个用于符合加薪要求的员工,另一个用于不符合加薪要求的员工。这两个集合的数据类型都是布尔型;不久将会看到,这些集合的数据类型
与FORALL语句毫无关系。FORALL语句只关心定义了哪些行。 在员工表中拥有50 000行信息的give_raises_in_department的三种执行方法的占
用时间 执行方法 用时
CURSOR FOR循环 00:00:38.01
Oracle数据库10g之前的批量处理 00:00:06.09
Oracle数据库10g的批量处理 00:00:02.06
在员工表中拥有100,000行数据的give_raises_in_department的三种执行方法的占用时间 执行方法 用时
CURSOR FOR循环 00:00:58.01
Oracle数据库10g之前的批量处理 00:00:12.00
Oracle数据库10g的批量处理 00:00:05.05
表1:处理50,000行和100,000行数据的用时测试结果
retrieve_employee_info子程序与前面的相同,但是对数据进行划分的方式完全不同(第32行至第44行)。我没有将记录从一个集合复制到另
一个集合(这个操作相对较慢),而只是确定与员工ID集合中的行号相匹配的相应引导集合中的行(通过为其指定一个TRUE值)。
现在可以在两个不同FORALL语句(由第49行和第65行开始)中,将approved_list和denied_list集合用作驱动集合。
为了插入到employee_history表中,我使用了如下语句:
FORALL indx IN VALUES OF denied_list
为了进行更新(给员工进行加薪),我使用这一格式:
FORALL indx IN VALUES OF approved_list
在这两个DML语句中,数据集合是在BULK COLLECT 检索步骤中填充的最初的集合;没有进行过复制。利用VALUES OF,Oracle数据库在这些数据
集合的行中进行筛选,仅使用行号与驱动集合中行号相匹配的行
利用本程序中的VALUES OF,可以避免复制对全部记录进行复制,而是用行号的一个简单列表来替换它们。对于大型数组,进行这些复制的开销
是非常可观的。为了测试Oracle数据库10g的优越性,我装入employee表并对50,000行和100,000行的数据运行测试。为了模拟更多的现实情况,我将Oracle数据库10g之前的批量处理的执行方法作了修改以进行集合内容的多次复制。然后我使用SQL*Plus SET TIMING ON来显示运行各个不同的执行方法所用的时间。表 1 给出了结果。
从这些时间测定得到的结论非常清楚:由单个DML语句变为批量处理将大幅缩短耗用时间,数据为50,000行时的用时由38秒减为6秒,数据为
100,000行时的用时由58秒减为12秒。而且,通过使用VALUES OF来避免复制数据,我可以将用时缩短一半左右。
即使没有性能上的改进,VALUES OF及其同类子句--INDICES OF也提高了PL/SQL语言的灵活性,使开发人员能够更轻松地编写出更直观和更容易维护的代码。
在产品寿命这一点上,PL/SQL是一种成熟且功能强大的语言。因而,其很多新特性都是逐渐增加和改进而成的。不过,这些新特性还是使应用
程序的性能和开发人员的开发效率有了重大改变。VALUES OF就是这种特性的一个很好的例子。
使用Bulk Collect提高Oracle查询效率
使用Bulk Collect提高Oracle查询效率
Oracle8i中首次引入了Bulk Collect特性,该特性可以让我们在PL/SQL中能使用批查询,批查询在某些情况下能显著提高查询效率。现在,我们对该特性进行一些简单的测试和分析。
一、首先,我们创建一个表,并插入100000条记录
在SQL/Plus中执行下列脚本:
drop table empl_tbl
/
create table empl_tbl(last_name varchar2(20),
first_name varchar2(10),
salary number(10))
/
begin
for i in 30000..1029999 loop
insert into empl_tbl(last_name,first_name,salary) values('carl'||(i-3000),'wu'||(1003000-i),i);
end loop;
end;
/
commit
/
select count(*) from empl_tbl;
/
二、使用三种方法计算表中某一字段含有多少个不重复值
2.1 使用常规的Distinct来实现
SQL> select count(distinct last_name) from empl_tbl;
Distinct Last Name
------------------
1000000
Executed in 1.531 seconds
我们可以看到,常规方法需要1.531 秒查出该表中有1000000个不重复的Last_name值。
2.2 使用游标来实现
我们执行下面语句来统计Last_name字段的不重复值个数:
CREATE OR REPLACE PROCEDURE tttt IS
all_rows number(10);
temp_last_name empl_tbl.last_name%type;
begin
all_rows:=0;
temp_last_name:=' ';
for cur in (select last_name from empl_tbl order by last_name) loop
if cur.last_name!=temp_last_name then
all_rows:=all_rows+1;
end if;
temp_last_name:=cur.last_name;
end loop;
dbms_output.put_line('all_rows are '||all_rows);
end;
请注意上面代码中的黑体部分使用了一个For Loop游标,为了提高程序可读性,我们没有显示定义游标变量。
执行结果:
all_rows are 1000000
PL/SQL procedure successfully completed
Executed in 10.39 seconds
游标需要10.396秒才能查出该表中有1000000个不重复的Last_name值,所耗时间是Distinct查询的10倍多。
2.3 使用Bulk Collect批查询来实现
示例代码如下:
declare
all_rows number(10);
--首先,定义一个Index-by表数据类型
type last_name_tab is table of empl_tbl.last_name%type index by binary_integer;
last_name_arr last_name_tab;
--定义一个Index-by表集合变量
temp_last_name empl_tbl.last_name%type;
begin
all_rows:=0;
temp_last_name:=' ';
--使用Bulk Collect批查询来充填集合变量
select last_name bulk collect into last_name_arr from empl_tbl;
for i in 1..last_name_arr.count loop
if temp_last_name!=last_name_arr(i) then
all_rows:=all_rows+1;
end if;
temp_last_name:=last_name_arr(i);
end loop;
dbms_output.put_line('all_rows are '||all_rows);
end;
请注意上面代码中,我们首先定义了一个Index-by表数据类型last_name_tab,然后定义了一个该集合数据类型的变量last_name_arr,最后我们使用Bulk Collect批查询来充填last_name_arr,请注意它
的使用语法。
执行结果:
all_rows are 1000000
PL/SQL procedure successfully completed
Executed in 2.343 seconds
从上面执行结果,我们可以看到,Bulk Collect批查询只需要2.343 秒就能查出该表中有1000000个不重复的Last_name值,所耗时间只有游标查询的1/2.5。
三. 测试结果分析
为什么会出现上面的结果呢?我们可以使用Oracle的SQL_Trace来分析一下结果。在SQL命令行中,使用alter session set sql_trace=true语句打开Oracle的Trace,然后在命令行中执行上面三种查询并使用TKPROF工具生成Trace报告。
3.1 常规Distinct查询结果分析
3.2 游标查询效率分析
3.3 Bulk Collect的查询效率分析
四. 结论
通过上面的测试和分析,我们可以看到Bulk Collect批查询在某种程度上可以提高查询效率,它首先将所需数据读入内存,然后再统计分析,这样就可以提高查询效率。但是,如果Oracle数据库的内存较小,Shared Pool Size不足以保存Bulk Collect批查询结果,那么该方法需要将Bulk Collect的集合结果保存在磁盘上,在这种情况下,Bulk Collect方法的效率反而不如其他两种方法,有兴趣的读者可以进一步测试。
另外,除了Bulk Collect批查询外,我们还可以使用FORALL语句来实现批插入、删除和更新,这在大批量数据操作时可以显著提高执行效率。
oracle_常用系统表
dba_users 数据库用户信息
dba_segments 表段信息
dba_extents 数据区信息
dba_objects 数据库对象信息
dba_tablespaces 数据库表空间信息
dba_data_files 数据文件设置信息
dba_temp_files 临时数据文件信息
dba_rollback_segs 回滚段信息
dba_ts_quotas 用户表空间配额信息
dba_free_space 数据库空闲空间信息
dba_profiles 数据库用户资源限制信息
dba_sys_privs 用户的系统权限信息
dba_tab_privs 用户具有的对象权限信息
dba_col_privs 用户具有的列对象权限信息
dba_role_privs 用户具有的角色信息
dba_audit_trail 审计跟踪记录信息
dba_stmt_audit_opts 审计设置信息
dba_audit_object 对象审计结果信息
dba_audit_session 会话审计结果信息
dba_indexes 用户模式的索引信息
user_开头
user_objects 用户对象信息
user_source 数据库用户的所有资源对象信息
user_segments 用户的表段信息
user_tables 用户的表对象信息
user_tab_columns 用户的表列信息
user_constraints 用户的对象约束信息
user_sys_privs 当前用户的系统权限信息
user_tab_privs 当前用户的对象权限信息
user_col_privs 当前用户的表列权限信息
user_role_privs 当前用户的角色权限信息
user_indexes 用户的索引信息
user_ind_columns 用户的索引对应的表列信息
user_cons_columns 用户的约束对应的表列信息
user_clusters 用户的所有簇信息
user_clu_columns 用户的簇所包含的内容信息
user_cluster_hash_expressions 散列簇的信息
v$开头
v$database 数据库信息
v$datafile 数据文件信息
v$controlfile 控制文件信息
v$logfile 重做日志信息
v$instance 数据库实例信息
v$log 日志组信息
v$loghist 日志历史信息
v$sga 数据库SGA信息
v$parameter 初始化参数信息
v$process 数据库服务器进程信息
v$bgprocess 数据库后台进程信息
v$controlfile_record_section 控制文件记载的各部分信息
v$thread 线程信息
v$datafile_header 数据文件头所记载的信息
v$archived_log 归档日志信息
v$archive_dest 归档日志的设置信息
v$logmnr_contents 归档日志分析的DML DDL结果信息
v$logmnr_dictionary 日志分析的字典文件信息
v$logmnr_logs 日志分析的日志列表信息
v$tablespace 表空间信息
v$tempfile 临时文件信息
v$filestat 数据文件的I/O统计信息
v$undostat Undo数据信息
v$rollname 在线回滚段信息
v$session 会话信息
v$transaction 事务信息
v$rollstat 回滚段统计信息
v$pwfile_users 特权用户信息
v$sqlarea 当前查询过的sql语句访问过的资源及相关的信息
v$sql 与v$sqlarea基本相同的相关信息
v$sysstat 数据库系统状态信息
all_开头
all_users 数据库所有用户的信息
all_objects 数据库所有的对象的信息
all_def_audit_opts 所有默认的审计设置信息
all_tables 所有的表对象信息
all_indexes 所有的数据库对象索引的信息
session_开头
session_roles 会话的角色信息
session_privs 会话的权限信息
index_开头
index_stats 索引的设置和存储信息
伪表 dual 系统伪列表信息
oracle 查看用户 用户权限 用户表空间 用户默认表空间[转]
1、查看所有用户
select * from dba_users;//dba
select * from all_users;//all
select * from user_users;//当前
2、查看当前用户连接:
select * from v$Session;
3、查看当前用户权限:
select * from session_privs;
4、查看所有用户系统权限
select * from dba_sys_privs;
select * from user_sys_privs;
5、查看所有用户对象权限
select * from dab_tab_privs;
select * from all_tab_privs;
select * from user_tab_privs;
6、查看所有角色
select * from dba_roles;
select * from dba_role_privs;
select * from user_role_privs;
7、查看所有表空间、临时表空间等信息
select tablespace_name,file_name,bytes file_size,autoextensible from dba_data_files;
select tablespace_name,file_name,bytes file_size,autoextensible from dba_temp_files;
8、查看用户表空间使用情况:
select
a.file_id "FileNo",a.tablespace_name "Tablespace_name",
a.bytes "Bytes",a.bytes-sum(nvl(b.bytes,0)) "Used", sum(nvl(b.bytes,0)) "Free",sum(nvl(b.bytes,0))/a.bytes*100 "%free"
from dba_data_files a,dba_free_space b
where a.file_id=b.file_id(+)
group by a.tablespace_name , a.file_id,a.bytes
order by a.tablespace_name;
9、查看用户所有对象
select table_name,num_rows from user_tables;
select * from all_tables;
10、询所有函数和存储过程
select * from dba_source;
select * from user_source;
oracle数据恢复
1.表查询闪回
create table xcp as (select * from b_za_bzdzkxx);
select * from xcp;
select count(1) from xcp;--22001
select count(1) from xcp t where t.dzbh like '510521%';--7011
delete from xcp t where t.dzbh like '510521%';
select count(1) from xcp;--14990
--查找指定时间点前的数据
select count(1) from xcp as of timestamp to_timestamp('2011-12-23 10:49:30','yyyy-MM-dd hh24:mi:ss');--22001
select * from xcp for update;--添加一条记录
select count(1) from xcp;--14991
--恢复指定时间点的前delete数据(将删除恢复时间点后面的数据)
alter table xcp enable row movement;--启动的行移动功能
flashback table xcp to timestamp to_timestamp('2011-12-23 10:49:30,'yyyy-MM-dd hh24:mi:ss');
select count(1) from xcp;--22001
--恢复指定时间点的前delete数据,并保留恢复时间点后面的数据
create table xcp2 as (select * from xcp t where t.createdtime>to_timestamp('2011-12-23 10:49:30','yyyy-MM-dd hh24:mi:ss'));
select * from xcp2;--临时表
alter table xcp enable row movement;--启动的行移动功能
flashback table xcp to timestamp to_timestamp('2011-12-23 10:49:30,'yyyy-MM-dd hh24:mi:ss');
select count(1) from xcp;--22001
insert into xcp select * from xcp2 --将临时表的数据再插入到源表
select count(1) from xcp;--22002
2.删除闪回[10g+]
删除闪回为删除oracle 10G提供了一个数据库安全机制,当用户删除一个表时,oracle 10G会将该表放到回收站中,回收站中的对象一直会保留,直到用户决定永久删除它们或出现表空间的空间不足时才会被删除。回收站是一个虚拟容器,用于存储所有被删除的对象。数据字典user_tables中的列dropped表示被删除的表,查询方法如下:
select table_name,dropped from user_tables;
--设置数据库是否启用回收站
alert session set recyclebin = off;
--查询回收站对象
select * from recyclebin;
select * from user_recyclebin;
select * from dba_recyclebin;
drop table xcp;
select count(1) from xcp;--0
--恢复drop的表
flashback table xcp to before drop;
select count(1) from xcp;--22001
如果不知道原表名,可以直接使用回收站中的名称进行闪回..
flashback table "BIN$JiXyAUO4R+u3qNVfQK/Kiw==$0" to before drop;
在回收的同时可以修改表名
flashback table "BIN$JiXyAUO4R+u3qNVfQK/Kiw==$0" to before drop rename to xcp1;
--真正删除一个表,而不进入回收站,可以在删除表时增加purge选项
drop table xcp1 purge;
--也可以从回收站永久性删除表
purge table xcp1;
--删除当前用户回收站
purge recyclebin
--删除全体用户在回收站的资源
purge dba_resyclebin
3.数据库闪口[10g+]
使用数据库闪回功能,可以使数据库回到过去某一状态,语法如下:
sql: alter database flashback on;
sql: flashback database to scn 46963;
sql: flashback database to timestamp to_timestamp('2007-2-12 12:00:00','yyyy-MM-dd hh24:mi:ss');
oracle 取得表字段注释
select * from user_tab_cols b where b.table_name='B_ZA_CZRKXX' ORDER BY b.internal_column_id;
--USER_TAB_COLUMNS,以避免获取到oracle自行添加的隐藏字段
select * from user_tab_columns b where b.TABLE_NAME='B_ZA_CZRKXX';
select * from user_tab_comments b where b.TABLE_NAME='B_ZA_CZRKXX';
select * from user_col_comments b where b.TABLE_NAME='B_ZA_CZRKXX';
select col.COLUMN_NAME ,com.comments,col.DATA_TYPE
from user_tab_columns col
inner join user_col_comments com
on col.TABLE_NAME='B_ZA_CZRKXX' and
col.TABLE_NAME=com.TABLE_NAME and
col.COLUMN_NAME=com.COLUMN_NAME
order by col.COLUMN_ID
Oracle SQL优化
尽量少用IN操作符,基本上所有的IN操作符都可以用EXISTS代替
用IN写出来的SQL的优点是比较容易写及清晰易懂,但是用IN的SQL性能总是比较低的,从ORACLE执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别:
ORACLE 试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用 IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。
Oracle在执行IN子查询时,首先执行子查询,将查询结果放入临时表再执行主查询。而EXIST则是首先检查主查询,然后运行子查询直到找到第一个匹配项。NOT EXISTS比NOT IN效率稍高。但具体在选择IN或EXIST操作时,要根据主子表数据量大小来具体考虑。
SELECT * FROM EMP WHERE EMPNO > 0 AND EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB')
SELECT * FROM EMP WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB')
推荐方案:在业务密集的SQL当中尽量不采用IN操作符。
不用NOT IN操作符,可以用NOT EXISTS或者外连接+替代
此操作是强列推荐不使用的,因为它不能应用表的索引。
推荐方案:用NOT EXISTS 或(外连接+判断为空)方案代替
不用“<>”或者“!=”操作符。对不等于操作符的处理会造成全表扫描,可以用“<” or “>”代替
不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。
推荐方案:用其它相同功能的操作运算代替,如:
1)a<>0 改为 a>0 or a<0
2)a<>’’ 改为 a>’’
Where子句中出现IS NULL或者IS NOT NULL时,Oracle会停止使用索引而执行全表扫描。可以考虑在设计表时,对索引列设置为NOT NULL。这样就可以用其他操作来取代判断NULL的操作
IS NULL 或IS NOT NULL操作(判断字段是否为空)
判断字段是否为空一般是不会应用索引的,因为B树索引是不索引空值的。
推荐方案:
用其它相同功能的操作运算代替,如:
1)a is not null 改为 a>0 或a>’’等。
2)不允许字段为空,而用一个缺省值代替空值,如业扩申请中状态字段不允许为空,缺省为申请。
3) 建立位图索引(有分区的表不能建,位图索引比较难控制,如字段值太多索引会使性能下降,多人更新操作会增加数据块锁的现象)
当通配符“%”或者“_”作为查询字符串的第一个字符时,索引不会被使用
对于有连接的列“||”,最后一个连接列索引会无效。尽量避免连接,可以分开连接或者使用不作用在列上的函数替代。
如果索引不是基于函数的,那么当在Where子句中对索引列使用函数时,索引不再起作用。
Where子句中避免在索引列上使用计算,否则将导致索引失效而进行全表扫描。
对数据类型不同的列进行比较时,会使索引失效。
> 及 < 操作符(大于或小于操作符)
大于或小于操作符一般情况下是不用调整的,因为它有索引就会采用索引查找,但有的情况下可以对它进行优化,如一个表有100万记录,一个数值型字段A, 30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A>2与A>=3的效果就有很大的区别了,因为 A>2时ORACLE会先找出为2的记录索引再进行比较,而A>=3时ORACLE则直接找到=3的记录索引。
推荐方案:用“>=”替代“>”。
例: A>2 改为 A>=3
A<2 改为 A<=1
UNION操作符
UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如:
select * from gc_dfys
union
select * from ls_jg_dfys
这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。
select * from gc_dfys
union all
select * from ls_jg_dfys
LIKE操作符
LIKE 操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘%5400%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE ‘%5400%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。
SQL书写的影响(共享SQL语句可以提高操作效率)
同一功能同一性能不同写法SQL的影响
如一个SQL在A程序员写的为
Select * from zl_yhjbqk
B程序员写的为
Select * from dlyx.zl_yhjbqk(带表所有者的前缀)
C程序员写的为
Select * from DLYX.ZLYHJBQK(大写表名)
D程序员写的为
Select * from DLYX.ZLYHJBQK(中间多了空格)
以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL 都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。
推荐方案:不同区域出现的相同的Sql语句,要保证查询字符完全相同,以利用SGA共享池,防止相同的Sql语句被多次分析。
WHERE后面的条件顺序影响
Oracle从下到上处理Where子句中多个查询条件,所以表连接语句应写在其他Where条件前,可以过滤掉最大数量记录的条件必须写在Where子句的末尾。
WHERE子句后面的条件顺序对大数据量表的查询会产生直接的影响,如
Select * from zl_yhjbqk where dy_dj = '1KV以下' and xh_bz=1
Select * from zl_yhjbqk where xh_bz=1 and dy_dj = '1KV以下'
以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj = '1KV以下'条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的时候99%条记录都进行dy_dj及xh_bz的比较,而在进行第二条SQL的时候0.5%条记录都进行dy_dj及xh_bz的比较,以此可以得出第二条SQL的CPU占用率明显比第一条低。
查询表顺序的影响
Oracle从右到左处理From子句中的表名,所以在From子句中包含多个表的情况下,将记录最少的表放在最后。(只在采用RBO优化时有效)
在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下ORACLE会按表出现的顺序进行链接,由此因为表的顺序不对会产生十分耗服务器资源的数据交叉。(注:如果对表进行了统计分析, ORACLE会自动先进小表的链接,再进行大表的链接)。
Order By语句中的非索引列会降低性能,可以通过添加索引的方式处理。严格控制在Order By语句中使用表达式
当在Sql语句中连接多个表时,使用表的别名,并将之作为每列的前缀。这样可以减少解析时间
多利用内部函数提高Sql效率
SQL语句索引的利用
对操作符的优化(见前面)
对条件字段的一些优化
采用函数处理的字段不能利用索引
如:
substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’
trunc(sk_rq)=trunc(sysdate), 优化处理:sk_rq>=trunc(sysdate) and sk_rq<trunc(sysdate+1)
进行了显式或隐式的运算的字段不能进行索引
如:
ss_df+20>50,优化处理:ss_df>30
‘X’||hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’
sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5
hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’,注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型。
条件内包括了多个本表的字段运算时不能进行索引
ys_df>cx_df,无法进行优化
qc_bh||kh_bh=’5400250000’,优化处理:qc_bh=’5400’ and kh_bh=’250000’
可能引起全表扫描的操作
在索引列上使用NOT或者“<>”
对索引列使用函数或者计算
NOT IN操作
通配符位于查询字符串的第一个字符
IS NULL或者IS NOT NULL
多列索引,但它的第一个列并没有被Where子句引用
ORACLE在SQL执行分析方面已经比较成熟,如果分析执行的路径不对首先应在数据库结构(主要是索引)、服务器当前性能(共享内存、磁盘文件碎片)、数据库对象(表、索引)统计信息是否正确这几方面分析。
--备注:多表查询时,记录最少的表放到最后
Oracle树查询及相关函数
Oracle树查询的最重要的就是select...start with... connect by ...prior 语法了。依托于该语法,我们可以将一个表形结构的中以树的顺序列出来。在下面列述了Oracle中树型查询的常用查询方式以及经常使用的与树查询相关的Oracle特性函数等,在这里只涉及到一张表中的树查询方式而不涉及多表中的关联等。
以我做过的一个项目中的表为例,表结构如下:
CREATE TABLE FLFL
(
ID NUMBER NOT NULL,
MC NVARCHAR2(20),
FLJB NUMBER,
SJFLID NUMBER
)
FLJB是作为树的级别,在很多查询中可以加快SQL的查询效率。在下面演示的功能基本上不使用这个关键字。
SJFLID存储的是上级ID,如果是顶级父节点,该SJFLID为null(得补充一句,当初的确是这样设计的,不过现在知道,表中最好别有null记录,这会引起全文扫描,建议改成0代替)。
我们从最基本的操作,逐步列出树查询中常见的操作,所以查询出来的节点以家族中的辈份作比方。
1. 查找树中的所有顶级父节点(辈份最长的人)。 假设这个树是个目录结构,那么第一个操作总是找出所有的顶级节点,再根据该节点找到其下属节点。
SELECT * FROM flfl WHERE sjflid IS NULL;
这是个引子,没用到树型查询。
2.查找一个节点的直属子节点(所有儿子)。 如果查找的是直属子类节点,也是不用用到树型查询的。
SELECT * FROM flfl WHERE sjflid = 819459;
这个可以找到ID为819459的直属子类节点。
3.查找一个节点的所有 直属子节点(所有后代)。
SELECT * FROM flfl START WITH ID = 819459 CONNECT BY sjflid = PRIOR ID;
这个查找的是ID为819459的节点下的所有直属子类节点,包括子辈的和孙子辈的所有直属节点。
4.查找一个节点的直属父节点(父亲)。 如果查找的是节点的直属父节点,也是不用用到树型查询的。
SELECT b.* FROM flfl a JOIN flfl b ON a.sjflid = b.ID WHERE a.ID = 6758;
这个找到的是ID为6758的节点的直属父节点,要用到同一张表的关联了。
5.查找一个节点的所有直属父节点(祖宗)。
SELECT * FROM flfl START WITH ID = 6758 CONNECT BY PRIOR sjflid = ID;
这里查找的就是ID为6758的所有直属父节点,打个比方就是找到一个人的父亲、祖父等。但是值得注意的是这个查询出来的结果的顺序是先列出子类节点再列出父类节点,姑且认为是个倒序吧。
上面列出两个树型查询方式,第3条语句和第5条语句,这两条语句之间的区别在于prior关键字的位置不同,所以决定了查询的方式不同。 当sjflid = PRIOR ID时,数据库会根据当前的ID迭代出sjflid与该ID相同的记录,所以查询的结果是迭代出了所有的子类记录;而PRIOR ID = sjflid时,数据库会跟据当前的sjflid来迭代出与当前的sjflid相同的id的记录,所以查询出来的结果就是所有的父类结果。
以下是一系列针对树结构的更深层次的查询,这里的查询不一定是最优的查询方式,或许只是其中的一种实现而已。
6.查询一个节点的兄弟节点(亲兄弟)。
SELECT a.*
FROM flfl a
WHERE EXISTS (SELECT *
FROM flfl b
WHERE a.sjflid = b.sjflid AND b.ID = 6757);
这里查询的就是与ID为6757的节点同属一个父节点的节点了,就好比亲兄弟了。
7.查询与一个节点同级的节点(族兄弟)。 如果在表中设置了级别的字段,上表中的FLJB,那么在做这类查询时会很轻松,同一级别的就是与那个节点同级的,在这里列出不使用该字段时的实现!
WITH tmp AS
(SELECT a.*, LEVEL lev
FROM flfl a
START WITH a.sjflid IS NULL
CONNECT BY a.sjflid = PRIOR a.ID)
SELECT *
FROM tmp
WHERE lev = (SELECT lev
FROM tmp
WHERE ID = 819394)
这里使用两个技巧,一个是使用了LEVEL来标识每个节点在表中的级别,还有就是使用with语法模拟出了一张带有级别的临时表。
8.查询一个节点的父节点的的兄弟节点(伯父与叔父)。
WITH tmp AS
(SELECT flfl.*, LEVEL lev
FROM flfl
START WITH sjflid IS NULL
CONNECT BY sjflid = PRIOR ID)
SELECT b.*
FROM tmp b,
(SELECT *
FROM tmp
WHERE ID = 7004 AND lev = 2) a
WHERE b.lev = 1
UNION ALL
SELECT *
FROM tmp
WHERE sjflid = (SELECT DISTINCT x.ID
FROM tmp x,
tmp y,
(SELECT *
FROM tmp
WHERE ID = 7004 AND lev > 2) z
WHERE y.ID = z.sjflid AND x.ID = y.sjflid);
这里查询分成以下几步。首先,将第7个一样,将全表都使用临时表加上级别;其次,根据级别来判断有几种类型,以上文中举的例子来说,有三种情况:(1)当前节点为顶级节点,即查询出来的lev值为1,那么它没有上级节点,不予考虑。(2)当前节点为2级节点,查询出来的lev值为2,那么就只要保证lev级别为1的就是其上级节点的兄弟节点。(3)其它情况就是3以及以上级别,那么就要选查询出来其上级的上级节点(祖父),再来判断祖父的下级节点都是属于该节点的上级节点的兄弟节点。 最后,就是使用UNION将查询出来的结果进行结合起来,形成结果集。
9.查询一个节点的父节点的同级节点(族叔)。
这个其实跟第7种情况是相同的。
WITH tmp AS
(SELECT a.*, LEVEL lev
FROM flfl a
START WITH a.sjflid IS NULL
CONNECT BY a.sjflid = PRIOR a.ID)
SELECT *
FROM tmp
WHERE lev = (SELECT lev
FROM tmp
WHERE ID = 819394) - 1
只需要做个级别判断就成了。
基本上,常见的查询在里面了,不常见的也有部分了。其中,查询的内容都是节点的基本信息,都是数据表中的基本字段,但是在树查询中还有些特殊需求,是对查询数据进行了处理的,常见的包括列出树路径等。
补充一个概念,对于数据库来说,根节点并不一定是在数据库中设计的顶级节点,对于数据库来说,根节点就是start with开始的地方。
下面列出的是一些与树相关的特殊需求。
10.名称要列出名称全部路径。
这里常见的有两种情况,一种是是从顶级列出,直到当前节点的名称(或者其它属性);一种是从当前节点列出,直到顶级节点的名称(或其它属性)。举地址为例:国内的习惯是从省开始、到市、到县、到居委会的,而国外的习惯正好相反(老师说的,还没接过国外的邮件,谁能寄个瞅瞅 )。
从顶部开始:
SELECT SYS_CONNECT_BY_PATH (mc, '/')
FROM flfl
WHERE ID = 6498
START WITH sjflid IS NULL
CONNECT BY sjflid = PRIOR ID;
从当前节点开始:
SELECT SYS_CONNECT_BY_PATH (mc, '/')
FROM flfl
START WITH ID = 6498
CONNECT BY PRIOR sjflid = ID;
在这里我又不得不放个牢骚了。oracle只提供了一个sys_connect_by_path函数,却忘了字符串的连接的顺序。在上面的例子中,第一个SQL是从根节点开始遍历,而第二个SQL是直接找到当前节点,从效率上来说已经是千差万别,更关键的是第一个SQL只能选择一个节点,而第二个SQL却是遍历出了一颗树来。再次PS一下。
sys_connect_by_path函数就是从start with开始的地方开始遍历,并记下其遍历到的节点,start with开始的地方被视为根节点,将遍历到的路径根据函数中的分隔符,组成一个新的字符串,这个功能还是很强大的。
11.列出当前节点的根节点。
在前面说过,根节点就是start with开始的地方。
SELECT CONNECT_BY_ROOT mc, flfl.*
FROM flfl
START WITH ID = 6498
CONNECT BY PRIOR sjflid = ID;
connect_by_root函数用来列的前面,记录的是当前节点的根节点的内容。
12.列出当前节点是否为叶子。
这个比较常见,尤其在动态目录中,在查出的内容是否还有下级节点时,这个函数是很适用的。
SELECT CONNECT_BY_ISLEAF, flfl.*
FROM flfl
START WITH sjflid IS NULL
CONNECT BY sjflid = PRIOR ID;
connect_by_isleaf函数用来判断当前节点是否包含下级节点,如果包含的话,说明不是叶子节点,这里返回0;反之,如果不包含下级节点,这里返回1。
至此,oracle树型查询基本上讲完了,以上的例子中的数据是使用到做过的项目中的数据,因为里面的内容可能不好理解,所以就全部用一些新的例子来进行阐述。以上所有SQL都在本机上测试通过,也都能实现相应的功能,但是并不能保证是解决这类问题的最优方案(如第8条明显写成存储过程会更好),如果谁有更好的解决方案、或者有关oracle树查询的任何问题,欢迎留言讨论,以上的SQL有什么问题也欢迎大家留言批评。
SQL的四种连接-左外连接、右外连接、内连接、全连接
今天在看一个遗留系统的数据表的时候发现平时查找的视图是FULL OUT JOIN的,导致平时的数据记录要进行一些限制性处理,其实也可以设置视图各表为右外连接并在视图上设置各列的排序和筛选条件就可以达到效果。
联接条件可在FROM或WHERE子句中指定,建议在FROM子句中指定联接条件。WHERE和HAVING子句也可以包含搜索条件,以进一步筛选联接条件所选的行。
联接可分为以下几类:
1、内联接(典型的联接运算,使用像 = 或 <> 之类的比较运算符)。包括相等联接和自然联接。
内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。例如,检索 students和courses表中学生标识号相同的所有行。
2、外联接。外联接可以是左向外联接、右向外联接或完整外部联接。
在 FROM子句中指定外联接时,可以由下列几组关键字中的一组指定:
1)LEFT JOIN或LEFT OUTER JOIN
左向外联接的结果集包括 LEFT OUTER子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
2)RIGHT JOIN 或 RIGHT OUTER JOIN
右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
3)FULL JOIN 或 FULL OUTER JOIN
完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
3、交叉联接
交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
FROM 子句中的表或视图可通过内联接或完整外部联接按任意顺序指定;但是,用左或右向外联接指定表或视图时,表或视图的顺序很重要。有关使用左或右向外联接排列表的更多信息,请参见使用外联接。
例子:
-------------------------------------------------
a表 id name b表 id job parent_id
1 张3 1 23 1
2 李四 2 34 2
3 王武 3 34 4
a.id同parent_id 存在关系
--------------------------------------------------
1) 内连接
select a.*,b.* from a inner join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
2)左连接
select a.*,b.* from a left join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
3 王武 null
3) 右连接
select a.*,b.* from a right join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
null 3 34 4
4) 完全连接
select a.*,b.* from a full join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
null 3 34 4
3 王武 null
oracle 利用 rownum分页的方法
不排序分页:
select *
from (select rownum as num,t.ID, t.NAME, t.MODEL, t.DEPTH, t.FATHER, t.PCS
from v_za_bzdzk t
where t.FATHER = '510502'
and PCS like '%510502400000%'
and depth = '2'
and rownum<=30) gg
where num >= 16
排序分页:
select *
from (select rownum as num, gg.*
from (select t.ID, t.NAME, t.MODEL, t.DEPTH, t.FATHER, t.PCS
from v_za_bzdzk t
where t.FATHER = '510502'
and PCS like '%510502400000%'
and depth = '2'
order by ORDERNAME) gg
where Rownum <= 30
) tt
where tt.num >= 16
破解移植wm_concat
--
/*****************************************
功能: 破解移植wm_concat
作者:xcp
日期:2011-07-14
说明:wm_concat出现在oracle10g版本中,不仅是加密的,而且是在一个单独的用户中,不方便使用。并且10g以前的版本也用不上。经过破解移植,可以方便大家使用
将下边的一个type,一个函数的创建脚本执行,就可以构建自己的wm_concat(这里叫zh_concat)。使用方法同wm_concat:
******************************************/
--type:
create or replace TYPE za_concat_im
AUTHID CURRENT_USER AS OBJECT
(
CURR_STR VARCHAR2(32767),
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT za_concat_im) RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT za_concat_im,
P1 IN VARCHAR2) RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN za_concat_im,
RETURNVALUE OUT VARCHAR2,
FLAGS IN NUMBER)
RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT za_concat_im,
SCTX2 IN za_concat_im) RETURN NUMBER
);
/
create or replace TYPE BODY za_concat_im
IS
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT za_concat_im)
RETURN NUMBER
IS
BEGIN
SCTX := za_concat_im(NULL) ;
RETURN ODCICONST.SUCCESS;
END;
MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT za_concat_im,
P1 IN VARCHAR2)
RETURN NUMBER
IS
BEGIN
IF(CURR_STR IS NOT NULL) THEN
CURR_STR := CURR_STR || ',' || P1;
ELSE
CURR_STR := P1;
END IF;
RETURN ODCICONST.SUCCESS;
END;
MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN za_concat_im,
RETURNVALUE OUT VARCHAR2,
FLAGS IN NUMBER)
RETURN NUMBER
IS
BEGIN
RETURNVALUE := CURR_STR ;
RETURN ODCICONST.SUCCESS;
END;
MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT za_concat_im,
SCTX2 IN za_concat_im)
RETURN NUMBER
IS
BEGIN
IF(SCTX2.CURR_STR IS NOT NULL) THEN
SELF.CURR_STR := SELF.CURR_STR || ',' || SCTX2.CURR_STR ;
END IF;
RETURN ODCICONST.SUCCESS;
END;
END;
/
--函数:
create or replace FUNCTION za_concat(P1 VARCHAR2)
RETURN VARCHAR2 AGGREGATE USING za_concat_im ;
/
oracle复制表的sql语句
1、只复制表结构的sql
create table b as select * from a where 1<>1
2、即复制表结构又复制表中数据的sql
create table b as select * from a
3、复制表的制定字段的sql
create table b as select row_id,name,age from a where 1<>1//前提是row_id,name,age都是a表的列
4、复制表的指定字段及这些指定字段的数据的sql
create table b as select row_id,name,age from a
以上语句虽然能够很容易的根据a表结构复制创建b表,但是a表的索引等却复制不了,需要在b中手动建立。
5、insert into 会将查询结果保存到已经存在的表中
insert into t2(column1, column2, ....) select column1, column2, .... from t1
ibatis的$与#的区别
# 会根据不同类型而生产字符串,如 String 类型 : aa ---> 'aa'
$ 表示原生类型,如 String 类型 : aa ---> aa
每天遇到问题记录
1.#变量名# 会转化为 jdbc 的 ?, 比如 select * from user where name=#name# 会转化为jdbc的 select * from user where name=?,把?参数设置为name的值,而$变量名$就直接把 $name$替换为 name的内容, 也就是由可能 select * from user where name=$name$ 如果name为 "' ' or 1 = 1", 那么这样就有可能导致sql注入,所以ibatis用#比$好,不会造成sql注入。
2.ibatis中的参数传入的值参数比较多,最好用bean方式传入,也就是通过set/get取值的方式给sql map注入参数,不要用hashmap结构传入,每次用hashmap传入会占用比较多的内容。如果参数少,用hashmap也比较方便简单。但是对传入参数的判断,用bean方式比较容易检测发现,配置也能够统一配置。
select * from user u where u.username=#name# //自动加''号
select * from user u where age=$age$ and username=$username$ // username=$username$ 会出错
select * from $username$ u where age =$age$ order by $filed$ //$主要用处是动态表名 ,指定排序字端
oracle学习
一、服务说明
1、OracleDBCConsoleeasy888 就是em等的控制台的开启
2、OracleOraDb10g_home3iSQL*Plus就是浏览器里面的sqlplus调试工具
3、OracleOraDb10g_home3TNSListener就是监听程序
4、OracleServiceeasy888就是Oracle服务
二、本地文件说明(sqlplus "sys/yjtc888 as sysdba")
1. oradata 存放控制文件,数据文件,日志文件
a. 控制文件
1).查看表结构:desc v$controlfile
2).查看记录 select status,name from v$controlfile
b. 数据文件
1).查看表结构:desc v$datafile
2).查看记录 select status,name from v$datafile
c.日志文件
1).查看表结构:desc v$logfile
2).查看记录 select status,name from v$logfile
2.flash_recovery_area 自动保存数据库目录 记录日志文件
3.db_3等文件,在其下面配置listener.ora、sqlnet.ora、tnsnames.ora
三、ORACLE10g服务器端自带工具集
(一)、配置和移植工具
1.Oracle Administration Assistant for window
作用:管理ORACEL本地账户
2.Database Configuration Assistant
作用:数据库创建、配置、删除、集群配置等
每个数据库都会有三个服务:OracleService[XXX]、OracleDBConsole[XXX]、OracleJobScheduler[XXX]
3.Database Upgrade Assistant
作用:将oracle8、9、10等版本数据库升级到10g2版本。
4.Locale Builder
作用:管理地域、语言、字符集等本地化内容。
5.Microsoft ODBC 管理员
作用:不用解释了吧,创建连接字符串的。
6.Net Configuration Assistant
作用:网络服务,包括监听程序配置、命名方法配置、本地NET服务名配置和目录使用配置。
默认监听:Oracle[XXX]10g_home1TNSListener
添加1522端口监听:Oracle[XXX]10g_home1TNSListener[新监听名称]
7.Net Manager
作用:管理服务命名、监听程序等。
(二)、应用程序开发
1.SQL Plus
作用:这个我就不多说了。
(三)、集成管理工具
1.Oracle Directory Manager
作用:ORACLE目录管理
2.Wallet Manager
作用:ORACLE钱包管理,我也没用过。
(四)、Oracle Installation Products
1.Universal Installer
三种SQL分页法
三种SQL分页法
表中主键必须为标识列,[ID] int IDENTITY (1,1)
1.分页方案一:(利用Not In和SELECT TOP分页)
语句形式:
SELECT TOP 页记录数量 *
FROM 表名
WHERE (ID NOT IN
(SELECT TOP (每页行数*(页数-1)) ID
FROM 表名
ORDER BY ID))
ORDER BY ID
//自己还可以加上一些查询条件
例:
select top 2 *
from Sys_Material_Type
where (MT_ID not in
(select top (2*(3-1)) MT_ID from Sys_Material_Type order by MT_ID))
order by MT_ID
2.分页方案二:(利用ID大于多少和SELECT TOP分页)
语句形式:
SELECT TOP 每页记录数量 *
FROM 表名
WHERE (ID >
(SELECT MAX(id)
FROM (SELECT TOP 每页行数*页数 id FROM 表
ORDER BY id) AS T)
)
ORDER BY ID
例:
SELECT TOP 2 *
FROM Sys_Material_Type
WHERE (MT_ID >
(SELECT MAX(MT_ID)
FROM (SELECT TOP (2*(3-1)) MT_ID
FROM Sys_Material_Type
ORDER BY MT_ID) AS T))
ORDER BY MT_ID
3.分页方案三:(利用SQL的游标存储过程分页)
create procedure SqlPager
@sqlstr nvarchar(4000), --查询字符串
@currentpage int, --第N页
@pagesize int --每页行数
as
set nocount on
declare @P1 int, --P1是游标的id
@rowcount int
exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1, @rowcount=@rowcount output
select ceiling(1.0*@rowcount/@pagesize) as 总页数--,@rowcount as 总行数,@currentpage as 当前页
set @currentpage=(@currentpage-1)*@pagesize+1
exec sp_cursorfetch @P1,16,@currentpage,@pagesize
exec sp_cursorclose @P1
set nocount off
4.总结:
其它的方案:如果没有主键,可以用临时表,也可以用方案三做,但是效率会低。
建议优化的时候,加上主键和索引,查询效率会提高。
通过SQL 查询分析器,显示比较:我的结论是:
分页方案二:(利用ID大于多少和SELECT TOP分页)效率最高,需要拼接SQL语句
分页方案一:(利用Not In和SELECT TOP分页) 效率次之,需要拼接SQL语句
分页方案三:(利用SQL的游标存储过程分页) 效率最差,但是最为通用
本文转摘自『IT学习者』http://www.itlearner.com/article/2007/3740.shtml
create index I_INFO_CLASSID on b_info(CLASSID); create index I_INFO_CLASSID_REVERSE on b_info(reverse(CLASSID)); analyze table b_info compute statistics for table for all indexes; --都使用索引 select * from b_info where classid like '1E92C5409E99484%'; --都使用索引 select * from b_info where reverse(classid) like reverse('%33B27');
Oracle中的动态SQL
select的时候就不需要returning 例: execute immediate T_SQL into T_COUNT;
oracle查询所有用户表的表名、主键名称、索引、外键等
1、查找表的所有索引(包括索引名,类型,构成列):
select t.*,i.index_type from user_ind_columns t,user_indexes i where t.index_name = i.index_name and t.table_name = i.table_name and t.table_name = 要查询的表
2、查找表的主键(包括名称,构成列):
select cu.* from user_cons_columns cu, user_constraints au where cu.constraint_name = au.constraint_name and au.constraint_type = 'P' and au.table_name = 要查询的表
3、查找表的唯一性约束(包括名称,构成列):
select column_name from user_cons_columns cu, user_constraints au where cu.constraint_name = au.constraint_name and au.constraint_type = 'U' and au.table_name = 要查询的表
4、查找表的外键(包括名称,引用表的表名和对应的键名,下面是分成多步查询):
select * from user_constraints c where c.constraint_type = 'R' and c.table_name = 要查询的表
5、查询外键约束的列名:
select * from user_cons_columns cl where cl.constraint_name = 外键名称
6、查询引用表的键的列名:
select * from user_cons_columns cl where cl.constraint_name = 外键引用表的键名
实例:
7、查询没有建立主键的表
select u.table_name, u.num_rows from user_tables u where not exists (select cu.table_name from user_cons_columns cu, user_constraints au where cu.constraint_name = au.constraint_name and au.constraint_type = 'P' and au.table_name = u.table_name) and u.num_rows is not null order by u.num_rows desc;
8、查询表记录中有空值的索引字段
-- Create table create table TEMP_INDEX ( ID VARCHAR2(32), TABLE_NAME VARCHAR2(100), COLUMN_NAME VARCHAR2(100), INDEX_NAME VARCHAR2(100), SCSJ DATE ) tablespace JG_ZFGFH pctfree 10 initrans 1 maxtrans 255 storage ( initial 16 minextents 1 maxextents unlimited ); -- Add comments to the table comment on table TEMP_INDEX is '放入索引值有空的表和列'; -- Add comments to the columns comment on column TEMP_INDEX.ID is '自动生成'; comment on column TEMP_INDEX.TABLE_NAME is '表名'; comment on column TEMP_INDEX.COLUMN_NAME is '字段名称'; comment on column TEMP_INDEX.INDEX_NAME is '索引名称'; comment on column TEMP_INDEX.SCSJ is '生成时间'; create or replace procedure P_PROCESS_INDEX /********************************************************************************* -----------功能:得到表索引字段中有空值字段 -----------作者: Xcp -----------创建日期:2013-02-20 -----------版本 v1.0 *******************************************************************************/ is cursor T_INDEX_CURSOR is select i.table_name, t.column_name, t.index_name from user_ind_columns t, user_indexes i, user_tab_cols c where t.index_name = i.index_name and t.column_name = c.column_name and t.table_name = i.table_name and c.table_name = i.table_name order by c.column_id; T_COUNT number:=0; T_SQL varchar2(1000); T_PRE_TABLE_NAME varchar2(100); begin --清空记录保存表 delete from TEMP_INDEX; commit; --重新清理 for T_INDEX in T_INDEX_CURSOR loop --事务控制,每个表提交一次 if T_PRE_TABLE_NAME is null then T_PRE_TABLE_NAME:=T_INDEX.Table_Name; elsif T_PRE_TABLE_NAME<>T_INDEX.Table_Name then commit; end if; --求是该索引字段是否有空 begin T_SQL:='select count(1) from '||T_INDEX.TABLE_NAME||' where '||T_INDEX.Column_Name||' is null ' ; --dbms_output.put_line(T_SQL); execute immediate T_SQL into T_COUNT; --dbms_output.put_line(T_COUNT) ; if T_COUNT>0 then insert into TEMP_INDEX values(sys_guid(),T_INDEX.Table_Name,T_INDEX.COLUMN_NAME,T_INDEX.Index_Name,sysdate); end if; exception when others then dbms_output.put_line('NO DATA FOUND!'); end; end loop; --事务控制,最后一个表的事务 if T_INDEX_CURSOR%NOTFOUND then commit; end if; end P_PROCESS_INDEX;
FORALL语句、FORALL语句自动回滚
FORALL语句的一个关键性改进,它可大大简化代码,并且对于那些要在PL/SQL程序中更新很多行数据的程序来说,它可显著提高其性能。
1:
用FORALL来增强DML的处理能力
Oracle为Oracle8i中的PL/SQL引入了两个新的数据操纵语言(DML)语句:BULK COLLECT和FORALL。这两个语句在PL/SQL内部进行一种数组处理
;BULK COLLECT提供对数据的高速检索,FORALL可大大改进INSERT、UPDATE和DELETE操作的性能。Oracle数据库使用这些语句大大减少了
PL/SQL与SQL语句执行引擎的环境切换次数,从而使其性能有了显著提高。
使用BULK COLLECT,你可以将多个行引入一个或多个集合中,而不是单独变量或记录中。下面这个BULK COLLECT的实例是将标题中包含
有"PL/SQL"的所有书籍检索出来并置于记录的一个关联数组中,它们都位于通向该数据库的单一通道中。
DECLARE
TYPE books_aat
IS TABLE OF book%ROWTYPE
INDEX BY PLS_INTEGER;
books books_aat;
BEGIN
SELECT *
BULK COLLECT INTO book
FROM books
WHERE title LIKE '%PL/SQL%';
...
END;
类似地,FORALL将数据从一个PL/SQL集合传送给指定的使用集合的表。下面的代码实例给出一个过程,即接收书籍信息的一个嵌套表,并将该
集合(绑定数组)的全部内容插入该书籍表中。注意,这个例子还利用了Oracle9i的FORALL的增强功能,可以将一条记录直接插入到表中。
BULK COLLECT和FORALL都非常有用,它们不仅提高了性能,而且还简化了为PL/SQL中的SQL操作所编写的代码。下面的多行FORALL INSERT相当
清楚地说明了为什么PL/SQL被认为是Oracle数据库的最佳编程语言。
CREATE TYPE books_nt
IS TABLE OF book%ROWTYPE;
/
CREATE OR REPLACE PROCEDURE add_books (
books_in IN books_nt)
IS
BEGIN
FORALL book_index
IN books_in.FIRST .. books_in.LAST
INSERT INTO book
VALUES books_in(book_index);
...
END;
不过在Oracle数据库10g之前,以FORAll方式使用集合有一个重要的限制:该数据库从IN范围子句中的第一行到最后一行,依次读取集合的内容
。如果在该范围内遇到一个未定义的行,Oracle数据库将引发ORA-22160异常事件:
ORA-22160: element at index [N] does not exist
对于FORALL的简单应用,这一规则不会引起任何麻烦。但是,如果想尽可能地充分利用FORALL,那么要求任意FORALL驱动数组都要依次填充可
能会增加程序的复杂性并降低性能。
在Oracle数据库10g中,PL/SQL现在在FORALL语句中提供了两个新子句:INDICES OF与VALUES OF,它们使你能够仔细选择驱动数组中该由扩展
DML语句来处理的行。
当绑定数组为稀疏数组或者包含有间隙时,INDICES OF会非常有用。该语句的语法结构为:
FORALL indx IN INDICES
OF sparse_collection
INSERT INTO my_table
VALUES sparse_collection (indx);
VALUES OF用于一种不同的情况:绑定数组可以是稀疏数组,也可以不是,但我只想使用该数组中元素的一个子集。那么我就可以使用VALUES
OF来指向我希望在DML操作中使用的值。该语句的语法结构为:
FORALL indx IN VALUES OF pointer_array
INSERT INTO my_table
VALUES binding_array (indx);
不用FOR循环而改用FORALL
假定我需要编写一个程序,对合格员工(由comp_analysis.is_eligible函数确定)加薪,编写关于不符合加薪条件的员工的报告并写入
employee_history表。我在一个非常大的公司工作;我们的员工非常非常多。
对于一位PL/SQL开发人员来说,这并不是一项十分困难的工作。我甚至不需要使用BULK COLLECT或FORALL就可以完成这项工作,如清单 1所示
,我使用一个CURSOR FOR循环和单独的INSERT及UPDATE语句。这样的代码简洁明了;不幸地是,我花了10分钟来运行此代码,我的"老式"方法
要运行30分钟或更长时间。
清单 1:
CREATE OR REPLACE PROCEDURE give_raises_in_department (
dept_in IN employee.department_id%TYPE
, newsal IN employee.salary%TYPE
)
IS
CURSOR emp_cur
IS
SELECT employee_id, salary, hire_date
FROM employee
WHERE department_id = dept_in;
BEGIN
FOR emp_rec IN emp_cur
LOOP
IF comp_analysis.is_eligible (emp_rec.employee_id)
THEN
UPDATE employee
SET salary = newsal
WHERE employee_id = emp_rec.employee_id;
ELSE
INSERT INTO employee_history
(employee_id, salary
, hire_date, activity
)
VALUES (emp_rec.employee_id, emp_rec.salary
, emp_rec.hire_date, 'RAISE DENIED'
);
END IF;
END LOOP;
END give_raises_in_department;
好在我公司的数据库升级到了Oracle9i,而且更幸运的是,在最近的Oracle研讨会上(以及Oracle技术网站提供的非常不错的演示中)我了解
到了批量处理方法。所以我决定使用集合与批量处理方法重新编写程序。写好的程序如清单 2所示。
清单 2:
1 CREATE OR REPLACE PROCEDURE give_raises_in_department (
2 dept_in IN employee.department_id%TYPE
3 , newsal IN employee.salary%TYPE
4 )
5 IS
6 TYPE employee_aat IS TABLE OF employee.employee_id%TYPE
7 INDEX BY PLS_INTEGER;
8 TYPE salary_aat IS TABLE OF employee.salary%TYPE
9 INDEX BY PLS_INTEGER;
10 TYPE hire_date_aat IS TABLE OF employee.hire_date%TYPE
11 INDEX BY PLS_INTEGER;
12
13 employee_ids employee_aat;
14 salaries salary_aat;
15 hire_dates hire_date_aat;
16
17 approved_employee_ids employee_aat;
18
19 denied_employee_ids employee_aat;
20 denied_salaries salary_aat;
21 denied_hire_dates hire_date_aat;
22
23 PROCEDURE retrieve_employee_info
24 IS
25 BEGIN
26 SELECT employee_id, salary, hire_date
27 BULK COLLECT INTO employee_ids, salaries, hire_dates
28 FROM employee
29 WHERE department_id = dept_in;
30 END;
31
32 PROCEDURE partition_by_eligibility
33 IS
34 BEGIN
35 FOR indx IN employee_ids.FIRST .. employee_ids.LAST
36 LOOP
37 IF comp_analysis.is_eligible (employee_ids (indx))
38 THEN
39 approved_employee_ids (indx) := employee_ids (indx);
40 ELSE
41 denied_employee_ids (indx) := employee_ids (indx);
42 denied_salaries (indx) := salaries (indx);
43 denied_hire_dates (indx) := hire_dates (indx);
44 END IF;
45 END LOOP;
46 END;
47
48 PROCEDURE add_to_history
49 IS
50 BEGIN
51 FORALL indx IN denied_employee_ids.FIRST .. denied_employee_ids.LAST
52 INSERT INTO employee_history
53 (employee_id
54 , salary
55 , hire_date, activity
56 )
57 VALUES (denied_employee_ids (indx)
58 , denied_salaries (indx)
59 , denied_hire_dates (indx), 'RAISE DENIED'
60 );
61 END;
62
63 PROCEDURE give_the_raise
64 IS
65 BEGIN
66 FORALL indx IN approved_employee_ids.FIRST .. approved_employee_ids.LAST
67 UPDATE employee
68 SET salary = newsal
69 WHERE employee_id = approved_employee_ids (indx);
70 END;
71 BEGIN
72 retrieve_employee_info;
73 partition_by_eligibility;
74 add_to_history;
75 give_the_raise;
76 END give_raises_in_department;
扫一眼清单1 和清单2 就会清楚地认识到:改用集合和批量处理方法将增加代码量和复杂性。但是,如果你需要大幅度提升性能,这还是值得
的。下面,我们不看这些代码,我们来看一看当使用FORALL时,用什么来处理CURSOR FOR循环内的条件逻辑。
定义集合类型与集合
在清单 2中,声明段的第一部分(第6行至第11行)定义了几种不同的集合类型,与我将从员工表检索出的列相对应。我更喜欢基于employee%
ROWTYPE来声明一个集合类型,但是FORALL还不支持对某些记录集合的操作,在这样的记录中,我将引用个别字段。所以,我还必须为员工ID、
薪金和雇用日期分别声明其各自的集合。
接下来为每一列声明所需的集合(第13行至第21行)。首先定义与所查询列相对应的集合(第13行至第15行):
employee_ids employee_aat;
salaries salary_aat;
hire_dates hire_date_aat;
然后我需要一个新的集合,用于存放已被批准加薪的员工的ID(第17行):
approved_employee_ids employee_aat;
最后,我再为每一列声明一个集合(第19行至第21行),用于记录没有加薪资格的员工:
denied_employee_ids employee_aat;
denied_salaries salary_aat;
denied_hire_dates hire_date_aat;
深入了解代码
数据结构确定后,我们现在跳过该程序的执行部分(第72行至第75行),了解如何使用这些集合来加速进程。
retrieve_employee_info;
partition_by_eligibility;
add_to_history;
give_the_raise;
我编写此程序使用了逐步细化法(也被称为"自顶向下设计")。所以执行部分不是很长,也不难理解,只有四行,按名称对过程中的每一步进
行了描述。首先检索员工信息(指定部门的所有员工)。然后进行划分,将要加薪和不予加薪的员工区分出来。完成之后,我就可以将那些不
予加薪的员工添加至员工历史表中,对其他员工进行加薪。
以这种方式编写代码使最终结果的可读性大大增强。因而我可以深入到该程序中对我有意义的任何部分。
有了已声明的集合,我现在就可以使用BULK COLLECT来检索员工信息(第23行至第30行)。这一部分有效地替代了CURSOR FOR循环。至此,数
据被加载到集合中。
划分逻辑(第32行至第46行)要求对刚刚填充的集合中的每一行进行检查,看其是否符合加薪条件。如果符合,我就将该员工ID从查询填充的
集合复制到符合条件的员工的集合。如果不符合,则复制该员工ID、薪金和雇用日期,因为这些都需要插入到employee_history表中。
初始数据现在已被分为两个集合,可以将其分别用作两个不同的FORALL语句(分别从第51行和第66行开始)的驱动器。我将不合格员工的集合
中的数据批量插入到employee_history(add_to_history)表中,并通过give_the_raise过程,在employee表中批量更新合格员工的信息。
最后再仔细地看一看add_to_history(第48行至第61行),以此来结束对这个重新编写的程序的分析。FORALL语句(第51行)包含一个IN子句
,它指定了要用于批量INSERT的行号范围。在对程序进行第二次重写的说明中,我将把用于定义范围的集合称为"驱动集合"。但在
add_to_history的这一版本中,我简单地假定: 使用在denied_employee_ids中定义的所有行。在INSERT自身内部,关于不合格员工的三个集
合都会被用到;我将把这些集合称为"数据集合"。可以看到,驱动集合与数据集合无需匹配。在学习Oracle数据库10g的新特性时,这是一个关
键点。
结果,清单 2 的行数大约是清单 1行数的2倍,但是清单 2 中的代码会在要求的时间内运行。在使用Oracle数据库10g之前,在这种情况下,
我只会对能够在这一时间内运行代码并开始下一个任务这一点感到高兴。
不过,有了Oracle数据库10g中最新版的PL/SQL,现在我就可以在性能、可读性和代码量方面作出更多的改进。
将VALUES OF用于此过程
在Oracle数据库10g中,可以指定FORALL语句使用的驱动集合中的行的子集。可以使用以下两种方法之一来定义该子集:
将数据集合中的行号与驱动集合中的行号进行匹配。你需要使用INDICES OF子句。
将数据集合中的行号与驱动集合中所定义行中找到的值进行匹配。这需要使用VALUES OF子句。
在对give_raises_in_department进行第二次和最后一次改写中我将使用VALUES OF子句。清单 3 包含这个版本的全部代码。我将略过这一程序
中与前一版本相同的部分。
从声明集合开始,请注意我不再另外定义集合来存放合格的和不合格的员工信息,而是在清单 3 (第17行至第21行)中声明两个"引导"集合:
一个用于符合加薪要求的员工,另一个用于不符合加薪要求的员工。这两个集合的数据类型都是布尔型;不久将会看到,这些集合的数据类型
与FORALL语句毫无关系。FORALL语句只关心定义了哪些行。 在员工表中拥有50 000行信息的give_raises_in_department的三种执行方法的占
用时间 执行方法 用时
CURSOR FOR循环 00:00:38.01
Oracle数据库10g之前的批量处理 00:00:06.09
Oracle数据库10g的批量处理 00:00:02.06
在员工表中拥有100,000行数据的give_raises_in_department的三种执行方法的占用时间 执行方法 用时
CURSOR FOR循环 00:00:58.01
Oracle数据库10g之前的批量处理 00:00:12.00
Oracle数据库10g的批量处理 00:00:05.05
表1:处理50,000行和100,000行数据的用时测试结果
retrieve_employee_info子程序与前面的相同,但是对数据进行划分的方式完全不同(第32行至第44行)。我没有将记录从一个集合复制到另
一个集合(这个操作相对较慢),而只是确定与员工ID集合中的行号相匹配的相应引导集合中的行(通过为其指定一个TRUE值)。
现在可以在两个不同FORALL语句(由第49行和第65行开始)中,将approved_list和denied_list集合用作驱动集合。
为了插入到employee_history表中,我使用了如下语句:
FORALL indx IN VALUES OF denied_list
为了进行更新(给员工进行加薪),我使用这一格式:
FORALL indx IN VALUES OF approved_list
在这两个DML语句中,数据集合是在BULK COLLECT 检索步骤中填充的最初的集合;没有进行过复制。利用VALUES OF,Oracle数据库在这些数据
集合的行中进行筛选,仅使用行号与驱动集合中行号相匹配的行
利用本程序中的VALUES OF,可以避免复制对全部记录进行复制,而是用行号的一个简单列表来替换它们。对于大型数组,进行这些复制的开销
是非常可观的。为了测试Oracle数据库10g的优越性,我装入employee表并对50,000行和100,000行的数据运行测试。为了模拟更多的现实情况,我将Oracle数据库10g之前的批量处理的执行方法作了修改以进行集合内容的多次复制。然后我使用SQL*Plus SET TIMING ON来显示运行各个不同的执行方法所用的时间。表 1 给出了结果。
从这些时间测定得到的结论非常清楚:由单个DML语句变为批量处理将大幅缩短耗用时间,数据为50,000行时的用时由38秒减为6秒,数据为
100,000行时的用时由58秒减为12秒。而且,通过使用VALUES OF来避免复制数据,我可以将用时缩短一半左右。
即使没有性能上的改进,VALUES OF及其同类子句--INDICES OF也提高了PL/SQL语言的灵活性,使开发人员能够更轻松地编写出更直观和更容易维护的代码。
在产品寿命这一点上,PL/SQL是一种成熟且功能强大的语言。因而,其很多新特性都是逐渐增加和改进而成的。不过,这些新特性还是使应用
程序的性能和开发人员的开发效率有了重大改变。VALUES OF就是这种特性的一个很好的例子。
使用Bulk Collect提高Oracle查询效率
使用Bulk Collect提高Oracle查询效率
Oracle8i中首次引入了Bulk Collect特性,该特性可以让我们在PL/SQL中能使用批查询,批查询在某些情况下能显著提高查询效率。现在,我们对该特性进行一些简单的测试和分析。
一、首先,我们创建一个表,并插入100000条记录
在SQL/Plus中执行下列脚本:
drop table empl_tbl
/
create table empl_tbl(last_name varchar2(20),
first_name varchar2(10),
salary number(10))
/
begin
for i in 30000..1029999 loop
insert into empl_tbl(last_name,first_name,salary) values('carl'||(i-3000),'wu'||(1003000-i),i);
end loop;
end;
/
commit
/
select count(*) from empl_tbl;
/
二、使用三种方法计算表中某一字段含有多少个不重复值
2.1 使用常规的Distinct来实现
SQL> select count(distinct last_name) from empl_tbl;
Distinct Last Name
------------------
1000000
Executed in 1.531 seconds
我们可以看到,常规方法需要1.531 秒查出该表中有1000000个不重复的Last_name值。
2.2 使用游标来实现
我们执行下面语句来统计Last_name字段的不重复值个数:
CREATE OR REPLACE PROCEDURE tttt IS
all_rows number(10);
temp_last_name empl_tbl.last_name%type;
begin
all_rows:=0;
temp_last_name:=' ';
for cur in (select last_name from empl_tbl order by last_name) loop
if cur.last_name!=temp_last_name then
all_rows:=all_rows+1;
end if;
temp_last_name:=cur.last_name;
end loop;
dbms_output.put_line('all_rows are '||all_rows);
end;
请注意上面代码中的黑体部分使用了一个For Loop游标,为了提高程序可读性,我们没有显示定义游标变量。
执行结果:
all_rows are 1000000
PL/SQL procedure successfully completed
Executed in 10.39 seconds
游标需要10.396秒才能查出该表中有1000000个不重复的Last_name值,所耗时间是Distinct查询的10倍多。
2.3 使用Bulk Collect批查询来实现
示例代码如下:
declare
all_rows number(10);
--首先,定义一个Index-by表数据类型
type last_name_tab is table of empl_tbl.last_name%type index by binary_integer;
last_name_arr last_name_tab;
--定义一个Index-by表集合变量
temp_last_name empl_tbl.last_name%type;
begin
all_rows:=0;
temp_last_name:=' ';
--使用Bulk Collect批查询来充填集合变量
select last_name bulk collect into last_name_arr from empl_tbl;
for i in 1..last_name_arr.count loop
if temp_last_name!=last_name_arr(i) then
all_rows:=all_rows+1;
end if;
temp_last_name:=last_name_arr(i);
end loop;
dbms_output.put_line('all_rows are '||all_rows);
end;
请注意上面代码中,我们首先定义了一个Index-by表数据类型last_name_tab,然后定义了一个该集合数据类型的变量last_name_arr,最后我们使用Bulk Collect批查询来充填last_name_arr,请注意它
的使用语法。
执行结果:
all_rows are 1000000
PL/SQL procedure successfully completed
Executed in 2.343 seconds
从上面执行结果,我们可以看到,Bulk Collect批查询只需要2.343 秒就能查出该表中有1000000个不重复的Last_name值,所耗时间只有游标查询的1/2.5。
三. 测试结果分析
为什么会出现上面的结果呢?我们可以使用Oracle的SQL_Trace来分析一下结果。在SQL命令行中,使用alter session set sql_trace=true语句打开Oracle的Trace,然后在命令行中执行上面三种查询并使用TKPROF工具生成Trace报告。
3.1 常规Distinct查询结果分析
3.2 游标查询效率分析
3.3 Bulk Collect的查询效率分析
四. 结论
通过上面的测试和分析,我们可以看到Bulk Collect批查询在某种程度上可以提高查询效率,它首先将所需数据读入内存,然后再统计分析,这样就可以提高查询效率。但是,如果Oracle数据库的内存较小,Shared Pool Size不足以保存Bulk Collect批查询结果,那么该方法需要将Bulk Collect的集合结果保存在磁盘上,在这种情况下,Bulk Collect方法的效率反而不如其他两种方法,有兴趣的读者可以进一步测试。
另外,除了Bulk Collect批查询外,我们还可以使用FORALL语句来实现批插入、删除和更新,这在大批量数据操作时可以显著提高执行效率。
oracle_常用系统表
dba_users 数据库用户信息
dba_segments 表段信息
dba_extents 数据区信息
dba_objects 数据库对象信息
dba_tablespaces 数据库表空间信息
dba_data_files 数据文件设置信息
dba_temp_files 临时数据文件信息
dba_rollback_segs 回滚段信息
dba_ts_quotas 用户表空间配额信息
dba_free_space 数据库空闲空间信息
dba_profiles 数据库用户资源限制信息
dba_sys_privs 用户的系统权限信息
dba_tab_privs 用户具有的对象权限信息
dba_col_privs 用户具有的列对象权限信息
dba_role_privs 用户具有的角色信息
dba_audit_trail 审计跟踪记录信息
dba_stmt_audit_opts 审计设置信息
dba_audit_object 对象审计结果信息
dba_audit_session 会话审计结果信息
dba_indexes 用户模式的索引信息
user_开头
user_objects 用户对象信息
user_source 数据库用户的所有资源对象信息
user_segments 用户的表段信息
user_tables 用户的表对象信息
user_tab_columns 用户的表列信息
user_constraints 用户的对象约束信息
user_sys_privs 当前用户的系统权限信息
user_tab_privs 当前用户的对象权限信息
user_col_privs 当前用户的表列权限信息
user_role_privs 当前用户的角色权限信息
user_indexes 用户的索引信息
user_ind_columns 用户的索引对应的表列信息
user_cons_columns 用户的约束对应的表列信息
user_clusters 用户的所有簇信息
user_clu_columns 用户的簇所包含的内容信息
user_cluster_hash_expressions 散列簇的信息
v$开头
v$database 数据库信息
v$datafile 数据文件信息
v$controlfile 控制文件信息
v$logfile 重做日志信息
v$instance 数据库实例信息
v$log 日志组信息
v$loghist 日志历史信息
v$sga 数据库SGA信息
v$parameter 初始化参数信息
v$process 数据库服务器进程信息
v$bgprocess 数据库后台进程信息
v$controlfile_record_section 控制文件记载的各部分信息
v$thread 线程信息
v$datafile_header 数据文件头所记载的信息
v$archived_log 归档日志信息
v$archive_dest 归档日志的设置信息
v$logmnr_contents 归档日志分析的DML DDL结果信息
v$logmnr_dictionary 日志分析的字典文件信息
v$logmnr_logs 日志分析的日志列表信息
v$tablespace 表空间信息
v$tempfile 临时文件信息
v$filestat 数据文件的I/O统计信息
v$undostat Undo数据信息
v$rollname 在线回滚段信息
v$session 会话信息
v$transaction 事务信息
v$rollstat 回滚段统计信息
v$pwfile_users 特权用户信息
v$sqlarea 当前查询过的sql语句访问过的资源及相关的信息
v$sql 与v$sqlarea基本相同的相关信息
v$sysstat 数据库系统状态信息
all_开头
all_users 数据库所有用户的信息
all_objects 数据库所有的对象的信息
all_def_audit_opts 所有默认的审计设置信息
all_tables 所有的表对象信息
all_indexes 所有的数据库对象索引的信息
session_开头
session_roles 会话的角色信息
session_privs 会话的权限信息
index_开头
index_stats 索引的设置和存储信息
伪表 dual 系统伪列表信息
oracle 查看用户 用户权限 用户表空间 用户默认表空间[转]
1、查看所有用户
select * from dba_users;//dba
select * from all_users;//all
select * from user_users;//当前
2、查看当前用户连接:
select * from v$Session;
3、查看当前用户权限:
select * from session_privs;
4、查看所有用户系统权限
select * from dba_sys_privs;
select * from user_sys_privs;
5、查看所有用户对象权限
select * from dab_tab_privs;
select * from all_tab_privs;
select * from user_tab_privs;
6、查看所有角色
select * from dba_roles;
select * from dba_role_privs;
select * from user_role_privs;
7、查看所有表空间、临时表空间等信息
select tablespace_name,file_name,bytes file_size,autoextensible from dba_data_files;
select tablespace_name,file_name,bytes file_size,autoextensible from dba_temp_files;
8、查看用户表空间使用情况:
select
a.file_id "FileNo",a.tablespace_name "Tablespace_name",
a.bytes "Bytes",a.bytes-sum(nvl(b.bytes,0)) "Used", sum(nvl(b.bytes,0)) "Free",sum(nvl(b.bytes,0))/a.bytes*100 "%free"
from dba_data_files a,dba_free_space b
where a.file_id=b.file_id(+)
group by a.tablespace_name , a.file_id,a.bytes
order by a.tablespace_name;
9、查看用户所有对象
select table_name,num_rows from user_tables;
select * from all_tables;
10、询所有函数和存储过程
select * from dba_source;
select * from user_source;
oracle数据恢复
1.表查询闪回
create table xcp as (select * from b_za_bzdzkxx);
select * from xcp;
select count(1) from xcp;--22001
select count(1) from xcp t where t.dzbh like '510521%';--7011
delete from xcp t where t.dzbh like '510521%';
select count(1) from xcp;--14990
--查找指定时间点前的数据
select count(1) from xcp as of timestamp to_timestamp('2011-12-23 10:49:30','yyyy-MM-dd hh24:mi:ss');--22001
select * from xcp for update;--添加一条记录
select count(1) from xcp;--14991
--恢复指定时间点的前delete数据(将删除恢复时间点后面的数据)
alter table xcp enable row movement;--启动的行移动功能
flashback table xcp to timestamp to_timestamp('2011-12-23 10:49:30,'yyyy-MM-dd hh24:mi:ss');
select count(1) from xcp;--22001
--恢复指定时间点的前delete数据,并保留恢复时间点后面的数据
create table xcp2 as (select * from xcp t where t.createdtime>to_timestamp('2011-12-23 10:49:30','yyyy-MM-dd hh24:mi:ss'));
select * from xcp2;--临时表
alter table xcp enable row movement;--启动的行移动功能
flashback table xcp to timestamp to_timestamp('2011-12-23 10:49:30,'yyyy-MM-dd hh24:mi:ss');
select count(1) from xcp;--22001
insert into xcp select * from xcp2 --将临时表的数据再插入到源表
select count(1) from xcp;--22002
2.删除闪回[10g+]
删除闪回为删除oracle 10G提供了一个数据库安全机制,当用户删除一个表时,oracle 10G会将该表放到回收站中,回收站中的对象一直会保留,直到用户决定永久删除它们或出现表空间的空间不足时才会被删除。回收站是一个虚拟容器,用于存储所有被删除的对象。数据字典user_tables中的列dropped表示被删除的表,查询方法如下:
select table_name,dropped from user_tables;
--设置数据库是否启用回收站
alert session set recyclebin = off;
--查询回收站对象
select * from recyclebin;
select * from user_recyclebin;
select * from dba_recyclebin;
drop table xcp;
select count(1) from xcp;--0
--恢复drop的表
flashback table xcp to before drop;
select count(1) from xcp;--22001
如果不知道原表名,可以直接使用回收站中的名称进行闪回..
flashback table "BIN$JiXyAUO4R+u3qNVfQK/Kiw==$0" to before drop;
在回收的同时可以修改表名
flashback table "BIN$JiXyAUO4R+u3qNVfQK/Kiw==$0" to before drop rename to xcp1;
--真正删除一个表,而不进入回收站,可以在删除表时增加purge选项
drop table xcp1 purge;
--也可以从回收站永久性删除表
purge table xcp1;
--删除当前用户回收站
purge recyclebin
--删除全体用户在回收站的资源
purge dba_resyclebin
3.数据库闪口[10g+]
使用数据库闪回功能,可以使数据库回到过去某一状态,语法如下:
sql: alter database flashback on;
sql: flashback database to scn 46963;
sql: flashback database to timestamp to_timestamp('2007-2-12 12:00:00','yyyy-MM-dd hh24:mi:ss');
oracle 取得表字段注释
select * from user_tab_cols b where b.table_name='B_ZA_CZRKXX' ORDER BY b.internal_column_id;
--USER_TAB_COLUMNS,以避免获取到oracle自行添加的隐藏字段
select * from user_tab_columns b where b.TABLE_NAME='B_ZA_CZRKXX';
select * from user_tab_comments b where b.TABLE_NAME='B_ZA_CZRKXX';
select * from user_col_comments b where b.TABLE_NAME='B_ZA_CZRKXX';
select col.COLUMN_NAME ,com.comments,col.DATA_TYPE
from user_tab_columns col
inner join user_col_comments com
on col.TABLE_NAME='B_ZA_CZRKXX' and
col.TABLE_NAME=com.TABLE_NAME and
col.COLUMN_NAME=com.COLUMN_NAME
order by col.COLUMN_ID
Oracle SQL优化
尽量少用IN操作符,基本上所有的IN操作符都可以用EXISTS代替
用IN写出来的SQL的优点是比较容易写及清晰易懂,但是用IN的SQL性能总是比较低的,从ORACLE执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别:
ORACLE 试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用 IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。
Oracle在执行IN子查询时,首先执行子查询,将查询结果放入临时表再执行主查询。而EXIST则是首先检查主查询,然后运行子查询直到找到第一个匹配项。NOT EXISTS比NOT IN效率稍高。但具体在选择IN或EXIST操作时,要根据主子表数据量大小来具体考虑。
SELECT * FROM EMP WHERE EMPNO > 0 AND EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB')
SELECT * FROM EMP WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB')
推荐方案:在业务密集的SQL当中尽量不采用IN操作符。
不用NOT IN操作符,可以用NOT EXISTS或者外连接+替代
此操作是强列推荐不使用的,因为它不能应用表的索引。
推荐方案:用NOT EXISTS 或(外连接+判断为空)方案代替
不用“<>”或者“!=”操作符。对不等于操作符的处理会造成全表扫描,可以用“<” or “>”代替
不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。
推荐方案:用其它相同功能的操作运算代替,如:
1)a<>0 改为 a>0 or a<0
2)a<>’’ 改为 a>’’
Where子句中出现IS NULL或者IS NOT NULL时,Oracle会停止使用索引而执行全表扫描。可以考虑在设计表时,对索引列设置为NOT NULL。这样就可以用其他操作来取代判断NULL的操作
IS NULL 或IS NOT NULL操作(判断字段是否为空)
判断字段是否为空一般是不会应用索引的,因为B树索引是不索引空值的。
推荐方案:
用其它相同功能的操作运算代替,如:
1)a is not null 改为 a>0 或a>’’等。
2)不允许字段为空,而用一个缺省值代替空值,如业扩申请中状态字段不允许为空,缺省为申请。
3) 建立位图索引(有分区的表不能建,位图索引比较难控制,如字段值太多索引会使性能下降,多人更新操作会增加数据块锁的现象)
当通配符“%”或者“_”作为查询字符串的第一个字符时,索引不会被使用
对于有连接的列“||”,最后一个连接列索引会无效。尽量避免连接,可以分开连接或者使用不作用在列上的函数替代。
如果索引不是基于函数的,那么当在Where子句中对索引列使用函数时,索引不再起作用。
Where子句中避免在索引列上使用计算,否则将导致索引失效而进行全表扫描。
对数据类型不同的列进行比较时,会使索引失效。
> 及 < 操作符(大于或小于操作符)
大于或小于操作符一般情况下是不用调整的,因为它有索引就会采用索引查找,但有的情况下可以对它进行优化,如一个表有100万记录,一个数值型字段A, 30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A>2与A>=3的效果就有很大的区别了,因为 A>2时ORACLE会先找出为2的记录索引再进行比较,而A>=3时ORACLE则直接找到=3的记录索引。
推荐方案:用“>=”替代“>”。
例: A>2 改为 A>=3
A<2 改为 A<=1
UNION操作符
UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如:
select * from gc_dfys
union
select * from ls_jg_dfys
这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。
select * from gc_dfys
union all
select * from ls_jg_dfys
LIKE操作符
LIKE 操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘%5400%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE ‘%5400%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。
SQL书写的影响(共享SQL语句可以提高操作效率)
同一功能同一性能不同写法SQL的影响
如一个SQL在A程序员写的为
Select * from zl_yhjbqk
B程序员写的为
Select * from dlyx.zl_yhjbqk(带表所有者的前缀)
C程序员写的为
Select * from DLYX.ZLYHJBQK(大写表名)
D程序员写的为
Select * from DLYX.ZLYHJBQK(中间多了空格)
以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL 都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。
推荐方案:不同区域出现的相同的Sql语句,要保证查询字符完全相同,以利用SGA共享池,防止相同的Sql语句被多次分析。
WHERE后面的条件顺序影响
Oracle从下到上处理Where子句中多个查询条件,所以表连接语句应写在其他Where条件前,可以过滤掉最大数量记录的条件必须写在Where子句的末尾。
WHERE子句后面的条件顺序对大数据量表的查询会产生直接的影响,如
Select * from zl_yhjbqk where dy_dj = '1KV以下' and xh_bz=1
Select * from zl_yhjbqk where xh_bz=1 and dy_dj = '1KV以下'
以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj = '1KV以下'条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的时候99%条记录都进行dy_dj及xh_bz的比较,而在进行第二条SQL的时候0.5%条记录都进行dy_dj及xh_bz的比较,以此可以得出第二条SQL的CPU占用率明显比第一条低。
查询表顺序的影响
Oracle从右到左处理From子句中的表名,所以在From子句中包含多个表的情况下,将记录最少的表放在最后。(只在采用RBO优化时有效)
在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下ORACLE会按表出现的顺序进行链接,由此因为表的顺序不对会产生十分耗服务器资源的数据交叉。(注:如果对表进行了统计分析, ORACLE会自动先进小表的链接,再进行大表的链接)。
Order By语句中的非索引列会降低性能,可以通过添加索引的方式处理。严格控制在Order By语句中使用表达式
当在Sql语句中连接多个表时,使用表的别名,并将之作为每列的前缀。这样可以减少解析时间
多利用内部函数提高Sql效率
SQL语句索引的利用
对操作符的优化(见前面)
对条件字段的一些优化
采用函数处理的字段不能利用索引
如:
substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’
trunc(sk_rq)=trunc(sysdate), 优化处理:sk_rq>=trunc(sysdate) and sk_rq<trunc(sysdate+1)
进行了显式或隐式的运算的字段不能进行索引
如:
ss_df+20>50,优化处理:ss_df>30
‘X’||hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’
sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5
hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’,注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型。
条件内包括了多个本表的字段运算时不能进行索引
ys_df>cx_df,无法进行优化
qc_bh||kh_bh=’5400250000’,优化处理:qc_bh=’5400’ and kh_bh=’250000’
可能引起全表扫描的操作
在索引列上使用NOT或者“<>”
对索引列使用函数或者计算
NOT IN操作
通配符位于查询字符串的第一个字符
IS NULL或者IS NOT NULL
多列索引,但它的第一个列并没有被Where子句引用
ORACLE在SQL执行分析方面已经比较成熟,如果分析执行的路径不对首先应在数据库结构(主要是索引)、服务器当前性能(共享内存、磁盘文件碎片)、数据库对象(表、索引)统计信息是否正确这几方面分析。
--备注:多表查询时,记录最少的表放到最后
Oracle树查询及相关函数
Oracle树查询的最重要的就是select...start with... connect by ...prior 语法了。依托于该语法,我们可以将一个表形结构的中以树的顺序列出来。在下面列述了Oracle中树型查询的常用查询方式以及经常使用的与树查询相关的Oracle特性函数等,在这里只涉及到一张表中的树查询方式而不涉及多表中的关联等。
以我做过的一个项目中的表为例,表结构如下:
CREATE TABLE FLFL
(
ID NUMBER NOT NULL,
MC NVARCHAR2(20),
FLJB NUMBER,
SJFLID NUMBER
)
FLJB是作为树的级别,在很多查询中可以加快SQL的查询效率。在下面演示的功能基本上不使用这个关键字。
SJFLID存储的是上级ID,如果是顶级父节点,该SJFLID为null(得补充一句,当初的确是这样设计的,不过现在知道,表中最好别有null记录,这会引起全文扫描,建议改成0代替)。
我们从最基本的操作,逐步列出树查询中常见的操作,所以查询出来的节点以家族中的辈份作比方。
1. 查找树中的所有顶级父节点(辈份最长的人)。 假设这个树是个目录结构,那么第一个操作总是找出所有的顶级节点,再根据该节点找到其下属节点。
SELECT * FROM flfl WHERE sjflid IS NULL;
这是个引子,没用到树型查询。
2.查找一个节点的直属子节点(所有儿子)。 如果查找的是直属子类节点,也是不用用到树型查询的。
SELECT * FROM flfl WHERE sjflid = 819459;
这个可以找到ID为819459的直属子类节点。
3.查找一个节点的所有 直属子节点(所有后代)。
SELECT * FROM flfl START WITH ID = 819459 CONNECT BY sjflid = PRIOR ID;
这个查找的是ID为819459的节点下的所有直属子类节点,包括子辈的和孙子辈的所有直属节点。
4.查找一个节点的直属父节点(父亲)。 如果查找的是节点的直属父节点,也是不用用到树型查询的。
SELECT b.* FROM flfl a JOIN flfl b ON a.sjflid = b.ID WHERE a.ID = 6758;
这个找到的是ID为6758的节点的直属父节点,要用到同一张表的关联了。
5.查找一个节点的所有直属父节点(祖宗)。
SELECT * FROM flfl START WITH ID = 6758 CONNECT BY PRIOR sjflid = ID;
这里查找的就是ID为6758的所有直属父节点,打个比方就是找到一个人的父亲、祖父等。但是值得注意的是这个查询出来的结果的顺序是先列出子类节点再列出父类节点,姑且认为是个倒序吧。
上面列出两个树型查询方式,第3条语句和第5条语句,这两条语句之间的区别在于prior关键字的位置不同,所以决定了查询的方式不同。 当sjflid = PRIOR ID时,数据库会根据当前的ID迭代出sjflid与该ID相同的记录,所以查询的结果是迭代出了所有的子类记录;而PRIOR ID = sjflid时,数据库会跟据当前的sjflid来迭代出与当前的sjflid相同的id的记录,所以查询出来的结果就是所有的父类结果。
以下是一系列针对树结构的更深层次的查询,这里的查询不一定是最优的查询方式,或许只是其中的一种实现而已。
6.查询一个节点的兄弟节点(亲兄弟)。
SELECT a.*
FROM flfl a
WHERE EXISTS (SELECT *
FROM flfl b
WHERE a.sjflid = b.sjflid AND b.ID = 6757);
这里查询的就是与ID为6757的节点同属一个父节点的节点了,就好比亲兄弟了。
7.查询与一个节点同级的节点(族兄弟)。 如果在表中设置了级别的字段,上表中的FLJB,那么在做这类查询时会很轻松,同一级别的就是与那个节点同级的,在这里列出不使用该字段时的实现!
WITH tmp AS
(SELECT a.*, LEVEL lev
FROM flfl a
START WITH a.sjflid IS NULL
CONNECT BY a.sjflid = PRIOR a.ID)
SELECT *
FROM tmp
WHERE lev = (SELECT lev
FROM tmp
WHERE ID = 819394)
这里使用两个技巧,一个是使用了LEVEL来标识每个节点在表中的级别,还有就是使用with语法模拟出了一张带有级别的临时表。
8.查询一个节点的父节点的的兄弟节点(伯父与叔父)。
WITH tmp AS
(SELECT flfl.*, LEVEL lev
FROM flfl
START WITH sjflid IS NULL
CONNECT BY sjflid = PRIOR ID)
SELECT b.*
FROM tmp b,
(SELECT *
FROM tmp
WHERE ID = 7004 AND lev = 2) a
WHERE b.lev = 1
UNION ALL
SELECT *
FROM tmp
WHERE sjflid = (SELECT DISTINCT x.ID
FROM tmp x,
tmp y,
(SELECT *
FROM tmp
WHERE ID = 7004 AND lev > 2) z
WHERE y.ID = z.sjflid AND x.ID = y.sjflid);
这里查询分成以下几步。首先,将第7个一样,将全表都使用临时表加上级别;其次,根据级别来判断有几种类型,以上文中举的例子来说,有三种情况:(1)当前节点为顶级节点,即查询出来的lev值为1,那么它没有上级节点,不予考虑。(2)当前节点为2级节点,查询出来的lev值为2,那么就只要保证lev级别为1的就是其上级节点的兄弟节点。(3)其它情况就是3以及以上级别,那么就要选查询出来其上级的上级节点(祖父),再来判断祖父的下级节点都是属于该节点的上级节点的兄弟节点。 最后,就是使用UNION将查询出来的结果进行结合起来,形成结果集。
9.查询一个节点的父节点的同级节点(族叔)。
这个其实跟第7种情况是相同的。
WITH tmp AS
(SELECT a.*, LEVEL lev
FROM flfl a
START WITH a.sjflid IS NULL
CONNECT BY a.sjflid = PRIOR a.ID)
SELECT *
FROM tmp
WHERE lev = (SELECT lev
FROM tmp
WHERE ID = 819394) - 1
只需要做个级别判断就成了。
基本上,常见的查询在里面了,不常见的也有部分了。其中,查询的内容都是节点的基本信息,都是数据表中的基本字段,但是在树查询中还有些特殊需求,是对查询数据进行了处理的,常见的包括列出树路径等。
补充一个概念,对于数据库来说,根节点并不一定是在数据库中设计的顶级节点,对于数据库来说,根节点就是start with开始的地方。
下面列出的是一些与树相关的特殊需求。
10.名称要列出名称全部路径。
这里常见的有两种情况,一种是是从顶级列出,直到当前节点的名称(或者其它属性);一种是从当前节点列出,直到顶级节点的名称(或其它属性)。举地址为例:国内的习惯是从省开始、到市、到县、到居委会的,而国外的习惯正好相反(老师说的,还没接过国外的邮件,谁能寄个瞅瞅 )。
从顶部开始:
SELECT SYS_CONNECT_BY_PATH (mc, '/')
FROM flfl
WHERE ID = 6498
START WITH sjflid IS NULL
CONNECT BY sjflid = PRIOR ID;
从当前节点开始:
SELECT SYS_CONNECT_BY_PATH (mc, '/')
FROM flfl
START WITH ID = 6498
CONNECT BY PRIOR sjflid = ID;
在这里我又不得不放个牢骚了。oracle只提供了一个sys_connect_by_path函数,却忘了字符串的连接的顺序。在上面的例子中,第一个SQL是从根节点开始遍历,而第二个SQL是直接找到当前节点,从效率上来说已经是千差万别,更关键的是第一个SQL只能选择一个节点,而第二个SQL却是遍历出了一颗树来。再次PS一下。
sys_connect_by_path函数就是从start with开始的地方开始遍历,并记下其遍历到的节点,start with开始的地方被视为根节点,将遍历到的路径根据函数中的分隔符,组成一个新的字符串,这个功能还是很强大的。
11.列出当前节点的根节点。
在前面说过,根节点就是start with开始的地方。
SELECT CONNECT_BY_ROOT mc, flfl.*
FROM flfl
START WITH ID = 6498
CONNECT BY PRIOR sjflid = ID;
connect_by_root函数用来列的前面,记录的是当前节点的根节点的内容。
12.列出当前节点是否为叶子。
这个比较常见,尤其在动态目录中,在查出的内容是否还有下级节点时,这个函数是很适用的。
SELECT CONNECT_BY_ISLEAF, flfl.*
FROM flfl
START WITH sjflid IS NULL
CONNECT BY sjflid = PRIOR ID;
connect_by_isleaf函数用来判断当前节点是否包含下级节点,如果包含的话,说明不是叶子节点,这里返回0;反之,如果不包含下级节点,这里返回1。
至此,oracle树型查询基本上讲完了,以上的例子中的数据是使用到做过的项目中的数据,因为里面的内容可能不好理解,所以就全部用一些新的例子来进行阐述。以上所有SQL都在本机上测试通过,也都能实现相应的功能,但是并不能保证是解决这类问题的最优方案(如第8条明显写成存储过程会更好),如果谁有更好的解决方案、或者有关oracle树查询的任何问题,欢迎留言讨论,以上的SQL有什么问题也欢迎大家留言批评。
SQL的四种连接-左外连接、右外连接、内连接、全连接
今天在看一个遗留系统的数据表的时候发现平时查找的视图是FULL OUT JOIN的,导致平时的数据记录要进行一些限制性处理,其实也可以设置视图各表为右外连接并在视图上设置各列的排序和筛选条件就可以达到效果。
联接条件可在FROM或WHERE子句中指定,建议在FROM子句中指定联接条件。WHERE和HAVING子句也可以包含搜索条件,以进一步筛选联接条件所选的行。
联接可分为以下几类:
1、内联接(典型的联接运算,使用像 = 或 <> 之类的比较运算符)。包括相等联接和自然联接。
内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。例如,检索 students和courses表中学生标识号相同的所有行。
2、外联接。外联接可以是左向外联接、右向外联接或完整外部联接。
在 FROM子句中指定外联接时,可以由下列几组关键字中的一组指定:
1)LEFT JOIN或LEFT OUTER JOIN
左向外联接的结果集包括 LEFT OUTER子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
2)RIGHT JOIN 或 RIGHT OUTER JOIN
右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
3)FULL JOIN 或 FULL OUTER JOIN
完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
3、交叉联接
交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
FROM 子句中的表或视图可通过内联接或完整外部联接按任意顺序指定;但是,用左或右向外联接指定表或视图时,表或视图的顺序很重要。有关使用左或右向外联接排列表的更多信息,请参见使用外联接。
例子:
-------------------------------------------------
a表 id name b表 id job parent_id
1 张3 1 23 1
2 李四 2 34 2
3 王武 3 34 4
a.id同parent_id 存在关系
--------------------------------------------------
1) 内连接
select a.*,b.* from a inner join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
2)左连接
select a.*,b.* from a left join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
3 王武 null
3) 右连接
select a.*,b.* from a right join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
null 3 34 4
4) 完全连接
select a.*,b.* from a full join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四 2 34 2
null 3 34 4
3 王武 null
oracle 利用 rownum分页的方法
不排序分页:
select *
from (select rownum as num,t.ID, t.NAME, t.MODEL, t.DEPTH, t.FATHER, t.PCS
from v_za_bzdzk t
where t.FATHER = '510502'
and PCS like '%510502400000%'
and depth = '2'
and rownum<=30) gg
where num >= 16
排序分页:
select *
from (select rownum as num, gg.*
from (select t.ID, t.NAME, t.MODEL, t.DEPTH, t.FATHER, t.PCS
from v_za_bzdzk t
where t.FATHER = '510502'
and PCS like '%510502400000%'
and depth = '2'
order by ORDERNAME) gg
where Rownum <= 30
) tt
where tt.num >= 16
破解移植wm_concat
--
/*****************************************
功能: 破解移植wm_concat
作者:xcp
日期:2011-07-14
说明:wm_concat出现在oracle10g版本中,不仅是加密的,而且是在一个单独的用户中,不方便使用。并且10g以前的版本也用不上。经过破解移植,可以方便大家使用
将下边的一个type,一个函数的创建脚本执行,就可以构建自己的wm_concat(这里叫zh_concat)。使用方法同wm_concat:
******************************************/
--type:
create or replace TYPE za_concat_im
AUTHID CURRENT_USER AS OBJECT
(
CURR_STR VARCHAR2(32767),
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT za_concat_im) RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT za_concat_im,
P1 IN VARCHAR2) RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN za_concat_im,
RETURNVALUE OUT VARCHAR2,
FLAGS IN NUMBER)
RETURN NUMBER,
MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT za_concat_im,
SCTX2 IN za_concat_im) RETURN NUMBER
);
/
create or replace TYPE BODY za_concat_im
IS
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT za_concat_im)
RETURN NUMBER
IS
BEGIN
SCTX := za_concat_im(NULL) ;
RETURN ODCICONST.SUCCESS;
END;
MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT za_concat_im,
P1 IN VARCHAR2)
RETURN NUMBER
IS
BEGIN
IF(CURR_STR IS NOT NULL) THEN
CURR_STR := CURR_STR || ',' || P1;
ELSE
CURR_STR := P1;
END IF;
RETURN ODCICONST.SUCCESS;
END;
MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN za_concat_im,
RETURNVALUE OUT VARCHAR2,
FLAGS IN NUMBER)
RETURN NUMBER
IS
BEGIN
RETURNVALUE := CURR_STR ;
RETURN ODCICONST.SUCCESS;
END;
MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT za_concat_im,
SCTX2 IN za_concat_im)
RETURN NUMBER
IS
BEGIN
IF(SCTX2.CURR_STR IS NOT NULL) THEN
SELF.CURR_STR := SELF.CURR_STR || ',' || SCTX2.CURR_STR ;
END IF;
RETURN ODCICONST.SUCCESS;
END;
END;
/
--函数:
create or replace FUNCTION za_concat(P1 VARCHAR2)
RETURN VARCHAR2 AGGREGATE USING za_concat_im ;
/
oracle复制表的sql语句
1、只复制表结构的sql
create table b as select * from a where 1<>1
2、即复制表结构又复制表中数据的sql
create table b as select * from a
3、复制表的制定字段的sql
create table b as select row_id,name,age from a where 1<>1//前提是row_id,name,age都是a表的列
4、复制表的指定字段及这些指定字段的数据的sql
create table b as select row_id,name,age from a
以上语句虽然能够很容易的根据a表结构复制创建b表,但是a表的索引等却复制不了,需要在b中手动建立。
5、insert into 会将查询结果保存到已经存在的表中
insert into t2(column1, column2, ....) select column1, column2, .... from t1
ibatis的$与#的区别
# 会根据不同类型而生产字符串,如 String 类型 : aa ---> 'aa'
$ 表示原生类型,如 String 类型 : aa ---> aa
每天遇到问题记录
1.#变量名# 会转化为 jdbc 的 ?, 比如 select * from user where name=#name# 会转化为jdbc的 select * from user where name=?,把?参数设置为name的值,而$变量名$就直接把 $name$替换为 name的内容, 也就是由可能 select * from user where name=$name$ 如果name为 "' ' or 1 = 1", 那么这样就有可能导致sql注入,所以ibatis用#比$好,不会造成sql注入。
2.ibatis中的参数传入的值参数比较多,最好用bean方式传入,也就是通过set/get取值的方式给sql map注入参数,不要用hashmap结构传入,每次用hashmap传入会占用比较多的内容。如果参数少,用hashmap也比较方便简单。但是对传入参数的判断,用bean方式比较容易检测发现,配置也能够统一配置。
select * from user u where u.username=#name# //自动加''号
select * from user u where age=$age$ and username=$username$ // username=$username$ 会出错
select * from $username$ u where age =$age$ order by $filed$ //$主要用处是动态表名 ,指定排序字端
oracle学习
一、服务说明
1、OracleDBCConsoleeasy888 就是em等的控制台的开启
2、OracleOraDb10g_home3iSQL*Plus就是浏览器里面的sqlplus调试工具
3、OracleOraDb10g_home3TNSListener就是监听程序
4、OracleServiceeasy888就是Oracle服务
二、本地文件说明(sqlplus "sys/yjtc888 as sysdba")
1. oradata 存放控制文件,数据文件,日志文件
a. 控制文件
1).查看表结构:desc v$controlfile
2).查看记录 select status,name from v$controlfile
b. 数据文件
1).查看表结构:desc v$datafile
2).查看记录 select status,name from v$datafile
c.日志文件
1).查看表结构:desc v$logfile
2).查看记录 select status,name from v$logfile
2.flash_recovery_area 自动保存数据库目录 记录日志文件
3.db_3等文件,在其下面配置listener.ora、sqlnet.ora、tnsnames.ora
三、ORACLE10g服务器端自带工具集
(一)、配置和移植工具
1.Oracle Administration Assistant for window
作用:管理ORACEL本地账户
2.Database Configuration Assistant
作用:数据库创建、配置、删除、集群配置等
每个数据库都会有三个服务:OracleService[XXX]、OracleDBConsole[XXX]、OracleJobScheduler[XXX]
3.Database Upgrade Assistant
作用:将oracle8、9、10等版本数据库升级到10g2版本。
4.Locale Builder
作用:管理地域、语言、字符集等本地化内容。
5.Microsoft ODBC 管理员
作用:不用解释了吧,创建连接字符串的。
6.Net Configuration Assistant
作用:网络服务,包括监听程序配置、命名方法配置、本地NET服务名配置和目录使用配置。
默认监听:Oracle[XXX]10g_home1TNSListener
添加1522端口监听:Oracle[XXX]10g_home1TNSListener[新监听名称]
7.Net Manager
作用:管理服务命名、监听程序等。
(二)、应用程序开发
1.SQL Plus
作用:这个我就不多说了。
(三)、集成管理工具
1.Oracle Directory Manager
作用:ORACLE目录管理
2.Wallet Manager
作用:ORACLE钱包管理,我也没用过。
(四)、Oracle Installation Products
1.Universal Installer
三种SQL分页法
三种SQL分页法
表中主键必须为标识列,[ID] int IDENTITY (1,1)
1.分页方案一:(利用Not In和SELECT TOP分页)
语句形式:
SELECT TOP 页记录数量 *
FROM 表名
WHERE (ID NOT IN
(SELECT TOP (每页行数*(页数-1)) ID
FROM 表名
ORDER BY ID))
ORDER BY ID
//自己还可以加上一些查询条件
例:
select top 2 *
from Sys_Material_Type
where (MT_ID not in
(select top (2*(3-1)) MT_ID from Sys_Material_Type order by MT_ID))
order by MT_ID
2.分页方案二:(利用ID大于多少和SELECT TOP分页)
语句形式:
SELECT TOP 每页记录数量 *
FROM 表名
WHERE (ID >
(SELECT MAX(id)
FROM (SELECT TOP 每页行数*页数 id FROM 表
ORDER BY id) AS T)
)
ORDER BY ID
例:
SELECT TOP 2 *
FROM Sys_Material_Type
WHERE (MT_ID >
(SELECT MAX(MT_ID)
FROM (SELECT TOP (2*(3-1)) MT_ID
FROM Sys_Material_Type
ORDER BY MT_ID) AS T))
ORDER BY MT_ID
3.分页方案三:(利用SQL的游标存储过程分页)
create procedure SqlPager
@sqlstr nvarchar(4000), --查询字符串
@currentpage int, --第N页
@pagesize int --每页行数
as
set nocount on
declare @P1 int, --P1是游标的id
@rowcount int
exec sp_cursoropen @P1 output,@sqlstr,@scrollopt=1,@ccopt=1, @rowcount=@rowcount output
select ceiling(1.0*@rowcount/@pagesize) as 总页数--,@rowcount as 总行数,@currentpage as 当前页
set @currentpage=(@currentpage-1)*@pagesize+1
exec sp_cursorfetch @P1,16,@currentpage,@pagesize
exec sp_cursorclose @P1
set nocount off
4.总结:
其它的方案:如果没有主键,可以用临时表,也可以用方案三做,但是效率会低。
建议优化的时候,加上主键和索引,查询效率会提高。
通过SQL 查询分析器,显示比较:我的结论是:
分页方案二:(利用ID大于多少和SELECT TOP分页)效率最高,需要拼接SQL语句
分页方案一:(利用Not In和SELECT TOP分页) 效率次之,需要拼接SQL语句
分页方案三:(利用SQL的游标存储过程分页) 效率最差,但是最为通用
本文转摘自『IT学习者』http://www.itlearner.com/article/2007/3740.shtml
相关推荐
ORACLEDATABASE11G数据库管理艺术 本书针对大多数企业常见的Oracle数据库管理任务,如安装Oracle Database 11g数据库软件、创建和升级数据库、导入和导出数据、数据库的备份与恢复以及性能调优等,全面深入地介绍了...
ORACLEDATABASE11G数据库管理艺术 本书针对大多数企业常见的Oracle数据库管理任务,如安装Oracle Database 11g数据库软件、创建和升级数据库、导入和导出数据、数据库的备份与恢复以及性能调优等,全面深入地介绍了...
ORACLEDATABASE11G数据库管理艺术 本书针对大多数企业常见的Oracle数据库管理任务,如安装Oracle Database 11g数据库软件、创建和升级数据库、导入和导出数据、数据库的备份与恢复以及性能调优等,全面深入地介绍了...
这份"OracleDataBase培训资料(final).ppt"显然是一份详细的Oracle数据库系统培训教程,旨在帮助学习者掌握Oracle数据库的安装、配置及管理等核心技能。下面我们将深入探讨其中的关键知识点。 首先,我们关注的是...
06_OracleDatabase.pdf
这本书籍,"PDF-OracleDatabase11gFreePdfBook-英文版",将引导读者深入理解Oracle Database 11g的基础知识和高级特性。 1. **Oracle数据库基础** - 数据库概念:了解数据库是什么,以及它在信息管理中的作用。 -...
《OracleLinux6.2下OracleDatabase11.2.0.4 RAC安装与配置详解》 Oracle Database 11g Release 2 (11.2.0.4) RAC(Real Application Clusters)是一种高可用性解决方案,它允许在多台服务器之间共享同一数据库,...
OracleDatabase11gSQL_数据库类.rarOracleDatabase11gSQL_数据库类.rarOracleDatabase11gSQL_数据库类.rarOracleDatabase11gSQL_数据库类.rarOracleDatabase11gSQL_数据库类.rarOracleDatabase11gSQL_数据库类....
OracleDatabase11g完全参考手册01
OracleLinux62上部署OracleDatabase11204RAC.docx
OracleDatabase11g闪回技术概述.pptx
第3章OracleDatabase的体系结构.pptx
CON1399-利用OracleDatabase12c整合数据库.pptx
PDF-OracleDatabase2DaySecurityGuide-英文版.rar
基于Linux平台的OracleDatabase10g管理.
OracleDatabase11g完全参考手册第1部分
5OracleDatabase12c最大化应用程序的可用性.pptx
OracleDatabase12c_V12.2.0_windows_64下载,内附百度网盘下载地址及提取码