亡命天涯

转贴介绍一些11g的新特性,从风云猫哪里搞来的

oracle admin — 作者 littledan @ 19:40

Zoundry 贴blog还没研究好,先贴这里吧

转贴介绍一些11g的新特性。

1. 数据库管理部分

· 数据库重演(Database Replay)

这一特性可以捕捉整个数据的负载,并且传递到一个从备份或者standby数据库中创建的测试数据库上,然后重演负责以测试系统调优后的效果。

· SQL重演(SQL Replay)

和前一特性类似。但是只是捕捉SQL负载部分,而不是全部负载。

· 计划管理(Plan Management

这一特性允许你将某一特定语句的查询计划固定下来,无论统计数据变化还是数据库版本变化都不会改变她的查询计划。

· 自动诊断知识库(Automatic Diagnostic Repository ADR

Oracle探测到重要错误时,会自动创纪一个事件(incident),并且捕捉到和这一事件相关的信息,同时自动进行数据库健康检查并通知DBA。此外,这些信息还可以打包发送给Oracle支持团队。

· 事件打包服务(Incident Packaging Service)

如果你需要进一步测试或者保留相关信息,这一特性可以将与某一事件相关的信息打包。并且你还可以将打包信息发给oracle支持团队。

· 基于特性打补丁(Feature Based Patching

在打补丁包时,这一特性可以使你很容易区分出补丁包中的那些特性是你正在使用而必须打的。企业管理器(EM)使你能订阅一个基于特性的补丁服务,因此企业管理器可以自动扫描那些你正在使用的特性有补丁可以打。

· 自动SQL优化(Auto SQL Tuning)

10g的自动优化建议器可以将优化建议写在SQL profile中。而在11g中,你可以让oracle自动将能3倍于原有性能的profile应用到SQL语句上。性能比较由维护窗口中一个新管理任务来完成。

· 访问建议器(Access Advisor

11g的访问建议器可以给出分区建议,包括对新的间隔分区(interval partitioning)的建议。间隔分区相当于范围分区(range partitioning)的自动化版本,她可以在必要时自动创建一个相同大小的分区。范围分区和间隔分区可以同时存在于一张表中,并且范围分区可以转换为间隔分区。

· 自动内存优化(Auto Memory Tuning

9i中,引入了自动PGA优化;10g中,又引入了自动SGA优化。到了11g,所有内存可以通过只设定一个参数来实现全表自动优化。你只要告诉oracle有多少内存可用,她就可以自动指定多少内存分配给PGA、多少内存分配给SGA和多少内存分配给操作系统进程。当然也可以设定最大、最小阈值。

· 资源管理器(Resource Manager

11g的资源管理器不仅可以管理CPU,还可以管理IO。你可以设置特定文件的优先级、文件类型和ASM磁盘组。

· ADDM

ADDM10g被引入。11g中,ADDM不仅可以给单个实例建议,还可以对整个RAC(即数据库级别)给出建议。另外,还可以将一些指示(directive)加入ADDM,使之忽略一些你不关心的信息。

· AWR 基线(AWR Baselines

AWR基线得到了扩展。可以为一些其他使用到的特性自动创建基线。默认会创建周基线。

2. PLSQL部分

· 结果集缓存(Result Set Caching

这一特性能大大提高很多程序的性能。在一些MIS系统或者OLAP系统中,需要使用到很多"select count(*)"这样的查询。在之前,我们如果要提高这样的查询的性能,可能需要使用物化视图或者查询重写的技术。在11g,我们就只需要加一个/*+result_cache*/的提示就可以将结果集缓存住,这样就能大大提高查询性能。当然,在这种情况下,我们可能还要关心另外一个问题:完整性。因为在oracle中是通过一致性读来保证数据的完整性的。而显然,在这种新特性下,为提高性能,是从缓存中的结果集中读取数据,而不会从回滚段中读取数据的。关于这个问题,答案是完全能保证完整性。因为结果集是被独立缓存的,在查询期间,任何其他DML语句都不会影响结果集中的内容,因而可以保证数据的完整性。

· 对象依赖性改进

11g之前,如果有函数或者视图依赖于某张表,一旦这张表发生结构变化,无论是否涉及到函数或视图所依赖的属性,都会使函数或视图变为invalid。在11g中,对这种情况进行了调整:如果表改变的属性与相关的函数或视图无关,则相关对象状态不会发生变化。

· 正则表达式的改进

10g中,引入了正则表达式。这一特性大大方便了开发人员。11goracle再次对这一特性进行了改进。其中,增加了一个名为regexp_count的函数。另外,其他的正则表达式函数也得到了改进。

· SQL语法 =>

我们在调用某一函数时,可以通过=>来为特定的函数参数指定数据。而在11g中,这一语法也同样可以出现在sql语句中了。例如,你可以写这样的语句:

select f(x=>6) from dual;

· TCP包(utl_tcputl_smtp…)支持FGAC(Fine Grained Access Control)安全控制

· 增加了只读表(read-only table

在以前,我们是通过触发器或者约束来实现对表的只读控制。11g中不需要这么麻烦了,可以直接指定表为只读表。

· 触发器执行效率提高了

· 内部单元内联(Intra-Unit inlining

C语言中,你可以通过内联函数(inline)或者宏实现使某些小的、被频繁调用的函数内联,编译后,调用内联函数的部分会编译成内联函数的函数体,因而提高函数效率。在11gplsql中,也同样可以实现这样的内联函数了。

· 设置触发器顺序

可能在一张表上存在多个触发器。在11g中,你可以指定它们的触发顺序,而不必担心顺序混乱导致数据混乱。

· 混合触发器(compound trigger

这是11g中新出现的一种触发器。她可以让你在同一触发器中同时具有申明部分、before过程部分、after each row过程部分和after过程部分。

· 创建无效触发器(Disabled Trigger)

11g中,开发人员可以可以闲创建一个invalid触发器,需要时再编译她。

· 在非DML语句中使用序列(sequence

在之前版本,如果要将sequence的值赋给变量,需要通过类似以下语句实现:

select seq_x.next_val into v_x from dual;

11g中,不需要这么麻烦了,下面语句就可以实现:

v_x := seq_x.next_val;

· PLSQL_Warning

11g中。可以通过设置PLSQL_Warning=enable all,如果在"when others"没有错误爆出就发警告信息。

· PLSQL的可继承性

可以在oracle对象类型中通过super(和java中类似)关键字来实现继承性。

· 编译速度提高

因为不在使用外部C编译器了,因此编译速度提高了。

· 改进了DBMS_SQL

其中的改进之一就是DBMS_SQL可以接收大于32kCLOB了。另外还能支持用户自定义类型和bulk操作。

· 增加了continue关键字

PLSQL的循环语句中可以使用continue关键字了(功能和其他高级语言中的continue关键字相同)。

· 新的PLSQL数据类型——simple_integer

这是一个比pls_integer效率更高的整数数据类型。

3. 其他部分

· 增强的压缩技术

可以最多压缩2/3的空间。

· 高速推进技术

可以大大提高对文件系统的数据读取速度。

· 增强了DATA Guard

可以创建standby数据库的快照,用于测试。结合数据库重演技术,可以实现模拟生成系统负载的压力测试

· 在线应用升级

也就是热补丁——安装升级或打补丁不需要重启数据库

· 数据库修复建议器

可以在错误诊断和解决方案实施过程中指导DBA

· 逻辑对象分区

可以对逻辑对象进行分区,并且可以自动创建分区以方便管理超大数据库(Very Large Databases VLDBs

· 新的高性能的LOB基础结构

· 新的PHP驱动

 查看全文

关于SELECT * FROM v$sga;结果的解析

oracle admin — 作者 littledan @ 15:47

关于SELECT * FROM v$sga;结果的解析
NAME VALUE
-------------------- ---------
Fixed Size 39816
Variable Size 151544820
Database Buffers 838860800
Redo Buffers 41943040

Fixed Size的大小由oracle控制,可以把这部分认为是SGA的“bootstrap”,
用于获取SGA其他部分的bits和pieces的信息.
compute sum of bytes on pool
break on pool skip 1
select pool, name, bytes
from v$sgastat
order by pool, name;
Variable Size的大小大致等于ava_pool_size + large_pool_size + shared_pool_size
要注意每个控制文件会占用256个字节的variable size,如果数据库有4个控制文件,那么
SGA中有1024个bytes分配给这些控制文件.



DBWR & LGWR & CKPT

oracle admin — 作者 littledan @ 11:53
Database Writer (DBWn) The server process records changes to undo and data blocks in the Database Buffer Cache. DBWn writes the dirty buffers from the Database Buffer Cache to the data files. It ensures that a sufficient number of free buffers (buffers that can be overwritten when server processes need to read in blocks from the data files) are available in the Database Buffer Cache. Database performance is improved because server processes make changes only in the Database Buffer Cache. DBWn defers writing to the data files until one of the following events occurs: Incremental or normal checkpoint The number of dirty buffers reaches a threshold value A process scans a specified number of blocks when scanning for free buffers and cannot find any Timeout occurs A ping request in Real Application Clusters (RAC) environment Placing a normal or temporary tablespace offline Placing a tablespace in read-only mode Dropping or truncating a table ALTER TABLESPACE tablespace name BEGIN BACKUP Log Writer (LGWR) LGWR performs sequential writes from the Redo Log Buffer to the online redo log file under the following situations: When a transaction commits When the Redo Log Buffer is one-third full When there is more than 1 MB of changes recorded in the Redo Log Buffer Before DBWn writes modified blocks in the Database Buffer Cache to the data files Every three seconds Because the redo is needed for recovery, LGWR confirms the commit operation only after the redo is written to disk. LGWR can also call on DBWn to write to the data files.

online doc, pay more attention about bulk insert , fetch into , forall ...

oracle admin — 作者 littledan @ 11:02
11 Tuning PL/SQL Applications for PerformanceThis chapter shows you how to write efficient PL/SQL code, and speed up existing code. This chapter contains these topics: Initialization Parameters for PL/SQL Compilation How PL/SQL Optimizes Your Programs Guidelines for Avoiding PL/SQL Performance Problems Profiling and Tracing PL/SQL Programs Reducing Loop Overhead for DML Statements and Queries with Bulk SQL Writing Computation-Intensive Programs in PL/SQL Tuning Dynamic SQL with EXECUTE IMMEDIATE and Cursor Variables Tuning PL/SQL Procedure Calls with the NOCOPY Compiler Hint Compiling PL/SQL Code for Native Execution Setting Up Transformations with Pipelined Functions See Also: Information about PL/SQL performance tuning at: http://www.oracle.com/technology/tech/pl_sql/htdocs/new_in_10gr1.htm Additional information related to tuning PL/SQL applications on the Oracle Technology Network (OTN), at: http://www.oracle.com/technology/tech/pl_sql/ Information for a specific topic on OTN, such as "PL/SQL best practices", by entering the appropriate phrase in the search field on the OTN main page at: http://www.oracle.com/technology/ Changes to the PL/SQL compiler for Oracle 10g: "Improved Performance" Initialization Parameters for PL/SQL Compilation There are several Oracle initialization parameters that are used in the compilation of PL/SQL units. These designated PL/SQL compiler parameters include PLSQL_CCFLAGS, PLSQL_CODE_TYPE, PLSQL_DEBUG, PLSQL_NATIVE_LIBRARY_DIR, PLSQL_NATIVE_LIBRARY_SUBDIR_COUNT, PLSQL_OPTIMIZE_LEVEL, PLSQL_WARNINGS, and NLS_LENGTH_SEMANTICS. For information about these initialization parameters, see Oracle Database Reference. The values at the time of compilation of the PLSQL_CCFLAGS, PLSQL_CODE_TYPE, PLSQL_DEBUG, PLSQL_OPTIMIZE_LEVEL, PLSQL_WARNINGS, and NLS_LENGTH_SEMANTICS initialization parameters are stored with the unit's metadata. You can view information about the settings of these parameters with the ALL_PLSQL_OBJECT_SETTINGS view. For information, see Oracle Database Reference. You can specify REUSE SETTINGS with the SQL ALTER statement to preserve the values stored in a unit's metadata. For information of the use of the PL/SQL compiler parameters with the ALTER statement, see the ALTER FUNCTION, ALTER PACKAGE, ALTER PROCEDURE, and ALTER SESSION statements in the Oracle Database SQL Reference. How PL/SQL Optimizes Your Programs In Oracle releases prior to 10g, the PL/SQL compiler translated your code to machine code without applying many changes for performance. Now, PL/SQL uses an optimizing compiler that can rearrange code for better performance. You do not need to do anything to get the benefits of this new optimizer. It is enabled by default. In rare cases, if the overhead of the optimizer makes compilation of very large applications take too long, you might lower the optimization by setting the initialization parameter PLSQL_OPTIMIZE_LEVEL=1 instead of its default value 2. In even rarer cases, you might see a change in exception behavior, either an exception that is not raised at all, or one that is raised earlier than expected. Setting PLSQL_OPTIMIZE_LEVEL=0 prevents the code from being rearranged at all. For information on the PLSQL_OPTIMIZE_LEVEL initialization parameter, see Oracle Database Reference. You can view information about the optimization level and other PLSQL compiler settings for your environment with the ALL_PLSQL_OBJECT_SETTINGS view. For information, see Oracle Database Reference. When to Tune PL/SQL Code The information in this chapter is especially valuable if you are responsible for: Programs that do a lot of mathematical calculations. You will want to investigate the datatypes PLS_INTEGER, BINARY_FLOAT, and BINARY_DOUBLE. Functions that are called from PL/SQL queries, where the functions might be executed millions of times. You will want to look at all performance features to make the function as efficient as possible, and perhaps a function-based index to precompute the results for each row and save on query time. Programs that spend a lot of time processing INSERT, UPDATE, or DELETE statements, or looping through query results. You will want to investigate the FORALL statement for issuing DML, and the BULK COLLECT INTO and RETURNING BULK COLLECT INTO clauses for queries. Older code that does not take advantage of recent PL/SQL language features. With the many performance improvements in Oracle Database 10g, any code from earlier releases is a candidate for tuning. Any program that spends a lot of time doing PL/SQL processing, as opposed to issuing DDL statements like CREATE TABLE that are just passed directly to SQL. You will want to investigate native compilation. Because many built-in database features use PL/SQL, you can apply this tuning feature to an entire database to improve performance in many areas, not just your own code. Before starting any tuning effort, benchmark the current system and measure how long particular subprograms take. PL/SQL in Oracle Database 10g includes many automatic optimizations, so you might see performance improvements without doing any tuning. Guidelines for Avoiding PL/SQL Performance Problems When a PL/SQL-based application performs poorly, it is often due to badly written SQL statements, poor programming practices, inattention to PL/SQL basics, or misuse of shared memory. Avoiding CPU Overhead in PL/SQL Code This sections discusses how you can avoid excessive CPU overhead in the PL/SQL code. Make SQL Statements as Efficient as Possible PL/SQL programs look relatively simple because most of the work is done by SQL statements. Slow SQL statements are the main reason for slow execution. If SQL statements are slowing down your program: Make sure you have appropriate indexes. There are different kinds of indexes for different situations. Your index strategy might be different depending on the sizes of various tables in a query, the distribution of data in each query, and the columns used in the WHERE clauses. Make sure you have up-to-date statistics on all the tables, using the subprograms in the DBMS_STATS package. Analyze the execution plans and performance of the SQL statements, using: EXPLAIN PLAN statement SQL Trace facility with TKPROF utility Rewrite the SQL statements if necessary. For example, query hints can avoid problems such as unnecessary full-table scans. For more information about these methods, see Oracle Database Performance Tuning Guide. Some PL/SQL features also help improve the performance of SQL statements: If you are running SQL statements inside a PL/SQL loop, look at the FORALL statement as a way to replace loops of INSERT, UPDATE, and DELETE statements. If you are looping through the result set of a query, look at the BULK COLLECT clause of the SELECT INTO statement as a way to bring the entire result set into memory in a single operation. Make Function Calls as Efficient as Possible Badly written subprograms (for example, a slow sort or search function) can harm performance. Avoid unnecessary calls to subprograms, and optimize their code: If a function is called within a SQL query, you can cache the function value for each row by creating a function-based index on the table in the query. The CREATE INDEX statement might take a while, but queries can be much faster. If a column is passed to a function within an SQL query, the query cannot use regular indexes on that column, and the function might be called for every row in a (potentially very large) table. Consider nesting the query so that the inner query filters the results to a small number of rows, and the outer query calls the function only a few times as shown in Example 11-1. Example 11-1 Nesting a Query to Improve Performance BEGIN -- Inefficient, calls function for every row FOR item IN (SELECT DISTINCT(SQRT(department_id)) col_alias FROM employees) LOOP DBMS_OUTPUT.PUT_LINE(item.col_alias); END LOOP; -- Efficient, only calls function once for each distinct value. FOR item IN ( SELECT SQRT(department_id) col_alias FROM ( SELECT DISTINCT department_id FROM employees) ) LOOP DBMS_OUTPUT.PUT_LINE(item.col_alias); END LOOP; END; / If you use OUT or IN OUT parameters, PL/SQL adds some performance overhead to ensure correct behavior in case of exceptions (assigning a value to the OUT parameter, then exiting the subprogram because of an unhandled exception, so that the OUT parameter keeps its original value). If your program does not depend on OUT parameters keeping their values in such situations, you can add the NOCOPY keyword to the parameter declarations, so the parameters are declared OUT NOCOPY or IN OUT NOCOPY. This technique can give significant speedup if you are passing back large amounts of data in OUT parameters, such as collections, big VARCHAR2 values, or LOBs. This technique also applies to member methods of object types. If these methods modify attributes of the object type, all the attributes are copied when the method ends. To avoid this overhead, you can explicitly declare the first parameter of the member method as SELF IN OUT NOCOPY, instead of relying on PL/SQL's implicit declaration SELF IN OUT. For information about design considerations for object methods, see Oracle Database Application Developer's Guide - Object-Relational Features. Make Loops as Efficient as Possible Because PL/SQL applications are often built around loops, it is important to optimize the loop itself and the code inside the loop: To issue a series of DML statements, replace loop constructs with FORALL statements. To loop through a result set and store the values, use the BULK COLLECT clause on the query to bring the query results into memory in one operation. If you have to loop through a result set more than once, or issue other queries as you loop through a result set, you can probably enhance the original query to give you exactly the results you want. Some query operators to explore include UNION, INTERSECT, MINUS, and CONNECT BY. You can also nest one query inside another (known as a subselect) to do the filtering and sorting in multiple stages. For example, instead of calling a PL/SQL function in the inner WHERE clause (which might call the function once for each row of the table), you can filter the result set to a small set of rows in the inner query, and call the function in the outer query. Do Not Duplicate Built-in String Functions PL/SQL provides many highly optimized string functions such as REPLACE, TRANSLATE, SUBSTR, INSTR, RPAD, and LTRIM. The built-in functions use low-level code that is more efficient than regular PL/SQL. If you use PL/SQL string functions to search for regular expressions, consider using the built-in regular expression functions, such as REGEXP_SUBSTR. You can search for regular expressions using the SQL operator REGEXP_LIKE. See Example 6-10. You can test or manipulate strings using the built-in functions REGEXP_INSTR, REGEXP_REPLACE, and REGEXP_SUBSTR. Oracle's regular expression features use characters like '.', '*', '^', and '$' that you might be familiar with from UNIX or Perl programming. For multi-language programming, there are also extensions such as '[:lower:]' to match a lowercase letter, instead of '[a-z]' which does not match lowercase accented letters. See Also: Oracle By Example - Using Regular Expressions on the Oracle Technology Network (OTN): http://www.oracle.com/technology/obe/obe10gdb/develop/regexp/regexp.htm Reorder Conditional Tests to Put the Least Expensive First PL/SQL stops evaluating a logical expression as soon as the result can be determined. This functionality is known as short-circuit evaluation. See "Short-Circuit Evaluation". When evaluating multiple conditions separated by AND or OR, put the least expensive ones first. For example, check the values of PL/SQL variables before testing function return values, because PL/SQL might be able to skip calling the functions. Minimize Datatype Conversions At run time, PL/SQL converts between different datatypes automatically. For example, assigning a PLS_INTEGER variable to a NUMBER variable results in a conversion because their internal representations are different. Whenever possible, choose datatypes carefully to minimize implicit conversions. Use literals of the appropriate types, such as character literals in character expressions and decimal numbers in number expressions. Minimizing conversions might mean changing the types of your variables, or even working backward and designing your tables with different datatypes. Or, you might convert data once, such as from an INTEGER column to a PLS_INTEGER variable, and use the PL/SQL type consistently after that. Note that the conversion from an INTEGER to a PLS_INTEGER datatype could actually improve performance because of the use of more efficient hardware arithmetic. See "Use PLS_INTEGER for Integer Arithmetic". Use PLS_INTEGER for Integer Arithmetic When you need to declare a local integer variable, use the datatype PLS_INTEGER, which is the most efficient integer type. PLS_INTEGER values require less storage than INTEGER or NUMBER values, and PLS_INTEGER operations use hardware arithmetic. Note that the BINARY_INTEGER datatype is identical to PLS_INTEGER. The datatype NUMBER and its subtypes are represented in a special internal format, designed for portability and arbitrary scale and precision, not performance. Even the subtype INTEGER is treated as a floating-point number with nothing after the decimal point. Operations on NUMBER or INTEGER variables require calls to library routines. Avoid constrained subtypes such as INTEGER, NATURAL, NATURALN, POSITIVE, POSITIVEN, and SIGNTYPE in performance-critical code. Variables of these types require extra checking at run time, each time they are used in a calculation. Use BINARY_FLOAT and BINARY_DOUBLE for Floating-Point Arithmetic The datatype NUMBER and its subtypes are represented in a special internal format, designed for portability and arbitrary scale and precision, not performance. Operations on NUMBER or INTEGER variables require calls to library routines. The BINARY_FLOAT and BINARY_DOUBLE types can use native hardware arithmetic instructions, and are more efficient for number-crunching applications such as scientific processing. They also require less space in the database. These types do not always represent fractional values precisely, and handle rounding differently than the NUMBER types. These types are less suitable for financial code where accuracy is critical. Avoiding Memory Overhead in PL/SQL Code This sections discusses how you can avoid excessive memory overhead in the PL/SQL code. Be Generous When Declaring Sizes for VARCHAR2 Variables You might need to allocate large VARCHAR2 variables when you are not sure how big an expression result will be. You can actually conserve memory by declaring VARCHAR2 variables with large sizes, such as 32000, rather than estimating just a little on the high side, such as by specifying 256 or 1000. PL/SQL has an optimization that makes it easy to avoid overflow problems and still conserve memory. Specify a size of more than 4000 characters for the VARCHAR2 variable; PL/SQL waits until you assign the variable, then only allocates as much storage as needed. Group Related Subprograms into Packages When you call a packaged subprogram for the first time, the whole package is loaded into the shared memory pool. Subsequent calls to related subprograms in the package require no disk I/O, and your code executes faster. If the package is aged out of memory, it must be reloaded if you reference it again. You can improve performance by sizing the shared memory pool correctly. Make sure it is large enough to hold all frequently used packages but not so large that memory is wasted. Pin Packages in the Shared Memory Pool You can pin frequently accessed packages in the shared memory pool, using the supplied package DBMS_SHARED_POOL. When a package is pinned, it is not aged out by the least recently used (LRU) algorithm that Oracle normally uses. The package remains in memory no matter how full the pool gets or how frequently you access the package. For more information on the DBMS_SHARED_POOL package, see Oracle Database PL/SQL Packages and Types Reference. Improve Your Code to Avoid Compiler Warnings The PL/SQL compiler issues warnings about things that do not make a program incorrect, but might lead to poor performance. If you receive such a warning, and the performance of this code is important, follow the suggestions in the warning and change the code to be more efficient. Profiling and Tracing PL/SQL Programs As you develop larger and larger PL/SQL applications, it becomes more difficult to isolate performance problems. PL/SQL provides a Profiler API to profile run-time behavior and to help you identify performance bottlenecks. PL/SQL also provides a Trace API for tracing the execution of programs on the server. You can use Trace to trace the execution by subprogram or exception. Using The Profiler API: Package DBMS_PROFILER The Profiler API is implemented as PL/SQL package DBMS_PROFILER, which provides services for gathering and saving run-time statistics. The information is stored in database tables, which you can query later. For example, you can learn how much time was spent executing each PL/SQL line and subprogram. To use the Profiler, you start the profiling session, run your application long enough to get adequate code coverage, flush the collected data to the database, then stop the profiling session. The Profiler traces the execution of your program, computing the time spent at each line and in each subprogram. You can use the collected data to improve performance. For instance, you might focus on subprograms that run slowly. For information about the DBMS_PROFILER subprograms, see Oracle Database PL/SQL Packages and Types Reference. After you have collected data with the Profiler, you can do the following: Analyze the Collected Performance Data Determine why more time was spent executing certain code segments or accessing certain data structures. Find the problem areas by querying the performance data. Focus on the subprograms and packages that use up the most execution time, inspecting possible performance bottlenecks such as SQL statements, loops, and recursive functions. Use Trace Data to Improve Performance Use the results of your analysis to rework slow algorithms. For example, due to an exponential growth in data, you might need to replace a linear search with a binary search. Also, look for inefficiencies caused by inappropriate data structures, and, if necessary, replace those data structures. Using The Trace API: Package DBMS_TRACE With large, complex applications, it becomes difficult to keep track of calls between subprograms. By tracing your code with the Trace API, you can see the order in which subprograms execute. The Trace API is implemented as PL/SQL package DBMS_TRACE, which provides services for tracing execution by subprogram or exception. To use Trace, you start the tracing session, run your application, then stop the tracing session. As the program executes, trace data is collected and stored in database tables. For information about the DBMS_TRACE subprograms, see Oracle Database PL/SQL Packages and Types Reference. Controlling the Trace Tracing large applications can produce huge amounts of data that are difficult to manage. Before starting Trace, you can optionally limit the volume of data collected by selecting specific subprograms for trace data collection. In addition, you can choose a tracing level. For example, you can choose to trace all subprograms and exceptions, or you can choose to trace selected subprograms and exceptions. Reducing Loop Overhead for DML Statements and Queries with Bulk SQL PL/SQL sends SQL statements such as DML and queries to the SQL engine for execution, and SQL returns the result data to PL/SQL. You can minimize the performance overhead of this communication between PL/SQL and SQL by using the PL/SQL language features known collectively as bulk SQL. The FORALL statement sends INSERT, UPDATE, or DELETE statements in batches, rather than one at a time. The BULK COLLECT clause brings back batches of results from SQL. If the DML statement affects four or more database rows, the use of bulk SQL can improve performance considerably. The assigning of values to PL/SQL variables in SQL statements is called binding. PL/SQL binding operations fall into three categories: in-bind: When a PL/SQL variable or host variable is stored in the database by an INSERT or UPDATE statement. out-bind: When a database value is assigned to a PL/SQL variable or a host variable by the RETURNING clause of an INSERT, UPDATE, or DELETE statement. define: When a database value is assigned to a PL/SQL variable or a host variable by a SELECT or FETCH statement. Bulk SQL uses PL/SQL collections, such as varrays or nested tables, to pass large amounts of data back and forth in a single operation. This process is known as bulk binding. If the collection has 20 elements, bulk binding lets you perform the equivalent of 20 SELECT, INSERT, UPDATE, or DELETE statements using a single operation. Queries can pass back any number of results, without requiring a FETCH statement for each row. To speed up INSERT, UPDATE, and DELETE statements, enclose the SQL statement within a PL/SQL FORALL statement instead of a loop construct. To speed up SELECT statements, include the BULK COLLECT INTO clause in the SELECT statement instead of using INTO. For full details of the syntax and restrictions for these statements, see "FORALL Statement" and "SELECT INTO Statement". Using the FORALL Statement The keyword FORALL lets you run multiple DML statements very efficiently. It can only repeat a single DML statement, unlike a general-purpose FOR loop. For full syntax and restrictions, see "FORALL Statement". The SQL statement can reference more than one collection, but FORALL only improves performance where the index value is used as a subscript. Usually, the bounds specify a range of consecutive index numbers. If the index numbers are not consecutive, such as after you delete collection elements, you can use the INDICES OF or VALUES OF clause to iterate over just those index values that really exist. The INDICES OF clause iterates over all of the index values in the specified collection, or only those between a lower and upper bound. The VALUES OF clause refers to a collection that is indexed by BINARY_INTEGER or PLS_INTEGER and whose elements are of type BINARY_INTEGER or PLS_INTEGER. The FORALL statement iterates over the index values specified by the elements of this collection. The FORALL statement in Example 11-2 sends all three DELETE statements to the SQL engine at once. Example 11-2 Issuing DELETE Statements in a Loop CREATE TABLE employees_temp AS SELECT * FROM employees; DECLARE TYPE NumList IS VARRAY(20) OF NUMBER; depts NumList := NumList(10, 30, 70); -- department numbers BEGIN FORALL i IN depts.FIRST..depts.LAST DELETE FROM employees_temp WHERE department_id = depts(i); COMMIT; END; / Example 11-3 loads some data into PL/SQL collections. Then it inserts the collection elements into a database table twice: first using a FOR loop, then using a FORALL statement. The FORALL version is much faster. Example 11-3 Issuing INSERT Statements in a Loop CREATE TABLE parts1 (pnum INTEGER, pname VARCHAR2(15)); CREATE TABLE parts2 (pnum INTEGER, pname VARCHAR2(15)); DECLARE TYPE NumTab IS TABLE OF parts1.pnum%TYPE INDEX BY PLS_INTEGER; TYPE NameTab IS TABLE OF parts1.pname%TYPE INDEX BY PLS_INTEGER; pnums NumTab; pnames NameTab; iterations CONSTANT PLS_INTEGER := 500; t1 INTEGER; t2 INTEGER; t3 INTEGER; BEGIN FOR j IN 1..iterations LOOP -- load index-by tables pnums(j) := j; pnames(j) := 'Part No. ' || TO_CHAR(j); END LOOP; t1 := DBMS_UTILITY.get_time; FOR i IN 1..iterations LOOP -- use FOR loop INSERT INTO parts1 VALUES (pnums(i), pnames(i)); END LOOP; t2 := DBMS_UTILITY.get_time; FORALL i IN 1..iterations -- use FORALL statement INSERT INTO parts2 VALUES (pnums(i), pnames(i)); t3 := DBMS_UTILITY.get_time; DBMS_OUTPUT.PUT_LINE('Execution Time (secs)'); DBMS_OUTPUT.PUT_LINE('---------------------'); DBMS_OUTPUT.PUT_LINE('FOR loop: ' || TO_CHAR((t2 - t1)/100)); DBMS_OUTPUT.PUT_LINE('FORALL: ' || TO_CHAR((t3 - t2)/100)); COMMIT; END; / Executing this block should show that the loop using FORALL is much faster. The bounds of the FORALL loop can apply to part of a collection, not necessarily all the elements, as shown in Example 11-4. Example 11-4 Using FORALL with Part of a Collection CREATE TABLE employees_temp AS SELECT * FROM employees; DECLARE TYPE NumList IS VARRAY(10) OF NUMBER; depts NumList := NumList(5,10,20,30,50,55,57,60,70,75); BEGIN FORALL j IN 4..7 -- use only part of varray DELETE FROM employees_temp WHERE department_id = depts(j); COMMIT; END; / You might need to delete some elements from a collection before using the collection in a FORALL statement. The INDICES OF clause processes sparse collections by iterating through only the remaining elements. You might also want to leave the original collection alone, but process only some elements, process the elements in a different order, or process some elements more than once. Instead of copying the entire elements into new collections, which might use up substantial amounts of memory, the VALUES OF clause lets you set up simple collections whose elements serve as pointers to elements in the original collection. Example 11-5 creates a collection holding some arbitrary data, a set of table names. Deleting some of the elements makes it a sparse collection that would not work in a default FORALL statement. The program uses a FORALL statement with the INDICES OF clause to insert the data into a table. It then sets up two more collections, pointing to certain elements from the original collection. The program stores each set of names in a different database table using FORALL statements with the VALUES OF clause. Example 11-5 Using FORALL with Non-Consecutive Index Values -- Create empty tables to hold order details CREATE TABLE valid_orders (cust_name VARCHAR2(32), amount NUMBER(10,2)); CREATE TABLE big_orders AS SELECT * FROM valid_orders WHERE 1 = 0; CREATE TABLE rejected_orders AS SELECT * FROM valid_orders WHERE 1 = 0; DECLARE -- Make collections to hold a set of customer names and order amounts. SUBTYPE cust_name IS valid_orders.cust_name%TYPE; TYPE cust_typ IS TABLe OF cust_name; cust_tab cust_typ; SUBTYPE order_amount IS valid_orders.amount%TYPE; TYPE amount_typ IS TABLE OF NUMBER; amount_tab amount_typ; -- Make other collections to point into the CUST_TAB collection. TYPE index_pointer_t IS TABLE OF PLS_INTEGER; big_order_tab index_pointer_t := index_pointer_t(); rejected_order_tab index_pointer_t := index_pointer_t(); PROCEDURE setup_data IS BEGIN -- Set up sample order data, including some invalid orders and some 'big' orders. cust_tab := cust_typ('Company1','Company2','Company3','Company4','Company5'); amount_tab := amount_typ(5000.01, 0, 150.25, 4000.00, NULL); END; BEGIN setup_data(); DBMS_OUTPUT.PUT_LINE('--- Original order data ---'); FOR i IN 1..cust_tab.LAST LOOP DBMS_OUTPUT.PUT_LINE('Customer #' || i || ', ' || cust_tab(i) || ': $' || amount_tab(i)); END LOOP; -- Delete invalid orders (where amount is null or 0). FOR i IN 1..cust_tab.LAST LOOP IF amount_tab(i) is null or amount_tab(i) = 0 THEN cust_tab.delete(i); amount_tab.delete(i); END IF; END LOOP; DBMS_OUTPUT.PUT_LINE('--- Data with invalid orders deleted ---'); FOR i IN 1..cust_tab.LAST LOOP IF cust_tab.EXISTS(i) THEN DBMS_OUTPUT.PUT_LINE('Customer #' || i || ', ' || cust_tab(i) || ': $' || amount_tab(i)); END IF; END LOOP; -- Because the subscripts of the collections are not consecutive, use -- FORALL...INDICES OF to iterate through the actual subscripts, -- rather than 1..COUNT FORALL i IN INDICES OF cust_tab INSERT INTO valid_orders(cust_name, amount) VALUES(cust_tab(i), amount_tab(i)); -- Now process the order data differently -- Extract 2 subsets and store each subset in a different table setup_data(); -- Initialize the CUST_TAB and AMOUNT_TAB collections again. FOR i IN cust_tab.FIRST .. cust_tab.LAST LOOP IF amount_tab(i) IS NULL OR amount_tab(i) = 0 THEN rejected_order_tab.EXTEND; -- Add a new element to this collection -- Record the subscript from the original collection rejected_order_tab(rejected_order_tab.LAST) := i; END IF; IF amount_tab(i) > 2000 THEN big_order_tab.EXTEND; -- Add a new element to this collection -- Record the subscript from the original collection big_order_tab(big_order_tab.LAST) := i; END IF; END LOOP; -- Now it's easy to run one DML statement on one subset of elements, -- and another DML statement on a different subset. FORALL i IN VALUES OF rejected_order_tab INSERT INTO rejected_orders VALUES (cust_tab(i), amount_tab(i)); FORALL i IN VALUES OF big_order_tab INSERT INTO big_orders VALUES (cust_tab(i), amount_tab(i)); COMMIT; END; / -- Verify that the correct order details were stored SELECT cust_name "Customer", amount "Valid order amount" FROM valid_orders; SELECT cust_name "Customer", amount "Big order amount" FROM big_orders; SELECT cust_name "Customer", amount "Rejected order amount" FROM rejected_orders; How FORALL Affects Rollbacks In a FORALL statement, if any execution of the SQL statement raises an unhandled exception, all database changes made during previous executions are rolled back. However, if a raised exception is caught and handled, changes are rolled back to an implicit savepoint marked before each execution of the SQL statement. Changes made during previous executions are not rolled back. For example, suppose you create a database table that stores department numbers and job titles, as shown in Example 11-6. Then, you change the job titles so that they are longer. The second UPDATE fails because the new value is too long for the column. Because we handle the exception, the first UPDATE is not rolled back and we can commit that change. Example 11-6 Using Rollbacks With FORALL CREATE TABLE emp_temp (deptno NUMBER(2), job VARCHAR2(18)); DECLARE TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(10, 20, 30); BEGIN INSERT INTO emp_temp VALUES(10, 'Clerk'); -- Lengthening this job title causes an exception INSERT INTO emp_temp VALUES(20, 'Bookkeeper'); INSERT INTO emp_temp VALUES(30, 'Analyst'); COMMIT; FORALL j IN depts.FIRST..depts.LAST -- Run 3 UPDATE statements. UPDATE emp_temp SET job = job || ' (Senior)' WHERE deptno = depts(j); -- raises a "value too large" exception EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Problem in the FORALL statement.'); COMMIT; -- Commit results of successful updates. END; / Counting Rows Affected by FORALL with the %BULK_ROWCOUNT Attribute The cursor attributes SQL%FOUND, SQL%ISOPEN, SQL%NOTFOUND, and SQL%ROWCOUNT, return useful information about the most recently executed DML statement. For additional description of cursor attributes, see "Implicit Cursors". The SQL cursor has one composite attribute, %BULK_ROWCOUNT, for use with the FORALL statement. This attribute works like an associative array: SQL%BULK_ROWCOUNT(i) stores the number of rows processed by the ith execution of an INSERT, UPDATE or DELETE statement. For example: Example 11-7 Using %BULK_ROWCOUNT With the FORALL Statement CREATE TABLE emp_temp AS SELECT * FROM employees; DECLARE TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(30, 50, 60); BEGIN FORALL j IN depts.FIRST..depts.LAST DELETE FROM emp_temp WHERE department_id = depts(j); -- How many rows were affected by each DELETE statement? FOR i IN depts.FIRST..depts.LAST LOOP DBMS_OUTPUT.PUT_LINE('Iteration #' || i || ' deleted ' || SQL%BULK_ROWCOUNT(i) || ' rows.'); END LOOP; END; / The FORALL statement and %BULK_ROWCOUNT attribute use the same subscripts. For example, if FORALL uses the range 5..10, so does %BULK_ROWCOUNT. If the FORALL statement uses the INDICES OF clause to process a sparse collection, %BULK_ROWCOUNT has corresponding sparse subscripts. If the FORALL statement uses the VALUES OF clause to process a subset of elements, %BULK_ROWCOUNT has subscripts corresponding to the values of the elements in the index collection. If the index collection contains duplicate elements, so that some DML statements are issued multiple times using the same subscript, then the corresponding elements of %BULK_ROWCOUNT represent the sum of all rows affected by the DML statement using that subscript. For examples on how to interpret %BULK_ROWCOUNT when using the INDICES OF and VALUES OF clauses, see the PL/SQL sample programs at http://www.oracle.com/technology/sample_code/tech/pl_sql/. %BULK_ROWCOUNT is usually equal to 1 for inserts, because a typical insert operation affects only a single row. For the INSERT ... SELECT construct, %BULK_ROWCOUNT might be greater than 1. For example, the FORALL statement in Example 11-8 inserts an arbitrary number of rows for each iteration. After each iteration, %BULK_ROWCOUNT returns the number of items inserted. Example 11-8 Counting Rows Affected by FORALL With %BULK_ROWCOUNT CREATE TABLE emp_by_dept AS SELECT employee_id, department_id FROM employees WHERE 1 = 0; DECLARE TYPE dept_tab IS TABLE OF departments.department_id%TYPE; deptnums dept_tab; BEGIN SELECT department_id BULK COLLECT INTO deptnums FROM departments; FORALL i IN 1..deptnums.COUNT INSERT INTO emp_by_dept SELECT employee_id, department_id FROM employees WHERE department_id = deptnums(i); FOR i IN 1..deptnums.COUNT LOOP -- Count how many rows were inserted for each department; that is, -- how many employees are in each department. DBMS_OUTPUT.PUT_LINE('Dept '||deptnums(i)||': inserted '|| SQL%BULK_ROWCOUNT(i)||' records'); END LOOP; DBMS_OUTPUT.PUT_LINE('Total records inserted: ' || SQL%ROWCOUNT); END; / You can also use the scalar attributes %FOUND, %NOTFOUND, and %ROWCOUNT after running a FORALL statement. For example, %ROWCOUNT returns the total number of rows processed by all executions of the SQL statement. %FOUND and %NOTFOUND refer only to the last execution of the SQL statement. You can use %BULK_ROWCOUNT to infer their values for individual executions. For example, when %BULK_ROWCOUNT(i) is zero, %FOUND and %NOTFOUND are FALSE and TRUE, respectively. Handling FORALL Exceptions with the %BULK_EXCEPTIONS Attribute PL/SQL provides a mechanism to handle exceptions raised during the execution of a FORALL statement. This mechanism enables a bulk-bind operation to save information about exceptions and continue processing. To have a bulk bind complete despite errors, add the keywords SAVE EXCEPTIONS to your FORALL statement after the bounds, before the DML statement. You should also provide an exception handler to track the exceptions that occurred during the bulk operation. Example 11-9 shows how you can perform a number of DML operations, without stopping if some operations encounter errors. In the example, EXCEPTION_INIT is used to associate the dml_errors exception with the ORA-24381 error. The ORA-24381 error is raised if any exceptions are caught and saved after a bulk operation. All exceptions raised during the execution are saved in the cursor attribute %BULK_EXCEPTIONS, which stores a collection of records. Each record has two fields: %BULK_EXCEPTIONS(i).ERROR_INDEX holds the iteration of the FORALL statement during which the exception was raised. %BULK_EXCEPTIONS(i).ERROR_CODE holds the corresponding Oracle error code. The values stored by %BULK_EXCEPTIONS always refer to the most recently executed FORALL statement. The number of exceptions is saved in %BULK_EXCEPTIONS.COUNT. Its subscripts range from 1 to COUNT. The individual error messages, or any substitution arguments, are not saved, but the error message text can looked up using ERROR_CODE with SQLERRM as shown in Example 11-9. You might need to work backward to determine which collection element was used in the iteration that caused an exception. For example, if you use the INDICES OF clause to process a sparse collection, you must step through the elements one by one to find the one corresponding to %BULK_EXCEPTIONS(i).ERROR_INDEX. If you use the VALUES OF clause to process a subset of elements, you must find the element in the index collection whose subscript matches %BULK_EXCEPTIONS(i).ERROR_INDEX, and then use that element's value as the subscript to find the erroneous element in the original collection. For examples showing how to find the erroneous elements when using the INDICES OF and VALUES OF clauses, see the PL/SQL sample programs at http://www.oracle.com/technology/tech/pl_sql/. If you omit the keywords SAVE EXCEPTIONS, execution of the FORALL statement stops when an exception is raised. In that case, SQL%BULK_EXCEPTIONS.COUNT returns 1, and SQL%BULK_EXCEPTIONS contains just one record. If no exception is raised during execution, SQL%BULK_EXCEPTIONS.COUNT returns 0. Example 11-9 Bulk Operation That Continues Despite Exceptions -- create a temporary table for this example CREATE TABLE emp_temp AS SELECT * FROM employees; DECLARE TYPE empid_tab IS TABLE OF employees.employee_id%TYPE; emp_sr empid_tab; -- create an exception handler for ORA-24381 errors NUMBER; dml_errors EXCEPTION; PRAGMA EXCEPTION_INIT(dml_errors, -24381); BEGIN SELECT employee_id BULK COLLECT INTO emp_sr FROM emp_temp WHERE hire_date < '30-DEC-94'; -- add '_SR' to the job_id of the most senior employees FORALL i IN emp_sr.FIRST..emp_sr.LAST SAVE EXCEPTIONS UPDATE emp_temp SET job_id = job_id || '_SR' WHERE emp_sr(i) = emp_temp.employee_id; -- If any errors occurred during the FORALL SAVE EXCEPTIONS, -- a single exception is raised when the statement completes. EXCEPTION WHEN dml_errors THEN -- Now we figure out what failed and why. errors := SQL%BULK_EXCEPTIONS.COUNT; DBMS_OUTPUT.PUT_LINE('Number of statements that failed: ' || errors); FOR i IN 1..errors LOOP DBMS_OUTPUT.PUT_LINE('Error #' || i || ' occurred during '|| 'iteration #' || SQL%BULK_EXCEPTIONS(i).ERROR_INDEX); DBMS_OUTPUT.PUT_LINE('Error message is ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE)); END LOOP; END; / DROP TABLE emp_temp; The output from the example is similar to: Number of statements that failed: 2 Error #1 occurred during iteration #7 Error message is ORA-12899: value too large for column Error #2 occurred during iteration #13 Error message is ORA-12899: value too large for column In Example 11-9, PL/SQL raises predefined exceptions because updated values were too large to insert into the job_id column. After the FORALL statement, SQL%BULK_EXCEPTIONS.COUNT returned 2, and the contents of SQL%BULK_EXCEPTIONS were (7,12899) and (13,12899). To get the Oracle error message (which includes the code), the value of SQL%BULK_EXCEPTIONS(i).ERROR_CODE was negated and then passed to the error-reporting function SQLERRM, which expects a negative number. Retrieving Query Results into Collections with the BULK COLLECT Clause Using the keywords BULK COLLECT with a query is a very efficient way to retrieve the result set. Instead of looping through each row, you store the results in one or more collections, in a single operation. You can use these keywords in the SELECT INTO and FETCH INTO statements, and the RETURNING INTO clause. With the BULK COLLECT clause, all the variables in the INTO list must be collections. The table columns can hold scalar or composite values, including object types. Example 11-10 loads two entire database columns into nested tables: Example 11-10 Retrieving Query Results With BULK COLLECT DECLARE TYPE NumTab IS TABLE OF employees.employee_id%TYPE; TYPE NameTab IS TABLE OF employees.last_name%TYPE; enums NumTab; -- No need to initialize the collections. names NameTab; -- Values will be filled in by the SELECT INTO. PROCEDURE print_results IS BEGIN IF enums.COUNT = 0 THEN DBMS_OUTPUT.PUT_LINE('No results!'); ELSE DBMS_OUTPUT.PUT_LINE('Results:'); FOR i IN enums.FIRST .. enums.LAST LOOP DBMS_OUTPUT.PUT_LINE(' Employee #' || enums(i) || ': ' || names(i)); END LOOP; END IF; END; BEGIN -- Retrieve data for employees with Ids greater than 1000 SELECT employee_id, last_name BULK COLLECT INTO enums, names FROM employees WHERE employee_id > 1000; -- The data has all been brought into memory by BULK COLLECT -- No need to FETCH each row from the result set print_results(); -- Retrieve approximately 20% of all rows SELECT employee_id, last_name BULK COLLECT INTO enums, names FROM employees SAMPLE (20); print_results(); END; / The collections are initialized automatically. Nested tables and associative arrays are extended to hold as many elements as needed. If you use varrays, all the return values must fit in the varray's declared size. Elements are inserted starting at index 1, overwriting any existing elements. Because the processing of the BULK COLLECT INTO clause is similar to a FETCH loop, it does not raise a NO_DATA_FOUND exception if no rows match the query. You must check whether the resulting nested table or varray is null, or if the resulting associative array has no elements, as shown in Example 11-10. To prevent the resulting collections from expanding without limit, you can use the LIMIT clause to or pseudocolumn ROWNUM to limit the number of rows processed. You can also use the SAMPLE clause to retrieve a random sample of rows. Example 11-11 Using the Pseudocolumn ROWNUM to Limit Query Results DECLARE TYPE SalList IS TABLE OF employees.salary%TYPE; sals SalList; BEGIN -- Limit the number of rows to 50 SELECT salary BULK COLLECT INTO sals FROM employees WHERE ROWNUM <= 50; -- Retrieve 10% (approximately) of the rows in the table SELECT salary BULK COLLECT INTO sals FROM employees SAMPLE (10); END; / You can process very large result sets by fetching a specified number of rows at a time from a cursor, as shown in the following sections. Examples of Bulk-Fetching from a Cursor You can fetch from a cursor into one or more collections as shown in Example 11-12. Example 11-12 Bulk-Fetching from a Cursor Into One or More Collections DECLARE TYPE NameList IS TABLE OF employees.last_name%TYPE; TYPE SalList IS TABLE OF employees.salary%TYPE; CURSOR c1 IS SELECT last_name, salary FROM employees WHERE salary > 10000; names NameList; sals SalList; TYPE RecList IS TABLE OF c1%ROWTYPE; recs RecList; v_limit PLS_INTEGER := 10; PROCEDURE print_results IS BEGIN IF names IS NULL OR names.COUNT = 0 THEN -- check if collections are empty DBMS_OUTPUT.PUT_LINE('No results!'); ELSE DBMS_OUTPUT.PUT_LINE('Results: '); FOR i IN names.FIRST .. names.LAST LOOP DBMS_OUTPUT.PUT_LINE(' Employee ' || names(i) || ': $' || sals(i)); END LOOP; END IF; END; BEGIN DBMS_OUTPUT.PUT_LINE('--- Processing all results at once ---'); OPEN c1; FETCH c1 BULK COLLECT INTO names, sals; CLOSE c1; print_results(); DBMS_OUTPUT.PUT_LINE('--- Processing ' || v_limit || ' rows at a time ---'); OPEN c1; LOOP FETCH c1 BULK COLLECT INTO names, sals LIMIT v_limit; EXIT WHEN names.COUNT = 0; print_results(); END LOOP; CLOSE c1; DBMS_OUTPUT.PUT_LINE('--- Fetching records rather than columns ---'); OPEN c1; FETCH c1 BULK COLLECT INTO recs; FOR i IN recs.FIRST .. recs.LAST LOOP -- Now all the columns from the result set come from a single record DBMS_OUTPUT.PUT_LINE(' Employee ' || recs(i).last_name || ': $' || recs(i).salary); END LOOP; END; / Example 11-13 shows how you can fetch from a cursor into a collection of records. Example 11-13 Bulk-Fetching from a Cursor Into a Collection of Records DECLARE TYPE DeptRecTab IS TABLE OF departments%ROWTYPE; dept_recs DeptRecTab; CURSOR c1 IS SELECT department_id, department_name, manager_id, location_id FROM departments WHERE department_id > 70; BEGIN OPEN c1; FETCH c1 BULK COLLECT INTO dept_recs; END; / Limiting the Rows for a Bulk FETCH Operation with the LIMIT Clause The optional LIMIT clause, allowed only in bulk FETCH statements, limits the number of rows fetched from the database. In Example 11-14, with each iteration of the loop, the FETCH statement fetches ten rows (or less) into index-by table empids. The previous values are overwritten. Note the use of empids.COUNT to determine when to exit the loop. Example 11-14 Using LIMIT to Control the Number of Rows In a BULK COLLECT DECLARE TYPE numtab IS TABLE OF NUMBER INDEX BY PLS_INTEGER; CURSOR c1 IS SELECT employee_id FROM employees WHERE department_id = 80; empids numtab; rows PLS_INTEGER := 10; BEGIN OPEN c1; LOOP -- the following statement fetches 10 rows or less in each iteration FETCH c1 BULK COLLECT INTO empids LIMIT rows; EXIT WHEN empids.COUNT = 0; -- EXIT WHEN c1%NOTFOUND; -- incorrect, can omit some data DBMS_OUTPUT.PUT_LINE('------- Results from Each Bulk Fetch --------'); FOR i IN 1..empids.COUNT LOOP DBMS_OUTPUT.PUT_LINE( 'Employee Id: ' || empids(i)); END LOOP; END LOOP; CLOSE c1; END; / Retrieving DML Results into a Collection with the RETURNING INTO Clause You can use the BULK COLLECT clause in the RETURNING INTO clause of an INSERT, UPDATE, or DELETE statement: Example 11-15 Using BULK COLLECT With the RETURNING INTO Clause CREATE TABLE emp_temp AS SELECT * FROM employees; DECLARE TYPE NumList IS TABLE OF employees.employee_id%TYPE; enums NumList; TYPE NameList IS TABLE OF employees.last_name%TYPE; names NameList; BEGIN DELETE FROM emp_temp WHERE department_id = 30 RETURNING employee_id, last_name BULK COLLECT INTO enums, names; DBMS_OUTPUT.PUT_LINE('Deleted ' || SQL%ROWCOUNT || ' rows:'); FOR i IN enums.FIRST .. enums.LAST LOOP DBMS_OUTPUT.PUT_LINE('Employee #' || enums(i) || ': ' || names(i)); END LOOP; END; / Using FORALL and BULK COLLECT Together You can combine the BULK COLLECT clause with a FORALL statement. The output collections are built up as the FORALL statement iterates. In Example 11-16, the employee_id value of each deleted row is stored in the collection e_ids. The collection depts has 3 elements, so the FORALL statement iterates 3 times. If each DELETE issued by the FORALL statement deletes 5 rows, then the collection e_ids, which stores values from the deleted rows, has 15 elements when the statement completes: Example 11-16 Using FORALL With BULK COLLECT CREATE TABLE emp_temp AS SELECT * FROM employees; DECLARE TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(10,20,30); TYPE enum_t IS TABLE OF employees.employee_id%TYPE; TYPE dept_t IS TABLE OF employees.department_id%TYPE; e_ids enum_t; d_ids dept_t; BEGIN FORALL j IN depts.FIRST..depts.LAST DELETE FROM emp_temp WHERE department_id = depts(j) RETURNING employee_id, department_id BULK COLLECT INTO e_ids, d_ids; DBMS_OUTPUT.PUT_LINE('Deleted ' || SQL%ROWCOUNT || ' rows:'); FOR i IN e_ids.FIRST .. e_ids.LAST LOOP DBMS_OUTPUT.PUT_LINE('Employee #' || e_ids(i) || ' from dept #' || d_ids(i)); END LOOP; END; / The column values returned by each execution are added to the values returned previously. If you use a FOR loop instead of the FORALL statement, the set of returned values is overwritten by each DELETE statement. You cannot use the SELECT ... BULK COLLECT statement in a FORALL statement. Using Host Arrays with Bulk Binds Client-side programs can use anonymous PL/SQL blocks to bulk-bind input and output host arrays. This is the most efficient way to pass collections to and from the database server. Host arrays are declared in a host environment such as an OCI or a Pro*C program and must be prefixed with a colon to distinguish them from PL/SQL collections. In the following example, an input host array is used in a DELETE statement. At run time, the anonymous PL/SQL block is sent to the database server for execution. DECLARE ... BEGIN -- assume that values were assigned to the host array -- and host variables in the host environment FORALL i IN :lower..:upper DELETE FROM employees WHERE department_id = :depts(i); COMMIT; END; / Writing Computation-Intensive Programs in PL/SQL The BINARY_FLOAT and BINARY_DOUBLE datatypes make it practical to write PL/SQL programs to do number-crunching, for scientific applications involving floating-point calculations. These datatypes behave much like the native floating-point types on many hardware systems, with semantics derived from the IEEE-754 floating-point standard. The way these datatypes represent decimal data make them less suitable for financial applications, where precise representation of fractional amounts is more important than pure performance. The PLS_INTEGER and BINARY_INTEGER datatypes are PL/SQL-only datatypes that are more efficient than the SQL datatypes NUMBER or INTEGER for integer arithmetic. You can use PLS_INTEGER or BINARY_INTEGER to write pure PL/SQL code for integer arithmetic, or convert NUMBER or INTEGER values to PLS_INTEGER or BINARY_INTEGER for manipulation by PL/SQL. Note that the BINARY_INTEGER datatype is identical to PLS_INTEGER. For additional considerations, see "Change to the BINARY_INTEGER Datatype". Within a package, you can write overloaded versions of procedures and functions that accept different numeric parameters. The math routines can be optimized for each kind of parameter (BINARY_FLOAT, BINARY_DOUBLE, NUMBER, PLS_INTEGER), avoiding unnecessary conversions. The built-in math functions such as SQRT, SIN, COS, and so on already have fast overloaded versions that accept BINARY_FLOAT and BINARY_DOUBLE parameters. You can speed up math-intensive code by passing variables of these types to such functions, and by calling the TO_BINARY_FLOAT or TO_BINARY_DOUBLE functions when passing expressions to such functions. Tuning Dynamic SQL with EXECUTE IMMEDIATE and Cursor Variables Some programs (a general-purpose report writer for example) must build and process a variety of SQL statements, where the exact text of the statement is unknown until run time. Such statements probably change from execution to execution. They are called dynamic SQL statements. Formerly, to execute dynamic SQL statements, you had to use the supplied package DBMS_SQL. Now, within PL/SQL, you can execute any kind of dynamic SQL statement using an interface called native dynamic SQL. The main PL/SQL features involved are the EXECUTE IMMEDIATE statement and cursor variables (also known as REF CURSORs). Native dynamic SQL code is more compact and much faster than calling the DBMS_SQL package. The following example declares a cursor variable, then associates it with a dynamic SELECT statement: DECLARE TYPE EmpCurTyp IS REF CURSOR; emp_cv EmpCurTyp; v_ename VARCHAR2(15); v_sal NUMBER := 1000; table_name VARCHAR2(30) := 'employees'; BEGIN OPEN emp_cv FOR 'SELECT last_name, salary FROM ' || table_name || ' WHERE salary > :s' USING v_sal; CLOSE emp_cv; END; / For more information, see Chapter 7, "Performing SQL Operations with Native Dynamic SQL". Tuning PL/SQL Procedure Calls with the NOCOPY Compiler Hint By default, OUT and IN OUT parameters are passed by value. The values of any IN OUT parameters are copied before the subprogram is executed. During subprogram execution, temporary variables hold the output parameter values. If the subprogram exits normally, these values are copied to the actual parameters. If the subprogram exits with an unhandled exception, the original parameters are unchanged. When the parameters represent large data structures such as collections, records, and instances of object types, this copying slows down execution and uses up memory. In particular, this overhead applies to each call to an object method: temporary copies are made of all the attributes, so that any changes made by the method are only applied if the method exits normally. To avoid this overhead, you can specify the NOCOPY hint, which allows the PL/SQL compiler to pass OUT and IN OUT parameters by reference. If the subprogram exits normally, the behavior is the same as normal. If the subprogram exits early with an exception, the values of OUT and IN OUT parameters (or object attributes) might still change. To use this technique, ensure that the subprogram handles all exceptions. The following example asks the compiler to pass IN OUT parameter v_staff by reference, to avoid copying the varray on entry to and exit from the subprogram: DECLARE TYPE Staff IS VARRAY(200) OF Employee; PROCEDURE reorganize (v_staff IN OUT NOCOPY Staff) IS ... Example 11-17 loads 25,000 records into a local nested table, which is passed to two local procedures that do nothing. A call to the procedure that uses NOCOPY takes much less time. Example 11-17 Using NOCOPY With Parameters DECLARE TYPE EmpTabTyp IS TABLE OF employees%ROWTYPE; emp_tab EmpTabTyp := EmpTabTyp(NULL); -- initialize t1 NUMBER; t2 NUMBER; t3 NUMBER; PROCEDURE get_time (t OUT NUMBER) IS BEGIN t := DBMS_UTILITY.get_time; END; PROCEDURE do_nothing1 (tab IN OUT EmpTabTyp) IS BEGIN NULL; END; PROCEDURE do_nothing2 (tab IN OUT NOCOPY EmpTabTyp) IS BEGIN NULL; END; BEGIN SELECT * INTO emp_tab(1) FROM employees WHERE employee_id = 100; emp_tab.EXTEND(49999, 1); -- copy element 1 into 2..50000 get_time(t1); do_nothing1(emp_tab); -- pass IN OUT parameter get_time(t2); do_nothing2(emp_tab); -- pass IN OUT NOCOPY parameter get_time(t3); DBMS_OUTPUT.PUT_LINE('Call Duration (secs)'); DBMS_OUTPUT.PUT_LINE('--------------------'); DBMS_OUTPUT.PUT_LINE('Just IN OUT: ' || TO_CHAR((t2 - t1)/100.0)); DBMS_OUTPUT.PUT_LINE('With NOCOPY: ' || TO_CHAR((t3 - t2))/100.0); END; / Restrictions on NOCOPY The use of NOCOPY increases the likelihood of parameter aliasing. For more information, see "Understanding Subprogram Parameter Aliasing". Remember, NOCOPY is a hint, not a directive. In the following cases, the PL/SQL compiler ignores the NOCOPY hint and uses the by-value parameter-passing method; no error is generated: The actual parameter is an element of an associative array. This restriction does not apply if the parameter is an entire associative array. The actual parameter is constrained, such as by scale or NOT NULL. This restriction does not apply to size-constrained character strings. This restriction does not extend to constrained elements or attributes of composite types. The actual and formal parameters are records, one or both records were declared using %ROWTYPE or %TYPE, and constraints on corresponding fields in the records differ. The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR loop, and constraints on corresponding fields in the records differ. Passing the actual parameter requires an implicit datatype conversion. The subprogram is called through a database link or as an external procedure. Compiling PL/SQL Code for Native Execution You can speed up PL/SQL procedures by compiling them into native code residing in shared libraries. The procedures are translated into C code, then compiled with your usual C compiler and linked into the Oracle process. You can use native compilation with both the supplied Oracle packages, and procedures you write yourself. Procedures compiled this way work in all server environments, such as the shared server configuration (formerly known as multi-threaded server) and Oracle Real Application Clusters. See Also: Best practices and additional information on setting up PL/SQL native compilation on the Oracle Technology Network (OTN): http://www.oracle.com/technology/tech/pl_sql/ Additional information about native compilation, such as Note 269012.1, on Oracle Metalink: http://metalink.oracle.com Before You Begin If you are a first-time user of native PL/SQL compilation, try it first with a test database, before proceeding to a production environment. Always back up your database before configuring the database for PL/SQL native compilation. If you find that the performance benefit is outweighed by extra compilation time, it might be faster to restore from a backup than to recompile everything in interpreted mode. Some of the setup steps require DBA authority. You must change the values of some initialization parameters, and create a new directory on the database server, preferably near the data files for the instance. The database server also needs a C compiler; on a cluster, the compiler is needed on each node. Even if you can test out these steps yourself on a development machine, you will generally need to consult with a DBA and enlist their help to use native compilation on a production server. Note: The pre-requirements for using native compiled PL/SQL code are documented for each platform in the individual Oracle database installation guides. Check the software requirements section for the certified compilers for native compilation on your platform to ensure that you use a certified compiler. Determining Whether to Use PL/SQL Native Compilation PL/SQL native compilation provides the greatest performance gains for computation-intensive procedural operations. Examples of such operations are data warehouse applications, and applications with extensive server-side transformations of data for display. In such cases, expect speed increases of up to 30%. Because this technique cannot do much to speed up SQL statements called from PL/SQL, it is most effective for compute-intensive PL/SQL procedures that do not spend most of their time executing SQL. You can test to see how much performance gain you can get by enabling PL/SQL native compilation. It takes longer to compile program units with native compilation than to use the default interpreted mode. You might turn off native compilation during the busiest parts of the development cycle, where code is being frequently recompiled. If you have determined that there will be significant performance gains in database operations using PL/SQL native compilation, Oracle Corporation recommends that you compile the entire database using the NATIVE setting. Compiling all the PL/SQL code in the database means you see the speedup in your own code, and in calls to all the built-in PL/SQL packages. If interpreted compilation is required for your environment, you can compile all the PL/SQL units to INTERPRETED. For example, if you import an entire database that includes NATIVE PL/SQL units into an environment where a C compiler is unavailable. To convert an entire database to NATIVE or INTERPRETED compilation, see "Modifying the Entire Database for PL/SQL Native or Interpreted Compilation". How PL/SQL Native Compilation Works If you do not use native compilation, each PL/SQL program unit is compiled into an intermediate form, machine-readable code (m-code). The m-code is stored in the database dictionary and interpreted at run time. With PL/SQL native compilation, the PL/SQL statements are turned into C code that bypasses all the runtime interpretation, giving faster runtime performance. PLSQL_ initialization parameters set up the environment for PL/SQL native compilation, as described in "Setting up Initialization Parameters for PL/SQL Native Compilation". PL/SQL uses the command file $ORACLE_HOME/plsql/spnc_commands, and the supported operating system C compiler and linker, to compile and link the resulting C code into shared libraries. See "The spnc_commands File". The shared libraries are stored inside the data dictionary, so that they can be backed up automatically and are protected from being deleted. These shared library files are copied to the file system and are loaded and run when the PL/SQL subprogram is invoked. If the files are deleted from the file system while the database is shut down, or if you change the directory that holds the libraries, they are extracted again automatically. Although PL/SQL program units that just call SQL statements might see little or no speedup, natively compiled PL/SQL is always at least as fast as the corresponding interpreted code. The compiled code makes the same library calls as the interpreted code would, so its behavior is exactly the same. Dependencies, Invalidation and Revalidation After the procedures are compiled and turned into shared libraries, they are automatically linked into the Oracle process. You do not need to restart the database, or move the shared libraries to a different location. You can call back and forth between stored procedures, whether they are all interpreted, all compiled for native execution, or a mixture of both. Recompilation is automatic with invalidated PL/SQL modules. For example, if an object on which a natively compiled PL/SQL subprogram depends changes, the subprogram is invalidated. The next time the same subprogram is called, the database recompiles the subprogram automatically. Because the PLSQL_CODE_TYPE setting is stored inside the library unit for each subprogram, the automatic recompilation uses this stored setting for code type. The stored settings are only used when recompiling as part of revalidation. If a PL/SQL subprogram is explicitly compiled through the SQL commands CREATE OR REPLACE or ALTER...COMPILE, the current session setting is used. See also "Initialization Parameters for PL/SQL Compilation". The generated shared libraries are stored in the database, in the SYSTEM tablespace. The first time a natively compiled procedure is executed, the corresponding shared library is copied from the database to the directory specified by the initialization parameter PLSQL_NATIVE_LIBRARY_DIR. Real Application Clusters and PL/SQL Native Compilation Because any node might need to compile a PL/SQL subprogram, each node in the cluster needs a C compiler and correct settings and paths in the $ORACLE_HOME/plsql/spnc_commands file. When you use PLSQL native compilation in a Real Application Clusters (RAC) environment, the original copies of the shared library files are stored in the databases, and these files are automatically propagated to all nodes in the cluster. You do not need to do any copying of libraries for this feature to work. Check that all nodes of a RAC cluster use the same settings for the initialization parameters that control PL/SQL native compilation. Make sure the path specified in PLSQL_NATIVE_LIBRARY_DIR where the shared libraries are placed is created identically on all nodes that are part of the cluster. Limitations of Native Compilation The following are some limitations of native compilation: Debugging tools for PL/SQL do not handle procedures compiled for native execution. When many procedures and packages (typically, over 15000) are compiled for native execution, the large number of shared objects in a single directory might affect system performance. See "Setting Up PL/SQL Native Library Subdirectories" for a workaround. The spnc_commands File The spnc_commands file, in the $ORACLE_HOME/plsql directory, contains the templates for the commands to compile and link each program. Some special names such as %(src) are predefined, and are replaced by the corresponding filename. The variable $(ORACLE_HOME) is replaced by the location of the Oracle home directory. Comment lines start with a # character. The file contains comments that explain all the special notation. The spnc_commands file contains a predefined path for the default C compiler, depending on the particular operating system. A specific compiler is supported on each operating system. Only one compiler can be used to compile the PL/SQL modules; do not compile PL/SQL modules in a database with different compilers. You can view spnc_commands file to confirm that the command templates are correct. You should not need to change this file unless the system administrator has installed the C compiler in another location or you want to use a different supported C compiler. Additional information can be found in the Oracle database installation guide for your platform and by searching for spnc_commands on Oracle Metalink at http://metalink.oracle.com. Setting up Initialization Parameters for PL/SQL Native Compilation This section describes the initialization parameters that are used to set PL/SQL native compilation. PLSQL_NATIVE_LIBRARY_DIR PLSQL_NATIVE_LIBRARY_SUBDIR_COUNT PLSQL_CODE_TYPE To check the settings of these parameters, enter the following in SQL*Plus: SHOW PARAMETERS PLSQL See also "Initialization Parameters for PL/SQL Compilation". PLSQL_NATIVE_LIBRARY_DIR Initialization Parameter This is a required system-level only parameter that specifies the full path and directory name of the location of the shared libraries that contain natively compiled PL/SQL code. The value must be explicit and point to an existing, accessible directory; the path cannot contain a variable such as ORACLE_HOME. Use the ALTER SYSTEM command or update the initialization file to set the parameter value. For example, if the path to the PL/SQL native library directory is /oracle/oradata/db1/natlib, then the setting in the initialization file is: PLSQL_NATIVE_LIBRARY_DIR='/oracle/oradata/db1/natlib' In accordance with optimal flexible architecture (OFA) rules, Oracle Corporation recommends that you create the shared library directory as a subdirectory where the data files are located. For security reasons, only the users oracle and root should have write privileges for this directory. If using a Real Applications Cluster environment, see "Real Application Clusters and PL/SQL Native Compilation". For details about the PLSQL_NATIVE_LIBRARY_DIR i

curious update in oracle, collect from web

oracle admin — 作者 littledan @ 10:25

update
 
ORACLE UPDATE 语句语法与性能分析的一点看法

为了方便起见,建立了以下简单模型,和构造了部分测试数据:
在某个业务受理子系统BSS中,
--客户资料表
create table customers
(
customer_id number(8) not null, -- 客户标示
city_name varchar2(10) not null, -- 所在城市
customer_type char(2) not null, -- 客户类型

...
)
create unique index PK_customers on customers (customer_id)
由于某些原因,客户所在城市这个信息并不什么准确,但是在
客户服务部的CRM子系统中,通过主动服务获取了部分客户20%的所在
城市等准确信息,于是你将该部分信息提取至一张临时表中:
create table tmp_cust_city
(
customer_id number(8) not null,
citye_name varchar2(10) not null,
customer_type char(2) not null
)

1) 最简单的形式
--经确认customers表中所有customer_id小于1000均为'北京'
--1000以内的均是公司走向全国之前的本城市的老客户:)
update customers
set city_name='北京'
where customer_id<1000

2) 两表(多表)关联update -- 仅在where字句中的连接
--这次提取的数据都是VIP,且包括新增的,所以顺便更新客户类别
update customers a -- 使用别名
set customer_type='01' --01 为vip,00为普通
where exists (select 1
from tmp_cust_city b
where b.customer_id=a.customer_id
)

3) 两表(多表)关联update -- 被修改值由另一个表运算而来
update customers a -- 使用别名
set city_name=(select b.city_name from tmp_cust_city b where b.customer_id=a.customer_id)
where exists (select 1
from tmp_cust_city b
where b.customer_id=a.customer_id
)
-- update 超过2个值
update customers a -- 使用别名
set (city_name,customer_type)=(select b.city_name,b.customer_type
from tmp_cust_city b
where b.customer_id=a.customer_id)
where exists (select 1
from tmp_cust_city b
where b.customer_id=a.customer_id
)
注意在这个语句中,
=(select b.city_name,b.customer_type
from tmp_cust_city b
where b.customer_id=a.customer_id
)

(select 1
from tmp_cust_city b
where b.customer_id=a.customer_id
)
是两个独立的子查询,查看执行计划可知,对b表/索引扫描了2篇;
如果舍弃where条件,则默认对A表进行全表
更新,但由于(select b.city_name from tmp_cust_city b where where b.customer_id=a.customer_id)
有可能不能提供"足够多"值,因为tmp_cust_city只是一部分客户的信息,
所以报错(如果指定的列--city_name可以为NULL则另当别论):

01407, 00000, "cannot update (%s) to NULL"
// *Cause:
// *Action:

一个替代的方法可以采用:
update customers a -- 使用别名
set city_name=nvl((select b.city_name from tmp_cust_city b where b.customer_id=a.customer_id),a.city_name)
或者
set city_name=nvl((select b.city_name from tmp_cust_city b where b.customer_id=a.customer_id),'未知')
-- 当然这不符合业务逻辑了

4) 上述3)在一些情况下,因为B表的纪录只有A表的20-30%的纪录数,
考虑A表使用INDEX的情况,使用cursor也许会比关联update带来更好的性能:

set serveroutput on

declare
cursor city_cur is
select customer_id,city_name
from tmp_cust_city
order by customer_id;
begin
for my_cur in city_cur loop

update customers
set city_name=my_cur.city_name
where customer_id=my_cur.customer_id;

/** 此处也可以单条/分批次提交,避免锁表情况 **/
-- if mod(city_cur%rowcount,10000)=0 then
-- dbms_output.put_line('----');
-- commit;
-- end if;
end loop;
end;

5) 关联update的一个特例以及性能再探讨
在oracle的update语句语法中,除了可以update表之外,也可以是视图,所以有以下1个特例:
update (select a.city_name,b.city_name as new_name
from customers a,
tmp_cust_city b
where b.customer_id=a.customer_id
)
set city_name=new_name
这样能避免对B表或其索引的2次扫描,但前提是 A(customer_id) b(customer_id)必需是unique index
或primary key。否则报错:

01779, 00000, "cannot modify a column which maps to a non key-preserved table"
// *Cause: An attempt was made to insert or update columns of a join view which
// map to a non-key-preserved table.
// *Action: Modify the underlying base tables directly.

6)oracle另一个常见错误
回到3)情况,由于某些原因,tmp_cust_city customer_id 不是唯一index/primary key
update customers a -- 使用别名
set city_name=(select b.city_name from tmp_cust_city b where b.customer_id=a.customer_id)
where exists (select 1
from tmp_cust_city b
where b.customer_id=a.customer_id
)
当对于一个给定的a.customer_id
(select b.city_name from tmp_cust_city b where b.customer_id=a.customer_id)
返回多余1条的情况,则会报如下错误:

01427, 00000, "single-row subquery returns more than one row"
// *Cause:
// *Action:

一个比较简单近似于不负责任的做法是
update customers a -- 使用别名
set city_name=(select b.city_name from tmp_cust_city b where b.customer_id=a.customer_id)

如何理解 01427 错误,在一个很复杂的多表连接update的语句,经常因考虑不周,出现这个错误,
仍已上述例子来描述,一个比较简便的方法就是将A表代入 值表达式 中,使用group by 和
having 字句查看重复的纪录
(select b.customer_id,b.city_name,count(*)
from tmp_cust_city b,customers a
where b.customer_id=a.customer_id
group by b.customer_id,b.city_name
having count(*)>=2
)

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=581488

×××××××××××××××××××××××××××

Oracle中的UPDATE FROM解决方法

来源:雪精灵社区 等级:III


今天又碰到了这个问题,在Oracle中没有update from这样的语法,但可以实现同样的功能。

表的更新操作中,在很多情况下需要在表达式中引用要更新的表以外的数据。象sql server提供了update的from 子句,可以将要更新的表与其它的数据源连接起来。虽然只能对一个表进行更新,但是通过将要更新的表与其它的数据源连接起来,就可以在update的表达式中引用要更新的表以外的其它数据。

例如:

UPDATE dbo.Table2
SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
FROM dbo.Table2
INNER JOIN dbo.Table1
ON (dbo.Table2.ColA = dbo.Table1.ColA);


实际更新的操作是在要更新的表上进行的,而不是在from子句所形成的新的结果集上进行的。

Oracle没有update from语法,可以通过两种写法实现同样的功能:

1:子查询UPDATE A SET A.NAME=(SELECT B.NAME FROM B WHERE B.ID=A.ID),本查询要根据具体情况看看是否变通成如下

UPDATE A SET A.NAME=(SELECT B.NAME FROM B WHERE B.ID=A.ID) WHERE A.ID IN (SELECT ID FROM B);


2:利用视图来做

UPDATE (SELECT A.NAME ANAME,B.NAME BNAME FROM A,B WHERE A.ID=B.ID)
SET ANAME=BNAME;


例如:

UPDATE tablea a
SET a.fieldforupdate = (SELECT b.fieldsource FROM tableb b
WHERE a.keyfield = b.keyfield)
WHERE EXISTS (SELECT b.fieldsource FROM tableb b
WHERE a.keyfield = b.keyfield)


有三点需要注意:

1. 对于一个给定的a.keyfield的值,SELECT b.fieldsource FROM tableb b WHERE a.keyfield = b.keyfield 的值只能是一个唯一值,不能是多值。

2. 在绝大多数情况下,最后面的where EXISTS子句是重要的,否则将得到错误的结果。

3. 对于视图更新的限制:

如果视图基于多个表的连接,那么用户更新(update)视图记录的能力将受到限制。除非update只涉及一个表且视图列中包含了被更新的表的整个主键,否则不能更新视图的基表。

×××××××××××××××××××××××××××

[转载]Oracle Update Statements Version 10.2

Basic Update Statements
Update all records UPDATE <table_name>
SET <column_name> = <value>
CREATE TABLE test AS
SELECT object_name, object_type
FROM all_objects;

SELECT DISTINCT object_name
FROM test;

UPDATE test
SET object_name = 'OOPS';

SELECT DISTINCT object_name
FROM test;

ROLLBACK;
Update a specific record UPDATE <table_name>
SET <column_name> = <value>
WHERE <column_name> = <value>
SELECT DISTINCT object_name
FROM test;

UPDATE test
SET object_name = 'LOAD'
WHERE object_name = 'DUAL';

COMMIT;

SELECT DISTINCT object_name
FROM test
Update based on a single queried value UPDATE <table_name>
SET <column_name> = (
SELECT <column_name>
FROM <table_name
WHERE <column_name> <condition> <value>);
CREATE TABLE test AS
SELECT table_name
FROM all_tables;

ALTER TABLE test
ADD (lower_name VARCHAR2(30));

SELECT *
FROM test
WHERE table_name LIKE '%A%';

UPDATE test t
SET lower_name = (
SELECT DISTINCT lower(table_name)
FROM all_tables a
WHERE a.table_name = t.table_name
AND a.table_name LIKE '%A%');

COMMIT;

SELECT *
FROM test;

Update based on a query returning multiple values UPDATE <table_name> <alias>
SET (<column_name>,<column_name> ) = (
SELECT (<column_name>, <column_name>)
FROM <table_name>
WHERE <alias.column_name> = <alias.column_name>);
CREATE TABLE test AS
SELECT t. table_name, t. tablespace_name, s.extent_management
FROM user_tables t, user_tablespaces s
WHERE t.tablespace_name = s. tablespace_name
AND 1=2;

desc test

SELECT * FROM test;

-- does not work
UPDATE test
SET (table_name, tablespace_name) = (
SELECT table_name, tablespace_name
FROM user_tables);

-- works
INSERT INTO test
(table_name, tablespace_name)
SELECT table_name, tablespace_name
FROM user_tables;

COMMIT;

SELECT *
FROM test
WHERE table_name LIKE '%A%';

-- does not work
UPDATE test t
SET tablespace_name, extent_management = (
SELECT tablespace_name, extent_management
FROM user_tables a, user_tablespaces u
WHERE t.table_name = a.table_name
AND a.tablespace_name = u.tablespace_name
AND t.table_name LIKE '%A%');

-- does not works
UPDATE test t
SET (tablespace_name, extent_management) = (
SELECT DISTINCT u.tablespace_name, u.extent_management
FROM user_tables a, user_tablespaces u
WHERE t.table_name = a.table_name
AND a.tablespace_name = u.tablespace_name
AND t.table_name LIKE '%A%');

rollback;

-- works
UPDATE test t
SET (tablespace_name, extent_management) = (
SELECT DISTINCT u.tablespace_name, u.extent_management
FROM user_tables a, user_tablespaces u
WHERE t.table_name = a.table_name
AND a.tablespace_name = u.tablespace_name)
WHERE t.table_name LIKE '%A%';

COMMIT;

SELECT *
FROM test;
Update the results of a SELECT statement UPDATE (<SELECT Statement>)
SET <column_name> = <value>;
SELECT *
FROM test
WHERE table_name LIKE '%A%';

SELECT *
FROM test
WHERE table_name NOT LIKE '%A%';

UPDATE (
SELECT *
FROM test
WHERE table_name NOT LIKE '%A%')
SET extent_management = 'Unknown';

SELECT *
FROM test;

Correlated Update
Single column UPDATE TABLE(<SELECT STATEMENT>) <alias>
SET <column_name> = (
SELECT <column_name>
FROM <table_name> <alias>
WHERE <alias.table_name> = <alias.table_name>;
conn hr/hr

CREATE TABLE empbak AS
SELECT * FROM employees;

UPDATE employees
SET salary = salary * 1.1;
COMMIT;

UPDATE employees t1
SET salary = (
SELECT salary
FROM empbak t2
WHERE t1.employee_id = t2.employee_id);
Multi-column UPDATE TABLE(<SELECT STATEMENT>) <alias>
SET <column_name> = <value>;
UPDATE table1 t_alias1
SET (col1, col2) = (
SELECT col1, col2
FROM table2 t_alias2
WHERE t_alias1.col3 = t_alias2.col3);

Nested Table Update
See Nested Tables page

Update With Returning Clause
Returning Clause demo UPDATE (<SELECT Statement>)
SET ....;
conn hr/hr

var bnd1 NUMBER
var bnd2 VARCHAR2(30)
var bnd3 NUMBER

UPDATE employees
SET job_id ='SA_MAN', salary = salary + 1000,
department_id = 140
WHERE last_name = 'Jones'
RETURNING salary*0.25, last_name, department_id
INTO :bnd1, :bnd2, :bnd3;

print bnd1
print bnd2
print bnd3
conn hr/hr

variable bnd1 NUMBER

UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 100
RETURNING SUM(salary) INTO :bnd1;

print bnd1

Update Object Table
Update a table object UPDATE (<SELECT Statement>)
SET ....;
UPDATE table1 p SET VALUE(p) =
(SELECT VALUE(q) FROM table2 q WHERE p.id = q.id)
WHERE p.id = 10;

Record Update
Update based on a record UPDATE <table_name>
SET ROW = <record_name>;
CREATE TABLE t AS
SELECT table_name, tablespace_name
FROM all_tables;

SELECT DISTINCT tablespace_name
FROM t;

DECLARE
trec t%ROWTYPE;
BEGIN
trec.table_name := 'DUAL';
trec.tablespace_name := 'NEW_TBSP';

UPDATE t
SET ROW = trec
WHERE table_name = 'DUAL';

COMMIT;
END;
/

SELECT DISTINCT tablespace_name
FROM t;

Update Partitioned Table
Update only records in a single partition UPDATE <table_name> PARTITION <partition_name>
SET <column_name> = <value>
WHERE <expression>;
UPDATE sales PARTITION (sales_q1_2005) s
SET s.promo_id = 494
WHERE amount_sold > 9000;

×××××××××××××××××××××××××××


在两张表之间使用UPDATE语句???

楼主xiaoailiu(小艾)2002-08-28 09:57:11 在 Oracle / 基础和管理 提问

两张表:id,ida
select * from id;
ID A B C
---------- - - -
1 a c c
2 a c c
3 a c c
4 a c c
select * from ida;
ID A B C
---------- - - -
2 A C C
3 A C C
1 A C C
4 A C C
想用IDA表的A,B,C字段替换到ID表中相同ID的A,B,C字段上去??
请大家帮助!!!

---------------------------------------------------
人见人爱的静静小艾与你交流,共享!

问题点数:0、回复次数:16Top


1 楼bluecetacean(剑鱼)回复于 2002-08-28 10:03:31 得分 0


update id set A=(select A from id,ida where id.id=ida.id),B=(select B from id,ida where id.id=ida.id),C=(select C from id,ida where id.id=ida.id)Top


2 楼xiaoailiu(小艾)回复于 2002-08-28 10:07:28 得分 0


楼上的朋友,请问这句你执行过吗????
我这执行是不正确的!Top


3 楼myxchy(阿呆)回复于 2002-08-28 10:23:50 得分 0


update id t1 set a=(select t2.a from ida t2 where t1.id=t2.id),b=(select t2.b from ida t2 where t1.id=t2.id),c=(select t2.c from ida t2 where t1.id=t2.id)Top


4 楼gwolfupb(括弧)回复于 2002-08-28 10:25:02 得分 0


delete from id where id in (select id from ida);
insert into id select * from ida;Top


5 楼myxchy(阿呆)回复于 2002-08-28 10:30:56 得分 0


还少一句条件
where t1.id=t2.idTop


6 楼myxchy(阿呆)回复于 2002-08-28 10:35:41 得分 0


delete from id where id in (select id from ida);
insert into id select * from ida where id.id=ida.id;

Top


7 楼black_dragon(半仙)回复于 2002-08-28 10:39:29 得分 0


update id set (a,b,c)=(select a,b,c from ida where ida.id=id.id);Top


8 楼myxchy(阿呆)回复于 2002-08-28 10:46:23 得分 0


或者
update id t1 set a=(select t2.a from ida t2 where t1.id=t2.id),b=(select t2.b from ida t2 where t1.id=t2.id),c=(select t2.c from ida t2 where t1.id=t2.id) where t1.id in(select t3.id from id t3,ida t4
where t3.id=t4.id)
Top


9 楼myxchy(阿呆)回复于 2002-08-28 10:54:39 得分 0


半仙的写法不对
Top


10 楼myxchy(阿呆)回复于 2002-08-28 11:00:19 得分 0


update id t1 set a=(select t2.a from ida t2 where t1.id=t2.id),b=(select t2.b from ida t2 where t1.id=t2.id),c=(select t2.c from ida t2 where t1.id=t2.id) where exists (select * from id,ida where id.id=ida.id)

Top


11 楼black_dragon(半仙)回复于 2002-08-28 11:12:27 得分 0


myxchy(阿呆) :
我的数据库是8.1.7,语句我已经验证过了,正确!Top


12 楼lgm76(lgm)回复于 2002-09-02 14:39:30 得分 0


update a set a.a = b.a , a.b = b.b , a.c=b.c from id a , ida b where a.id = b.idTop


13 楼lyl000(liyl)回复于 2002-09-02 17:02:32 得分 0


楼上的与具有错误,不过思路挺好,但没有调出来,它可能做到一条语句更新多个表的
black_dragon(半仙)的方法非常好


Top


14 楼luckysxn(坚持到底)回复于 2002-09-02 17:24:45 得分 0


楼上的楼上。你以为ORACLE是SQLSERVER 或是SYBASE 呢。

半仙是对的。应该没有什么问题。
Top


15 楼biti_rainy(biti_rainy)回复于 2002-09-04 21:37:12 得分 0


http://www.itpub.net/showthread.php?s=&threadid=45822&highlight=%BE%AD%B5%E4

可以给大家以参考
注意8i和9i的变化Top


16 楼jettli(李杰)回复于 2002-09-17 10:56:38 得分 0


我也有同样的问题,我觉得这些回答好象都不太好吧,因为如果两表的数据是一对多的,会发现返回多行数据的情况,就不好解决了但是我记得在某书中见一过,想不起来了,我也想知道一个好的UPDATE的方法


有关UPdate语句的吐血问题

_____


发布时间: 2006-12-15 ; 上次回复: 2006-12-15; 总计回复: 16人次

_____


我有下面的一个批处理更新的SQL Server的语句,请问转到Oracle的语法是怎样的?
这句SQL的含义是,用b表与a表关联,满足条件的a表的记录的字段用B表更新。如果不能实现这个,岂不是样定义Cursor来循环?
UPdate tOperAll set fEmplName = b.EMPNAME ,fPWD = b.PassWord
from tOperAll a ,Employee b
Where a.EMPNO = b.fEmplNo
and a.fSysNo = '1'
哪位大侠能否提供一些Oracle的SQL写法的例子,包括左连接,右连接,子查询等内容,谢谢。


_____

bluelamb [等级:◆◆(初级)] (信誉值: 100) 回复于: 2004-10-12 10:56:19 Top

转换为oracle为:
update (select a.*,b.* from tOperAll a ,Employee b
where a.EMPNO = b.fEmplNo
and a.fSysNo = '1') set fEmplName =EMPNAME ,fPWD = PassWord

_____

匿名 [等级:◆(初级)] (信誉值: 100) 回复于: 2004-10-12 10:59:55 Top

谢谢大哥,我马上Try,看结构很有道理,还有其他的写法吗?


_____

bluelamb [等级:◆◆(初级)] (信誉值: 100) 回复于: 2004-10-12 11:00:55 Top

或者
update tOperAll a set fEmplName =(select EMPNAME from Employee b
where Where a.EMPNO = b.fEmplNo
and a.fSysNo = '1'),fPWD =select PassWord from Employee b
where Where a.EMPNO = b.fEmplNo
and a.fSysNo = '1');
这样比较罗索


_____

SongZip [等级:★★(中级)] (信誉值: 149) 回复于: 2004-10-12 11:03:37 Top

UPdate tOperAll a set (fEmplName,fPWD) = (
select b.EMPNAME,b.PassWord
from Employee b
Where a.EMPNO = b.fEmplNo
and a.fSysNo = '1')
where exits(select b.EMPNAME,b.PassWord
from Employee b
Where a.EMPNO = b.fEmplNo
and a.fSysNo = '1')


_____

@ [等级:◆(初级)] (信誉值: 100) 回复于: 2004-10-12 11:08:22 Top

UPdate tOperAll a set
fEmplName = (select b.EMPNAME from Employee b Where a.EMPNO = b.fEmplNo ),
fPWD = (select c.PassWord from Employee c Where a.EMPNO = c.fEmplNo )
where a.fSysNo = '1'

没调过,试试吧


_____

@ [等级:◆(初级)] (信誉值: 100) 回复于: 2004-10-12 11:10:11 Top

UPdate tOperAll a set
(fEmplName,fPWD) = (select b.EMPNAME,b.PassWord from Employee b Where a.EMPNO = b.fEmplNo )
where a.fSysNo = '1'

_____

bluelamb [等级:◆◆(初级)] (信誉值: 100) 回复于: 2004-10-12 11:14:31 Top

还有就是
UPdate tOperAll a set
(fEmplName,fPWD) = (select b.EMPNAME,b.PassWord from Employee b Where a.EMPNO = b.fEmplNo )
where a.fSysNo = '1'


_____

匿名 [等级:◆(初级)] (信誉值: 100) 回复于: 2004-10-12 11:15:59 Top

还有吗?让我开开眼界,我只熟悉SQL SERVER的语法。谢谢了


_____

匿名 [等级:◆(初级)] (信誉值: 100) 回复于: 2004-10-12 11:27:36 Top

csdn的高手很多,热心人也很多,谢谢.


_____

king [等级:◆(初级)] (信誉值: 98) 回复于: 2004-10-12 13:40:10 Top

update (select a.*,b.* from tOperAll a ,Employee b
where a.EMPNO = b.fEmplNo
and a.fSysNo = '1') set fEmplName =EMPNAME ,fPWD = PassWord

这种写法还是第一次看见,真是长见识了!


_____

匿名 [等级:◆(初级)] (信誉值: 100) 回复于: 2004-10-12 15:27:19 Top

update (select a.*,b.* from tOperAll a ,Employee b
where a.EMPNO = b.fEmplNo
and a.fSysNo = '1') set fEmplName =EMPNAME ,fPWD = PassWord

这种写法效率应该比较好,普通的写法是什么?


_____

匿名 [等级:◆(初级)] (信誉值: 100) 回复于: 2004-10-12 15:27:36 Top

update (select a.*,b.* from tOperAll a ,Employee b
where a.EMPNO = b.fEmplNo
and a.fSysNo = '1') set fEmplName =EMPNAME ,fPWD = PassWord

这种写法效率应该比较好,普通的写法是什么?



AWR vs statspack‘s snap method

oracle test — 作者 littledan @ 10:20

Oracle AWR and STATSPACK Manual snapshot commands

There are times when you want to manually collect AWR and STATSPACK snapshots, such as when you are experiencing a performance problem or doing a stress test. You can issue these commands to get a manual AWR or STATSPACK snapshot at whatever elapsed time interval you desire:

AWR manual snapshot:

EXEC DBMS_WORKLOAD_REPOSITORY.create_snapshot;

STATSPACK manual snapshot:

exec statspack.snap;



sql server case vs decode,转的

一般分类 — 作者 littledan @ 10:18

Oracle:
select decode(a1,'0', 'easy', '1', 'normal', '2', 'hard') level from a;

转成SQL Server为:
select (case a1 when '0' then 'easy'
when '1' then 'normal'
when '2' then 'hard' end) level
from a



row chain VS row migration

oracle admin — 作者 littledan @ 10:13

Overview

If you notice poor performance in your Oracle database Row Chaining and Migration may be one of several reasons, but we can prevent some of them by properly designing and/or diagnosing the database.

Row Migration & Row Chaining are two potential problems that can be prevented. By suitably diagnosing, we can improve database performance. The main considerations are:

  • What is Row Migration & Row Chaining ?
  • How to identify Row Migration & Row Chaining ?
  • How to avoid Row Migration & Row Chaining ?

Migrated rows affect OLTP systems which use indexed reads to read singleton rows. In the worst case, you can add an extra I/O to all reads which would be really bad. Truly chained rows affect index reads and full table scans.

Oracle Block

The Operating System Block size is the minimum unit of operation (read /write) by the OS and is a property of the OS file system. While creating an Oracle database we have to choose the «Data Base Block Size» as a multiple of the Operating System Block size. The minimum unit of operation (read /write) by the Oracle database would be this «Oracle block», and not the OS block. Once set, the «Data Base Block Size» cannot be changed during the life of the database (except in case of Oracle 9i). To decide on a suitable block size for the database, we take into consideration factors like the size of the database and the concurrent number of transactions expected.

The database block has the following structure (within the whole database structure)

Header

Header contains the general information about the data i.e. block address, and type of segments (table, index etc). It Also contains the information about table and the actual row (address) which that holds the data.

Free Space

Space allocated for future update/insert operations. Generally affected by the values of PCTFREE and PCTUSED parameters.

Data

Actual row data.

FREELIST, PCTFREE and PCTUSED

While creating / altering any table/index, Oracle used two storage parameters for space control.

  • PCTFREE - The percentage of space reserved for future update of existing data.
  • PCTUSED - The percentage of minimum space used for insertion of new row data.
    This value determines when the block gets back into the FREELISTS structure.
  • FREELIST - Structure where Oracle maintains a list of all free available blocks.

Oracle will first search for a free block in the FREELIST and then the data is inserted into that block. The availability of the block in the FREELIST is decided by the PCTFREE value. Initially an empty block will be listed in the FREELIST structure, and it will continue to remain there until the free space reaches the PCTFREE value.

When the free space reach the PCTFREE value the block is removed from the FREELIST, and it is re-listed in the FREELIST table when the volume of data in the block comes below the PCTUSED value.

Oracle use FREELIST to increase the performance. So for every insert operation, oracle needs to search for the free blocks only from the FREELIST structure instead of searching all blocks.

Row Migration

We will migrate a row when an update to that row would cause it to not fit on the block anymore (with all of the other data that exists there currently). A migration means that the entire row will move and we just leave behind the «forwarding address». So, the original block just has the rowid of the new block and the entire row is moved.

Full Table Scans are not affected by migrated rows

The forwarding addresses are ignored. We know that as we continue the full scan, we'll eventually get to that row so we can ignore the forwarding address and just process the row when we get there. Hence, in a full scan migrated rows don't cause us to really do any extra work -- they are meaningless.

Index Read will cause additional IO's on migrated rows

When we Index Read into a table, then a migrated row will cause additional IO's. That is because the index will tell us «goto file X, block Y, slot Z to find this row». But when we get there we find a message that says «well, really goto file A, block B, slot C to find this row». We have to do another IO (logical or physical) to find the row.

Row Chaining

A row is too large to fit into a single database block. For example, if you use a 4KB blocksize for your database, and you need to insert a row of 8KB into it, Oracle will use 3 blocks and store the row in pieces. Some conditions that will cause row chaining are: Tables whose rowsize exceeds the blocksize. Tables with long and long raw columns are prone to having chained rows. Tables with more then 255 columns will have chained rows as Oracle break wide tables up into pieces. So, instead of just having a forwarding address on one block and the data on another we have data on two or more blocks.

Chained rows affect us differently. Here, it depends on the data we need. If we had a row with two columns that was spread over two blocks, the query:

select column1 from table

where column1 is in Block 1, would not cause any «table fetch continued row». It would not actually have to get column2, it would not follow the chained row all of the way out. On the other hand, if we ask for:

select column2 from table

and column2 is in Block 2 due to row chaining, then you would in fact see a «table fetch continued row»

Example

The following example was published by Tom Kyte, it will show row migration and chaining. We are using an 4k block size:

select name,value
from v$parameter
where name = 'db_block_size';

NAME VALUE
-------------- ------
db_block_size 4096

Create the following table with CHAR fixed columns:

create table row_mig_chain_demo (
x int primary key,
a char(1000),
b char(1000),
c char(1000),
d char(1000),
e char(1000)
);

That is our table. The char(1000)'s will let us easily cause rows to migrate or chain. We used 5 columns a,b,c,d,e so that the total rowsize can grow to about 5K, bigger than one block, ensuring we can truly chain a row.

insert into row_mig_chain_demo (x) values (1);
insert into row_mig_chain_demo (x) values (2);
insert into row_mig_chain_demo (x) values (3);
commit;

We are not interested about seeing a,b,c,d,e - just fetching them. They are really wide so we'll surpress their display.

column a noprint
column b noprint
column c noprint
column d noprint
column e noprint

select * from row_mig_chain_demo;

X
----------
1
2
3

Check for chained rows:

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
table fetch continued row                                                 0

Now that is to be expected, the rows came out in the order we put them in (Oracle full scanned this query, it processed the data as it found it). Also expected is the table fetch continued row is zero. This data is so small right now, we know that all three rows fit on a single block. No chaining.

Demonstration of the Row Migration

Now, lets do some updates in a specific way. We want to demonstrate the row migration issue and how it affects the full scan:

update row_mig_chain_demo set a = 'z1', b = 'z2', c = 'z3' where x = 3;
commit;
update row_mig_chain_demo set a = 'y1', b = 'y2', c = 'y3' where x = 2;
commit;
update row_mig_chain_demo set a = 'w1', b = 'w2', c = 'w3' where x = 1;
commit;

Note the order of updates, we did last row first, first row last.

select * from row_mig_chain_demo;

X
----------
3
2
1

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 0

Interesting, the rows came out «backwards» now. That is because we updated row 3 first. It did not have to migrate, but it filled up block 1. We then updated row 2. It migrated to block 2 with row 3 hogging all of the space, it had to. We then updated row 1, it migrated to block 3. We migrated rows 2 and 1, leaving 3 where it started.

So, when Oracle full scanned the table, it found row 3 on block 1 first, row 2 on block 2 second and row 1 on block 3 third. It ignored the head rowid piece on block 1 for rows 1 and 2 and just found the rows as it scanned the table. That is why the table fetch continued row is still zero. No chaining.

So, lets see a migrated row affecting the «table fetch continued row»:

select * from row_mig_chain_demo where x = 3;

X
----------
3

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 0

This was an index range scan / table access by rowid using the primary key. We didn't increment the «table fetch continued row» yet since row 3 isn't migrated.

select * from row_mig_chain_demo where x = 1;
X
----------
1

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 1

Row 1 is migrated, using the primary key index, we forced a «table fetch continued row».

Demonstration of the Row Chaining

update row_mig_chain_demo set d = 'z4', e = 'z5' where x = 3;
commit;

Row 3 no longer fits on block 1. With d and e set, the rowsize is about 5k, it is truly chained.

select x,a from row_mig_chain_demo where x = 3;

X
----------
3

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 1

We fetched column «x» and «a» from row 3 which are located on the «head» of the row, it will not cause a «table fetch continued row». No extra I/O to get it.

select x,d,e from row_mig_chain_demo where x = 3;

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 2

Now we fetch from the «tail» of the row via the primary key index. This increments the «table fetch continued row» by one to put the row back together from its head to its tail to get that data.

Now let's see a full table scan - it is affected as well:

select * from row_mig_chain_demo;

X
----------
3
2
1

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 3

The «table fetch continued row» was incremented here because of Row 3, we had to assemble it to get the trailing columns. Rows 1 and 2, even though they are migrated don't increment the «table fetch continued row» since we full scanned.

select x,a from row_mig_chain_demo;

X
----------
3
2
1

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 3

No «table fetch continued row» since we didn't have to assemble Row 3, we just needed the first two columns.

select x,e from row_mig_chain_demo;

X
----------
3
2
1

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 4

But by fetching for d and e, we incemented the «table fetch continued row». We most likely have only migrated rows but even if they are truly chained, the columns you are selecting are at the front of the table.

So, how can you decide if you have migrated or truly chained?

Count the last column in that table. That'll force to construct the entire row.

select count(e) from row_mig_chain_demo;

COUNT(E)
----------
1

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) = 'table fetch continued row';

NAME VALUE
---------------------------------------------------------------- ----------
table fetch continued row 5

Analyse the table to verify the chain count of the table:

analyze table row_mig_chain_demo compute statistics;

select chain_cnt
from user_tables
where table_name = 'ROW_MIG_CHAIN_DEMO';

CHAIN_CNT
----------
3

Three rows that are chained. Apparently, 2 of them are migrated (Rows 1 and 2) and one is truly chained (Row 3).

Total Number of «table fetch continued row» since instance startup?

The V$SYSSTAT view tells you how many times, since the system (database) was started you did a «table fetch continued row» over all tables.

sqlplus system/

SELECT 'Chained or Migrated Rows = '||value
FROM v$sysstat
WHERE name = 'table fetch continued row';

Chained or Migrated Rows = 31637

You could have 1 table with 1 chained row that was fetched 31'637 times. You could have 31'637 tables, each with a chained row, each of which was fetched once. You could have any combination of the above -- any combo.

Also, 31'637 - maybe that's good, maybe that's bad. it is a function of

  • how long has the database has been up
  • how many rows is this as a percentage of total fetched rows.
    For example if 0.001% of your fetched are table fetch continued row, who cares!

Therefore, always compare the total fetched rows against the continued rows.

select name,value from v$sysstat where name like '%table%';

select name,value from v$sysstat where name like '%table%';

NAME VALUE
---------------------------------------------------------------- ----------
table scans (short tables) 124338
table scans (long tables) 1485
table scans (rowid ranges) 0
table scans (cache partitions) 10
table scans (direct read) 0
table scan rows gotten 20164484
table scan blocks gotten 1658293
table fetch by rowid 1883112
table fetch continued row 31637table lookup prefetch client count 0

How many Rows in a Table are chained?

The USER_TABLES tells you immediately after an ANALYZE (will be null otherwise) how many rows in the table are chained.

analyze table row_mig_chain_demo compute statistics;
select chain_cnt,
round(chain_cnt/num_rows*100,2) pct_chained,
avg_row_len, pct_free , pct_used
from user_tables
where table_name = 'ROW_MIG_CHAIN_DEMO';

CHAIN_CNT PCT_CHAINED AVG_ROW_LEN PCT_FREE PCT_USED
---------- ----------- ----------- ---------- ----------
3 100 3691 10 40

PCT_CHAINED shows 100% which means all rows are chained or migrated.

How to avoid Chained and Migrated Rows?

Increasing PCTFREE can help to avoid migrated rows. If you leave more free space available in the block, then the row has room to grow. You can also reorganize or re-create tables and indexes that have high deletion rates. If tables frequently have rows deleted, then data blocks can have partially free space in them. If rows are inserted and later expanded, then the inserted rows might land in blocks with deleted rows but still not have enough room to expand. Reorganizing the table ensures that the main free space is totally empty blocks.

The ALTER TABLE ... MOVE statement enables you to relocate data of a nonpartitioned table or of a partition of a partitioned table into a new segment, and optionally into a different tablespace for which you have quota. This statement also lets you modify any of the storage attributes of the table or partition, including those which cannot be modified using ALTER TABLE. You can also use the ALTER TABLE ... MOVE statement with the COMPRESS keyword to store the new segment using table compression.

  1. ALTER TABLE MOVEFirst count the number of Rows per Block before the ALTER TABLE MOVE

    select dbms_rowid.rowid_block_number(rowid) "Block-Nr", count(*) "Rows"
    from row_mig_chain_demo
    group by dbms_rowid.rowid_block_number(rowid) order by 1;

    Block-Nr Rows
    ---------- ----------
    2066 3
    Now, de-chain the table, the ALTER TABLE MOVE rebuilds the row_mig_chain_demo table in a new segment, specifying new storage parameters:

    ALTER TABLE row_mig_chain_demo MOVE
    PCTFREE 20
    PCTUSED 40
    STORAGE (INITIAL 20K
    NEXT 40K
    MINEXTENTS 2
    MAXEXTENTS 20
    PCTINCREASE 0);

    Table altered.
    Again count the number of Rows per Block after the ALTER TABLE MOVE

    select dbms_rowid.rowid_block_number(rowid) "Block-Nr", count(*) "Rows"
    from row_mig_chain_demo
    group by dbms_rowid.rowid_block_number(rowid) order by 1;
    Block-Nr Rows
    ---------- ----------
    2322 1
    2324 1
    2325 1
  2. Rebuild the Indexes for the TableMoving a table changes the rowids of the rows in the table. This causes indexes on the table to be marked UNUSABLE, and DML accessing the table using these indexes will receive an ORA-01502 error. The indexes on the table must be dropped or rebuilt. Likewise, any statistics for the table become invalid and new statistics should be collected after moving the table.

    analyze table row_mig_chain_demo compute statistics;

    ERROR at line 1:
    ORA-01502: index 'SCOTT.SYS_C003228' or partition of such index is in unusable
    state
    This is the primary key of the table which must be rebuilt.alter index SYS_C003228 rebuild;Index altered.analyze table row_mig_chain_demo compute statistics;Table analyzed.select chain_cnt,
    round(chain_cnt/num_rows*100,2) pct_chained,
    avg_row_len, pct_free , pct_used
    from user_tables
    where table_name = 'ROW_MIG_CHAIN_DEMO';
    CHAIN_CNT PCT_CHAINED AVG_ROW_LEN PCT_FREE PCT_USED
    ---------- ----------- ----------- ---------- ----------
    1 33.33 3687 20 40

    If the table includes LOB column(s), this statement can be used to move the table along with LOB data and LOB index segments (associated with this table) which the user explicitly specifies. If not specified, the default is to not move the LOB data and LOB index segments.

Detect all Tables with Chained and Migrated Rows

Using the CHAINED_ROWS table, you can find out the tables with chained or migrated rows.

  1. Create the CHAINED_ROWS table

    cd $ORACLE_HOME/rdbms/admin
    sqlplus scott/tiger
    @utlchain.sql
  2. Analyse all or only your Tables

    select 'analyze table '||table_name||' list chained rows into CHAINED_ROWS;'
    from user_tables
    /


    analyze table ROW_MIG_CHAIN_DEMO list chained rows into CHAINED_ROWS;
    analyze table DEPT list chained rows into CHAINED_ROWS;
    analyze table EMP list chained rows into CHAINED_ROWS;
    analyze table BONUS list chained rows into CHAINED_ROWS;
    analyze table SALGRADE list chained rows into CHAINED_ROWS;
    analyze table DUMMY list chained rows into CHAINED_ROWS;

    Table analyzed.
  3. Show the RowIDs for all chained rows

    This will allow you to quickly see how much of a problem chaining is in each table. If chaining is prevalent in a table, then that table should be rebuild with a higher value for PCTFREE

    select owner_name,
    table_name,
    count(head_rowid) row_count
    from chained_rows
    group by owner_name,table_name
    /


    OWNER_NAME TABLE_NAME ROW_COUNT
    ------------------------------ ------------------------------ ----------
    SCOTT ROW_MIG_CHAIN_DEMO 1

Conclusion

Migrated rows affect OLTP systems which use indexed reads to read singleton rows. In the worst case, you can add an extra I/O to all reads which would be really bad. Truly chained rows affect index reads and full table scans.

  • Row migration is typically caused by UPDATE operation

  • Row chaining is typically caused by INSERT operation.

  • SQL statements which are creating/querying these chained/migrated rows will degrade the performance due to more I/O work.

  • To diagnose chained/migrated rows use ANALYZE command , query V$SYSSTAT view

  • To remove chained/migrated rows use higher PCTFREE using ALTER TABLE MOVE.



Oracle中,一个Delete操作的流程

oracle admin — 作者 littledan @ 10:06

Oracle中,一个Delete操作的流程

删除(DELETE)

1.Oracle读Block到Buffer Cache(如果该Block在Buffer中不存在)
2.在redo log buffer中记录delete操作的细节
3.在相应回滚段段头的事务表中创建一个undo条目
4.把将要删除的记录创建前镜像,存放到Undo Block中
5.在Buffer Cache中的相应数据块上删除记录,并且标记相应的数据块为Dirty


提交(COMMIT)
1.Oracle产生一个SCN
2.在回滚段事务表中标记该事务状态为commited
3.LGWR Flush Log Buffer到日志文件
3.如果此时数据块仍然在Buffer Cache中,那么SCN将被记录到Block Header上,这被称为快速提交(fast commit)
4.如果dirty block已经被写回到磁盘,那么下一个访问这个block的进程将会自回滚段中获取该事务的状态,确认该事务被提交。然后这个进程获得提交SCN并写回到Block Header上。这被称为延迟块清除(delayed block cleanout)。



转 Parsing in Oracle

oracle admin — 作者 littledan @ 18:27

Parsing in Oracle
Amar Kumar Padhi, amar_padhi@hotmail.com <mailto:amar_padhi@hotmail.com>

Whenever a statement is executed, Oracle follows a methodology to evaluate the statement in terms of syntax, validity of objects being referred and of course, privileges to the user. Apart from this, Oracle also checks for identical statements that may have been fired, with the intention of reducing processing overheads. All this takes place in a fraction of a second, even less, without the user knowing what is happening to the statement that was fired. This process is known as Parsing.


Types of Parsing


All statements, DDL or DML, are parsed whenever they are executed. The only key fact is that whether it was a Soft (statement is already parsed and available in memory) or a Hard (all parsing steps to be carried out) parse. Soft parse will considerably improve the system performance where as frequent Hard parsing will affect the system. Reducing Hard parsing will improve the resource utilization and optimize the SQL code.


Parsing process


Oracle internally does the following to arrive at the output of an SQL statement.

1. Syntactical check. The query fired is checked for its syntax.

2. Semantic check. Checks on the validity of the objects being referred in the statement and the privileges available to the user firing the statement. This is a data dictionary check.

3. Allocation of private SQL area in the memory for the statement.

4. Generating a parsed representation of the statement and allocating Shared SQL area. This involves finding an optimal execution path for the statement.

In point four, Oracle first checks if the same statement is already parsed and existing in the memory. If found, the parsed representation will be picked up and the statement executed immediately (Soft parse). If not found, then the parsed representation is generated and stored in a shared SQL area (Part of shared pool memory in SGA), the statement is then executed (Hard parse). This step involves the optimization of the statement, the one that decides the performance.


Identical statements


Oracle does the following to find identical statements to decide on a soft or a hard parse.

a. When a new statement is fired, a hash value is generated for the text string. Oracle checks if this new hash value matches with any existing hash value in the shared pool.

b. Next, the text string of the new statement is compared with the hash value matching statements. This includes comparison of case, blanks and comments present in the statements.

c. If a match is found, the objects referred in the new statement are compared with the matching statement objects. Tables of the same name belonging to different a schema will not account for a match.

d. The bind variable types of the new statement should be of same type as the identified matching statement.

e. If all of the above is satisfied, Oracle re-uses the existing parse (soft). If a match is not found, Oracle goes through the process of parsing the statement and putting it in the shared pool (hard).


Reduce hard parsing


The shared pool memory can be increased when contention occurs, but more important is that such issues should be addressed at the coding level. Following are some initiatives that can be taken to reduce hard parsing.

1. Make use of bind variables rather than hard-coding values in your statements.

2. Write generic routines that can be called from different places. This will also eliminate code repetition.

3. Even with stringent checks, it may so happen that same statements are written in different formats. Search the SQL area periodically to check on similar queries that are being parsed separately. Change these statements to be look-alike or put them in a common routine so that a single parse can take care of all calls to the statement.

Identifying unnecessary parse calls at system level

select parse_calls, executions,
substr(sql_text, 1, 300)
from v$sqlarea
where command_type in (2, 3, 6, 7);


Check for statements with a lot of executions. It is bad to have the PARSE_CALLS value in the above statement close to the EXECUTIONS value. The above query will fire only for DML statements (to check on other types of statements use the appropriate command type number). Also ignore Recursive calls (dictionary access), as it is internal to Oracle.

Identifying unnecessary parse calls at session level

select b.sid, a.name, b.value
from v$sesstat b, v$statname a
where a.name in ('parse count (hard)', 'execute count')
and b.statistic# = a.statistic#
order by sid;


Identify the sessions involved with a lot of re-parsing (VALUE column). Query these sessions from V$SESSION and then locate the program that is being executed, resulting in so much parsing.

select a.parse_calls, a.executions, substr(a.sql_text, 1, 300)
from v$sqlarea a, v$session b
where b.schema# = a.parsing_schema_id
and b.sid = <:sid>
order by 1 desc;


The above query will also show recursive SQL being fired internally by Oracle.

4. Provide enough private SQL area to accommodate all of the SQL statements for a session. Depending on the requirement, the parameter OPEN_CURSORS may need to be reset to a higher value. Set the SESSION_CACHED_CURSORS to a higher value to allow more cursors to be cached at session level and to avoid re-parsing.

Identify how many cursors are being opened by sessions

select a.username, a.sid, b.value
from v$session a, v$sesstat b, v$statname c
where b.sid = a.sid
and c.statistic# = b.statistic#
and c.name = 'opened cursors current'
order by 3 desc;


The VALUE column will identify how many cursors are open for a session and how near the count is to the OPEN_CURSORS parameter value. If the margin is very small, consider increasing the OPEN_CURSORS parameter.

Evaluate cached cursors for sessions as compared to parsing

select a.sid, a.value parse_cnt,
(select x.value
from v$sesstat x, v$statname y
where x.sid = a.sid
and y.statistic# = x.statistic#
and y.name = 'session cursor cache hits') cache_cnt
from v$sesstat a, v$statname b
where b.statistic# = a.statistic#
and b.name = 'parse count (total)'
and value > 0;


The CACHE_CNT ('session cursor cache hits') of a session should be compared to the PARSE_CNT ('parse count (total)'), if the difference is high, consider increasing the SESSION_CACHED_CURSORS parameter.

The following parse related information is available in V$SYSSTAT and V$SESSTAT views, connect with V$STATNAME using STATISTIC# column.

SQL> select * from v$statname where name like '%parse%';

STATISTIC# NAME CLASS
---------- ------------------------- ----------
217 parse time cpu 64
218 parse time elapsed 64
219 parse count (total) 64
220 parse count (hard) 64
221 parse count (failures) 64


5. Shared SQL area may be further utilized for not only identical but also for some-what similar queries by setting the initialization parameter CURSOR_SHARING to FORCE. The default value is EXACT. Do not use this parameter in Oracle 8i, as there is a bug involved with it that hangs similar query sessions because of some internal processing. If you are on 9i, try out this parameter for your application in test mode before making changes in production.

6. Prevent large SQL or PL/SQL areas from ageing out of the shared pool memory. Ageing out takes place based on Least recently used (LRU) mechanism. Set the parameter SHARED_POOL_RESERVED_SIZE to a larger value to prevent large packages from being aged out because of new entries. A large overhead is involved in reloading a large package that was aged out.

7. Pin frequent objects in memory using the DBMS_SHARED_POOL package. This package is created by default. It can also be created explicitly by running DBMSPOOL.SQL script; this internally calls PRVTPOOL.PLB script. Use it to pin most frequently used objects that should be in memory while the instance is up, these would include procedure (p), functions (p), packages (p) and triggers (r). Pin objects when the instance starts to avoid memory fragmentation (Even frequently used data can be pinned but this is a separate topic).

To view a list of frequently used and re-loaded objects

select loads, executions, substr(owner, 1, 15) "Owner",
substr(namespace, 1, 20) "Type", substr(name, 1, 100) "Text"
from v$db_object_cache
order by executions desc;


To pin a package in memory

SQL>exec dbms_shared_pool.keep('standard', 'p');

To view a list of pinned objects

select substr(owner, 1, 15) "Owner",
substr(namespace, 1, 20) "Type",
substr(name, 1, 100) "Text"
from v$db_object_cache
where kept = 'YES';


8. Increasing the shared pool size is an immediate solution, but the above steps need to be carried out to optimize the database in the long run. The size of the shared pool can be increased by setting the parameter SHARED_POOL_SIZE in the initialization file.

Conclusion

Reduce Hard parsing as much as possible! This can be done by writing generic routines that can be called from different parts of the application, thus the importance of writing uniform and generic code.



一条语句被解析parse的过程

oracle admin — 作者 littledan @ 18:26

一条语句被解析parse的过程


2006-11-22 00:35:42 / 个人分类:administration <http://blog.oracle.com.cn/47810/spacelist_type_blog_itemtypeid_144.html>

SQL <javascript:;> Parsing Flow Diagram ========================

Statement
Submitted
|
Is it in an open cursor?--------------YES----V
| |
NO |
| |
Is SESSION_CACHED_CURSORS = Value |
and cursor in --------------YES----V In these 3 cases we
Session Cursor cache? | know that the cursor has
| | already been parsed, so
NO | re-parsing is
| | unnecessary.
Is HOLD_CURSOR=Y |
and cursor in --------------YES----V
Held cursor cache? |
| |
NO |
| | ^
OPEN A CURSOR | CLIENT SIDE |
| | -------------|
Statement is Hashed and compared | SERVER SIDE |
with the Hashed value in the sql area | V
| V
Is it in sql area? --YES-(Soft Parse)--> ---------
| | |
NO | EXECUTE |
| | |
PARSE STATEMENT ('Hard' Parse)---------> ---------


NOTES
=====

1. A cursor is an address on the client that points to the memory
location of a SQL statement on the server.
Multiple-client cursors may point at the same address on the server.

2. Remember that 'Client' and 'Server' sides may reside on the same
machine - in which case Client/Server is a logical distinction.

3. If a cursor is open, then the statement will be in the sql_area,
so no parsing is necessary.

This is why locks may remain when a client is terminated
abnormally (such as a PC Client being turned off without
closing open cursors).

4. SESSION_CACHED_CURSORS is the initialisation parameter that
specifies how many cursors to hold open for a particular session.

5. HOLD_CURSOR is an precompiler parameter that specifies that an
individual cursor should be held open.

See Page 11-3 of the Programmer's guide to the Oracle <javascript:;> Precompilers.

6. Both the soft and hard parse register as a parse in tkprof. Hashing the
current statement updates the parse count.

7. Soft parse avoids many of the steps taken during the parse phase for a
particular statement. Initial syntactic and semantic checks are made and
then the statement is hashed and compared with hashed statements in
the SQL area. If a match is found, then existing information is used
and relatively expensive steps (such as query optimization etc.) are avoided.

8. The 10053 event is only invoked during a hard parse.

From:Note:32895.1



转 Oracle's buffer cache

oracle admin — 作者 littledan @ 18:24

Oracle's buffer cache

The buffer cache is part of the SGA <http://www.adp-gmbh.ch/ora/concepts/sga.html#db_buffer_cache> . It holds copies of data blocks <http://www.adp-gmbh.ch/ora/concepts/db_block.html> so as they can be accessed quicker by oracle than by reading them off disk.

Purpose

The purpose of the buffer cache is to minimize physical io <http://www.adp-gmbh.ch/ora/tuning/logical_vs_physical_io.html#physical_io> . When a block <http://www.adp-gmbh.ch/ora/concepts/db_block.html> is read by Oracle, it places this block into the buffer cache, because there is a chance that this block is needed again. Reading a block from the buffer cache is less costly (in terms of time) than reading it from the disk.

Segments

The database buffer cache (as well as the shared sql cache are logically segmented into multiple sets. This organization reduces contention on multiprocessor systems.

Buffer

The buffer cache consists of... buffers. A buffer is a database block <http://www.adp-gmbh.ch/ora/concepts/db_block.html> that happens to be in memory.,

MRU and LRU blocks

Blocks within the buffer cache are ordered from MRU (most recently used) blocks to LRU (least recently used) blocks. Whenever a block is accessed, the block goes to the MRU end of the list, thereby shifting the other blocks down towards the LRU end. When a block is read from disk and when there is no buffer available in the db buffer cache, one block in the buffer cache has to "leave". It will be the block on the LRU end in the list.
However, blocks read during a full table (multi block reads <http://www.adp-gmbh.ch/ora/tuning/logical_vs_physical_io.html#multiple_blocks> are placed on the LRU side of the list instead of on the MRU side.
The time a block has been touched most recently is recorded in tim of x$bh <http://www.adp-gmbh.ch/ora/misc/x.html#bh>

Different pools within the cache

The cache consists actually of three buffer pools for different purposes.

Keep pool

The keep pool's purpose is to take small objects <http://www.adp-gmbh.ch/ora/admin/objects.html> that should always be cached, for example Look Up Tables.
See db_keep_cache_size <http://www.adp-gmbh.ch/ora/admin/init_params/sga.html#db_keep_cache_size> .

Recycle pool

The recycle pool is for larger objects <http://www.adp-gmbh.ch/ora/admin/objects.html> .
See db_recycle_cache_size <http://www.adp-gmbh.ch/ora/admin/init_params/sga.html#db_recycle_cache_size> .

Default pool

The default pool is for everything else.
See also x$kcbwbpd <http://www.adp-gmbh.ch/ora/misc/x.html#kcbwbpd>

Cold and hot area

Each pool's LRU is divided into a hot area and a cold area. Accordingly, buffers with in the hot area are hot buffers (and buffers in the cold are are called cold buffers).
By default, 50% of the buffers belong to the cold area and the other 50% belong to the hot area. This behaviour can be changed with _db_percent_hot_default <http://www.adp-gmbh.ch/ora/misc/init_params.html#db_percent_hot_default> (for the default pool) _db_percent_hot_recycle <http://www.adp-gmbh.ch/ora/misc/init_params.html#db_percent_hot_recycle> (for the recycle pool) and _db_percent_hot_keep <http://www.adp-gmbh.ch/ora/misc/init_params.html#db_percent_hot_keep> (for the keep pool).
A newly read db block will be inserted between the cold and the hot area such that it belongs to the hot area. This is called midpoint insertion. However, this is only true for single block reads <http://www.adp-gmbh.ch/ora/tuning/logical_vs_physical_io.html#single_block> , multi block reads <http://www.adp-gmbh.ch/ora/tuning/logical_vs_physical_io.html#multiple_blocks> will be placed at the LRU end.

Touch count

Each buffer has an associated touch count. This touch count might be increased if a buffer is accessed (although it needs not always be). It is valid to claim that the higher the touch count, the more important (more used) the buffer. Therefore, buffers with a high touch count should stay in the buffer cache while buffers with a low touch count should age out in order to make room for other buffers. A touch time can only be increased once within a time period controlled by the parameter _db_aging_touch_time <http://www.adp-gmbh.ch/ora/misc/init_params.html#db_aging_touch_time> (default: 3 seconds).
The touch count is recorded in the tch column of x$bh <http://www.adp-gmbh.ch/ora/misc/x.html#bh_tch> .
By the way, it looks like Oracle doesn't protect manipulations of the touch count in a buffer with a latch <http://www.adp-gmbh.ch/ora/concepts/latches.html> . This is interesting because all other manipulations on the LRU list are protected by latches. A side effect of the lack of latch-protection is that the touch count is not incremented if another process <http://www.adp-gmbh.ch/ora/concepts/processes/index.html> updates the buffer header.

Flushing the cache

With Oracle 10g <http://www.adp-gmbh.ch/ora/misc/10g.html> it is possible to flush the buffer cache with alter system flush buffer_cache <http://www.adp-gmbh.ch/ora/sql/alter_system.html#flush_buffer_cache> .

Optimal Size

Some common wisdom says that the larger the buffer cache is, the better the performance of the database becomes. However, this claim is not always true.
To begin with, the cache needs to be managed. The bigger the cache, the larger the LRU and dirty list becomes. That results in longer search times for a free buffer (buffer busy waits <http://www.adp-gmbh.ch/ora/tuning/event.html#free_buffer_waits> .
Also, the bigger the cache, the greater the burden on the DBWn <http://www.adp-gmbh.ch/ora/concepts/processes/dbwr.html> process.

v$bh

The v$bh <http://www.adp-gmbh.ch/ora/misc/dynamic_performance_views.html#bh> dynamic view has an entry for each block in the buffer cache.



转: Latch 系列

oracle test — 作者 littledan @ 18:23

Latch 系列一: Introducion <http://www.oracledba.com.cn/blog/?p=26>


Posted in Oracle <http://www.oracledba.com.cn/blog/?cat=1> , Brotherxiao's <http://www.oracledba.com.cn/blog/?cat=18> at 3:55 pm by bachelor

什么是latch?
latch是一种低级别的序列化的内存锁,用于保护SGA中的共享内存结构.
(1)确保内核代码序列执行;
(2)防止因缓存区崩溃导致的物理块的坏块
任何内核代码在执行前,必须获取latch,执行完后释放latch.
latch通常通过两种方式来获取:immediate和willing to wait.
当进程以immeidate方式请求latch失败时,控制权将转会到进程并产生一次immediate miss;当进程以willing to wait方式请求latch时将以”spinning and sleeping”的循环方式重复请求获取latch,spin一次如果仍然不能得到latch则sleep一定间隔(间隔时间成指数级增长)直至获得latch或达到spin_count次数(此时产生一次miss)

(写的好累哟... 慢慢再来补,先copy一点过来)
常见的两种latch: cache buffer chains latch 和library cache诊断

1.Cache buffers chains latch:
This latch is acquired whenever a block in the buffer cache is accessed (pinned). Reducing contention for the cache buffer chains latch will usually require reducing logical I/O rates by tuning and minimizing the I/O requirements of the SQL involved. High I/O rates could be a sign of a hot block (meaning a block highly accessed). See How To Identify a Hot Block Within The Database to correctly identify this issue

Data buffer chains—热点块
select CHILD# “cCHILD”
, ADDR “sADDR”
, GETS “sGETS”
, MISSES “sMISSES”
, SLEEPS “sSLEEPS”
from v$latch_children
where name = ‘cache buffers chains’
order by 4, 1, 2, 3;

select /*+ RULE */
e.owner ||’.’|| e.segment_name segment_name,
e.extent_id extent#,
x.dbablk – e.block_id + 1 block#,
x.tch,
l.child#
from
sys.v$latch_children l,
sys.x$bh x,
sys.dba_extents e
where
x.hladdr = sADDR’ and
e.file_id = x.file# and
x.hladdr = l.addr and
x.dbablk between e.block_id and e.block_id + e.blocks -1
order by x.tch desc ;

2.library cache latch的诊断
查看latch信息:
select name,gets,misses,sleeps
from v$latch
where name like ‘library%’;
查看latch操作系统进程号
select a.name,pid from v$latch a , V$latchholder b
where a.addr=b.laddr
and a.name = ‘library cache%’;
2.查看
select count(*) number_of_waiters
from v$session_wait w, v$latch l
where w.wait_time = 0
and w.event = ‘latch free’
and w.p2 = l.latch#
and l.name like ‘library%’;

select * from v$session_wait
where event != ‘client message’
and event not like ‘%NET%’
and wait_time = 0
and sid > 5;

3.解决方法

i) FRAGMENTATION

The primary cause of library cache latch contention is fragmentation of the shared pool. A common symptom is the ORA-04031 error. This can be diagnosed and addressed. Please refer to to have detailed information on this topic.

ii) INCREASE SHARING

By increasing the amount of sharing that occurs on the system you can decrease the amount of missing and loading that occurs in the library cache and therefore the load on the library cache latch. This is done by identifying statements that are not being shared as described in the fragmentation section above. To determine the percentage of sql statement parse calls that find a cursor to share you can execute the following:

select gethitratio from v$librarycache where namespace = ‘SQL AREA’;

This value should be in the high nineties.

iii) REDUCE PARSING

Any time a SQL statement is executed a PARSE stage must be executed. When a PARSING representation can be reused because is already loaded into the shared pool a “soft parse” is issued. When is required to compile the statement and create the parse representation a “hard parse” is issued. To reduce library cache latch contention you will need to monitor and reduce “hard parse” To identify the SQL statements that are receiving a lot of parse calls execute the following query:

select sql_text, parse_calls, executions from v$sqlarea
where parse_calls > 100 and executions < 2*parse_calls;

To identify the total amount of parsing going on in the system execute the
following:

Oracle7:
select name, value from v$sysstat where name = ‘parse count’;

NAME VALUE————————————————————————————parse count &nbs ; 220267

Oracle8: (Oracle8.x keep record of the “hard parse”)
select name, value from v$sysstat where name like ‘parse count%’;

NAME VALUE———————————————————————-—————parse count (total) 220267 parse count (hard) 90345

If this value increases at a rate greater than about 10 per second then this may be a problem.

iv) CURSOR_SPACE_FOR_TIME

Setting the init.ora parameter cursor_space_for_time to TRUE can reduce the load on the library cache latch somewhat. However, setting this parameter may add a lot of memory utilization, so before setting it to true make sure that there is a lot of free memory on the system and that the number of hard page faults per minute is very low or zero.
v) SESSION_CACHED_CURSORS
session_cached_cursors can be set that will help in situations where a user repeatedly parses the same statements. This can occur in many applications including FORMS based application if users often switch between forms. Every time a user switches to a new form all the SQL statements opened for the old form will be closed. The session_cached_cursors parameter will cause closed cursors to be cached within the session so that a subsequent call to parse the statement will bypass the parse phase. This is similar to HOLD_CURSORS in the precompilers. One thing to be careful about is that if this parameter is set to a high value, the amount of fragmentation in the shared pool may be increased.
vi) USING FULLY QUALIFIED TABLE NAMES
It can help to reduce the load on the library cache latch somewhat to use fully qualified names for tables in SQL statements. That is, instead of saying ‘select * from emp’, say ‘select * from scott.emp’. This is especially helpful for SQL statements that are parsed very frequently. If all users log onto the database using the same userid then this may be of little or no use.

Permalink <http://www.oracledba.com.cn/blog/?p=26>



转 Oracle's x$ Tables

oracle admin — 作者 littledan @ 18:22

Oracle's x$ Tables

See also: Speculation of X$ Table Names <http://www.stormloader.com/yonghuang/computer/x$table.html>
x$ tables are the sql interface to viewing oracle's memory in the SGA <http://www.adp-gmbh.ch/ora/concepts/sga.html> .
The names for the x$ tables can be queried with
select <http://www.adp-gmbh.ch/ora/sql/select.html> kqftanam from x$kqfta;

x$activeckpt


x$bh

Information on buffer headers.
Contains a record (the buffer header) for each block in the buffer cache <http://www.adp-gmbh.ch/ora/concepts/cache.html> .
This select statement lists how many blocks are Available, Free and Being Used.
select count(*), State from (
select decode <http://www.adp-gmbh.ch/ora/sql/decode.html> (state,
0, 'Free',
1, decode (lrba_seq,
0, 'Available',
'Being Used'),
3, 'Being Used',
state) State
from x$bh )
group by state
The meaning of state:
0 FREE no valid block image
1 XCUR a current mode block, exclusive to this instance
2 SCUR a current mode block, shared with other instances
3 CR a consistent read (stale) block image
4 READ buffer is reserved for a block being read from disk
5 MREC a block in media recovery mode
6 IREC a block in instance (crash) recovery mode
The meaning of tch: tch is the touch count. A high touch count indicates that the buffer is used often. Therefore, it will probably be at the head of the MRU list. See also touch count <http://www.adp-gmbh.ch/ora/concepts/cache.html#touch_count> .
The meaning of tim: touch time.
class represents a value designated for the use of the block.
lru_flag
set_ds maps to addr on x$kcbwds.
le_addr can be outer joined on x$le.le_addr.
flag is a bit array.
Bit if set
0 Block is dirty
4 temporary block
9 or 10 ping
14 stale
16 direct
524288 (=0x80000) Block was read in a full table scan <http://www.adp-gmbh.ch/ora/tuning/fulltablescan.html> See this link <http://www.adp-gmbh.ch/ora/tuning/fts_blocks_in_cache.html>

x$bufqm


x$class_stat


x$context


x$globalcontext


x$hofp


x$hs_session


The x$kc... tables


x$kcbbhs


x$kcbmmav


x$kcbsc


x$kcbwait


x$kcbwbpd

Buffer pool descriptor, the base table for v$buffer_pool <http://www.adp-gmbh.ch/ora/misc/dynamic_performance_views.html#buffer_pool> .
How is the buffer cache split between the default <http://www.adp-gmbh.ch/ora/concepts/cache.html#default_pool> , the recycle <http://www.adp-gmbh.ch/ora/concepts/cache.html#recycle_pool> and the keep <http://www.adp-gmbh.ch/ora/concepts/cache.html#keep_pool> buffer pool.

x$kcbwds

Set descriptor, see also x$kcbwbpd
The column id can be joined with v$buffer_pool.id <http://www.adp-gmbh.ch/ora/misc/dynamic_performance_views.html#buffer_pool> .
The column bbwait corresponds to the buffer busy waits <http://www.adp-gmbh.ch/ora/tuning/event.html#buffer_busy_wait> wait event.
Information on working set buffers
addr can be joined with x$bh.set_ds.
set_id will be between lo_setid and hi_setid in v$buffer_pool <http://www.adp-gmbh.ch/ora/misc/dynamic_performance_views.html#buffer_pool> for the relevant buffer pool.

x$kccal


x$kccbf


x$kccbi


x$kccbl


x$kccbp


x$kccbs


x$kcccc


x$kcccf


x$kccdc


x$kccdi


x$kccdl


x$kccfc


x$kccfe


x$kccfn


x$kccic


x$kccle

Controlfile logfile entry. Use
select max(lebsz) from x$kccle
to find out the size of a log block. The log block size is the unit for the following init params: log_checkpoint_interval <http://www.adp-gmbh.ch/ora/misc/init_params.html#log_checkpoint_interval> , _log_io_size <http://www.adp-gmbh.ch/ora/misc/init_params.html#log_io_size> , and max_dump_file_size <http://www.adp-gmbh.ch/ora/misc/init_params.html#max_dump_file_size> .

x$kcclh


x$kccor


x$kcccp

Checkpoint Progress:

The column cpodr_bno displays the current redo block number. Multiplied with the OS Block Size (usually 512), it returns the amount of bytes of redo currently written to the redo logs. Hence, this number is reset at each log switch <http://www.adp-gmbh.ch/ora/concepts/redo_log_switch.html> .
k$kcccp can (together with x$kccle) be used to monitor the progress of the writing of online redo logs <http://www.adp-gmbh.ch/ora/concepts/online_redo_logs.html> . The following query does this.
select
le.leseq "Current log sequence No",
100*cp.cpodr_bno/le.lesiz "Percent Full",
cp.cpodr_bno "Current Block No",
le.lesiz "Size of Log in Blocks"
from
x$kcccp cp,
x$kccle le
where
LE.leseq =CP.cpodr_seq
and bitand(le.leflg,24)=8;
bitand(le.leflg,24)=8 makes sure we get the current log group
How much redo is written by Oracle uses a variation of this SQL statement to track how much redo is written by different DML Statements.

x$kccrs


x$kccrt


x$kccsl


x$kcctf


x$kccts


x$kcfio


x$kcftio


x$kckce


x$kckty


x$kclcrst


x$kcrfx


x$kcrmf


x$kcrmx


x$kcrralg


x$kcrrarch


x$kcrrdest


x$kcrrdstat


x$kcrrms


x$kcvfh


x$kcvfhmrr


x$kcvfhonl


x$kcvfhtmp


x$kdnssf


The x$kg... tables

KG stands for kernel generic

x$kghlu

This view shows one row per shared pool <http://www.adp-gmbh.ch/ora/concepts/shared_pool.html> area. If there's a java pool, an additional row is displayed.

x$kgicc


x$kgics


x$kglcursor


x$kgldp


x$kgllk

This table lists all held and requested library object locks for all sessions. It is more complete than v$lock <http://www.adp-gmbh.ch/ora/misc/dynamic_performance_views.html#lock> .
The column kglnaobj displays the first 80 characters of the name of the object.
select
kglnaobj, kgllkreq
from
x$kgllk x join v$session s on
s.saddr = x.kgllkses;
kgllkreq = 0 means, the lock is held, while kgllkreq > 0 means that the lock is requested.

x$kglmem


x$kglna


x$kglna1


x$kglob

Library Cache Object

x$kglsim


x$kglst


x$kgskasp


x$kgskcft


x$kgskcp


x$kgskdopp


x$kgskpft


x$kgskpp


x$kgskquep


x$kjbl


x$kjbr


x$kjdrhv


x$kjdrpcmhv


x$kjdrpcmpf


x$kjicvt


x$kjilkft


x$kjirft


x$kjisft


x$kjitrft


x$kksbv


x$kkscs


x$kkssrd


x$klcie


x$klpt


x$kmcqs


x$kmcvc


x$kmmdi


x$kmmrd


x$kmmsg


x$kmmsi


x$knstacr


x$knstasl


x$knstcap


x$knstmvr


x$knstrpp


x$knstrqu


x$kocst


The x$kq... tables


x$kqfco

This table has an entry for each column of the x$tables and can be joined with x$kqfta.
The column kqfcosiz indicates the size (in bytes?) of the columns.
select
t.kqftanam "Table Name",
c.kqfconam "Column Name",
c.kqfcosiz "Column Size"
from
x$kqfta t,
x$kqfco c
where
t.indx = c.kqfcotab

x$kqfdt


x$kqfsz


x$kqfta

It seems that all x$table names can be retrieved with the following query.
select kqftanam from x$kqfta;
This table can be joined with x$kqfco which contains the columns for the tables:
select
t.kqftanam "Table Name",
c.kqfconam "Column Name"
from
x$kqfta t,
x$kqfco c
where
t.indx = c.kqfcotab

x$kqfvi


x$kqfvt


x$kqlfxpl


x$kqlset


x$kqrfp


x$kqrfs


x$kqrst


x$krvslv


x$krvslvs


x$krvxsv


The x$ks... tables

KS stands for kernel services.

x$ksbdd


x$ksbdp


x$ksfhdvnt


x$ksfmcompl


x$ksfmelem


x$ksfmextelem


x$ksfmfile


x$ksfmfileext


x$ksfmiost


x$ksfmlib


x$ksfmsubelem


x$ksfqp


x$ksimsi


x$ksled


x$kslei


x$ksles


x$kslld


x$ksllt


x$ksllw


x$kslwsc


x$ksmfs


x$ksmfsv

This SGA map.

x$ksmge


x$ksmgop


x$ksmgsc


x$ksmgst


x$ksmgv


x$ksmhp


x$ksmjch


x$ksmjs


x$ksmlru

Memory least recently used
Whenever a select is performed on x$ksmlru, its content is reset!
This table show which memory allocations in the shared pool <http://www.adp-gmbh.ch/ora/concepts/shared_pool.html> caused the throw out of the biggest memory chunks since it was last queried.

x$ksmls


x$ksmmem

This 'table' seems to allow to address (that is read (write????)) every byte in the SGA <http://www.adp-gmbh.ch/ora/concepts/sga.html> . Since the size of the SGA equals the size of select sum(value) from v$sga, the following query must return 0 (at least on a four byte architecture. Don't know about 8 bytes.)
select
(select sum(value) from v$sga <http://www.adp-gmbh.ch/ora/misc/dynamic_performance_views.html#sga> ) -
(select 4*count(*) from x$ksmmem) "Must be Zero!"
from
dual;

x$ksmsd


x$ksmsp


x$ksmsp_nwex


x$ksmspr


x$ksmss


x$ksolsfts


x$ksolsstat


x$ksppcv


x$ksppcv2

Contains the value kspftctxvl for each parameter found in x$ksppi. Determine if this value is the default value with the column kspftctxdf.

x$ksppi

This table contains a record for all documented and undocumented (starting with an underscore) parameters. select ksppinm from x$ksppi to show the names of all parameters. Join indx+1 with x$ksppcv2.kspftctxpn.

x$ksppo


x$ksppsv


x$ksppsv2


x$kspspfile


x$ksqeq


x$ksqrs


x$ksqst

Enqueue management statistics by type.
ksqstwat: The number of wait for the enqueue statistics class.
ksqstwtim: Cumulated waiting time. This column is selected when v$enqueue_stat.cum_wait_time <http://www.adp-gmbh.ch/ora/misc/dynamic_performance_views.html#enqueue_stat> is selected.
The types of classes are:
BL Buffer Cache Management
CF Controlfile <http://www.adp-gmbh.ch/ora/concepts/controlfiles.html> Transaction
CI Cross-instance call invocation
CU Bind Enqueue
DF Datafile <http://www.adp-gmbh.ch/ora/concepts/datafiles.html>
DL Direct Loader index creation
DM Database mount
DP ???
DR Distributed Recovery
DX Distributed TX
FB acquired when formatting a range of bitmap blocks far ASSM segments. id1=ts#, id2=relative dba
FS File Set
IN Instance number
IR Instance Recovery
IS Instance State
IV Library cache invalidation
JD Something to do with dbms_job <http://www.adp-gmbh.ch/ora/plsql/dbms_job.html>
JQ Job queue
KK Redo log kick
LA..LP Library cache <http://www.adp-gmbh.ch/ora/concepts/library_cache.html> lock
MD enqueue for Change data capture materialized view log (gotten internally for DDL on a snapshot log) id1=object# of the snapshot log.
MR Media recovery
NA..NZ Library cache <http://www.adp-gmbh.ch/ora/concepts/library_cache.html> pin
PF Password file
PI Parallel slaves
PR Process startup
PS Parallel slave synchronization
SC System commit number <http://www.adp-gmbh.ch/ora/concepts/scn.html>
SM SMON <http://www.adp-gmbh.ch/ora/concepts/processes/smon.html>
SQ Sequence <http://www.adp-gmbh.ch/ora/sql/create_sequence.html> number enqueue
SR Synchronized replication
SS Sort segment
ST Space management transaction
SV Sequence number value
SW Suspend writes enqueue gotten when someone issues alter system suspend|resume <http://www.adp-gmbh.ch/ora/sql/alter_system.html#suspend_resume>
TA Transaction recovery
UL User defined lock
UN User name
US Undo segment, serialization
WL Redo log being written
XA Instance attribute lock
XI Instance registration lock
XR Acquired for alter system quiesce restricted <http://www.adp-gmbh.ch/ora/sql/alter_system.html#quiesce_restricted>

x$kstex


x$ksull


x$ksulop


x$ksulv


x$ksumysta


x$ksupr


x$ksuprlat


x$ksurlmt


x$ksusd

Contains a record for all statistics <http://www.adp-gmbh.ch/ora/tuning/statistics.html> .

x$ksuse


x$ksusecon


x$ksusecst


x$ksusesta


x$ksusgif


x$ksusgsta


x$ksusio


x$ksutm


x$ksuxsinst


x$ktadm


x$targetrba


x$ktcxb

The SGA <http://www.adp-gmbh.ch/ora/concepts/sga.html> transaction table.

x$ktfbfe


x$ktfthc


x$ktftme


x$ktprxrs


x$ktprxrt


x$ktrso


x$ktsso


x$ktstfc


x$ktstssd


x$kttvs

Lists save undo for each tablespace: The column kttvstnm is the name of the tablespace <http://www.adp-gmbh.ch/ora/concepts/tablespaces.html> that has saved undo. The column is null otherwise.

x$kturd


x$ktuxe

Kernel transaction, undo transaction entry

x$kvis

Has (among others) a row containing the db block size:
select kvisval from x$kvis where kvistag = 'kcbbkl'

x$kvit


x$kwddef


x$kwqpd


x$kwqps


x$kxfpdp


x$kxfpns


x$kxfpsst


x$kxfpys


x$kxfqsrow


x$kxsbd


x$kxscc


x$kzrtpd


x$kzspr


x$kzsrt


x$le

Lock element: contains an entry for each PCM lock held for the buffer cache. x$le can be left outer joined to x$bh on le_addr.

x$le_stat


x$logmnr_callback


x$logmnr_contents


x$logmnr_dictionary


x$logmnr_logfile


x$logmnr_logs


x$logmnr_parameters


x$logmnr_process


x$logmnr_region


x$logmnr_session


x$logmnr_transaction


x$nls_parameters


x$option


x$prmsltyx


x$qesmmiwt


x$qesmmsga


x$quiesce


x$uganco


x$version


x$xsaggr


x$xsawso


x$xssinfo


A perlscript to find x$ tables

#!/usr/bin/perl -w

use strict;

open O, ("/appl/oracle/product/9.2.0.2/bin/oracle");
open F, (">x");

my $l;
my $p = ' ' x 40;
my %x;

while (read (O,$l,10000)) {
$l = $p.$l;

foreach ($l =~ /(x$w{3,})/g) {
$x{$_}++;
}

$p = substr ($l,-40);
}

foreach (sort keys %x) {
print F "$_n";
}
Obviously, it is also possible to extract those names through x$kqfta



statspack 必看的十项内容

oracle admin — 作者 littledan @ 18:20

虽然是转的,也得认真看看....

statspack 必看的十项内容

发表人:pentium | 发表时间: 2007年三月05日, 01:40

http://xsb.itpub.net/post/419/38713 <http://xsb.itpub.net/post/419/38713>

statspack 必看的十项内容


、负载间档(Load profile)
2、实例效率点击率(Instance efficiency hit ratios)
3、首要的5个等待事件(Top 5 wait events)
4、等待事件(Wait events)
5、闩锁等待
6、首要的SQL(Top sql)
7、实例活动(Instance activity)
8、文件I/O(File I/O)
9、内存分配(Memory allocation)
10、缓冲区等待(Buffer waits)

二、输出结果解释

1、报表头信息
数据库实例相关信息,包括数据库名称、ID、版本号及主机等信息

2、负载间档
该部分提供每秒和每个事物的统计信息,是监控系统吞吐量和负载变化的重要部分

Load Profile
说明:
Redo size:每秒产生的日志大小(单位字节),可标志数据变更频率, 数据库任务的繁重与否
Logical reads:平决每秒产生的逻辑读,单位是block
block changes:每秒block变化数量,数据库事物带来改变的块数量
Physical reads:平均每秒数据库从磁盘读取的block数
Physical writes:平均每秒数据库写磁盘的block数
User calls:每秒用户call次数
Parses: 每秒解析次数,近似反应每秒语句的执行次数, 软解析每秒超过300次意味着你的"应 用程序"效率不高,没有使用soft soft parse,调整session_cursor_cache
Hard parses:每秒产生的硬解析次数, 每秒超过100次,就可能说明你绑定使用的不好
Sorts:每秒产生的排序次数
Executes:每秒执行次数
Transactions:每秒产生的事务数,反映数据库任务繁重与否
Recursive Call %: 如果有很多PLSQL,那么他就会比较高
Rollback per transaction %:看回滚率是不是很高,因为回滚很耗资源 ,如果回滚率过高,可能说明你的数据库经历了太多的无效操作 ,过多的回滚可能还会带来Undo Block的竞争 该参数计算公式如下:
Round(User rollbacks / (user commits + user rollbacks) ,4)* 100%

3、实例命中率
该部分可以提前找出ORACLE潜在将要发生的性能问题,很重要
Instance Efficiency Percentages (Target 100%)
说明:
Buffer Nowait %:在缓冲区中获取Buffer的未等待比率, Buffer Nowait<99%说明,有可能是有热, 块(查找x$bh的 tch和v$latch_children的cache buffers chains)
Redo NoWait %:在Redo缓冲区获取Buffer的未等待比率
Buffer Hit %:数据块在数据缓冲区中得命中率,通常应在90%以上,否则,需要调整, 小于 95%,重要的参数,小于90%可能是要加db_cache_size,但是大量的非选择的索引也会造成该值很高(大量的db file sequential read)
In-memory Sort %:在内存中的排序率
Library Hit %:主要代表sql在共享区的命中率,通常在95%以上,否,需要要考虑加
大共享池,绑定变量,修改cursor_sharing等参数。
Soft Parse %:近似看作sql在共享区的命中率,小于<95%,需要考虑到绑定,如果低于80%,
那么就可能sql基本没有被重用
Execute to Parse %:sql语句解析后被重复执行的次数,如果过低,可以考虑设置
session_cached_cursors参数, 公式为100 * (1 - Parses/Executions) = Execute to Parse所以如果系统Parses > Executions,就可能出现该比率小于0的情况, 该值<0通常说明shared pool设置或效率存在问题造成反复解析,reparse可能较严重,或者可是同snapshot有关如果该值为负值或者极低,通常说明数据库性能存在问题
Latch Hit %: Latch Hit<99%,要确保>99%,否则存在严重的性能问题,比如绑定等会影响该参数
Parse CPU to Parse Elapsd %:解析实际运行事件/(解析实际运行时间+
Parse CPU to Parse Elapsd %:解析实际运行事件/(解析实际运行时间+解析中等待资源时间)越高越好
% Non-Parse CPU:查询实际运行时间/(查询实际运行时间+sql解析时间),太低表示解析消耗时间过多。100*(parse time cpu / parse time elapsed)= Parse CPU to Parse Elapsd %
如果一个经常访问的列上的索引被删除,可能会造成buffer hit 显著的下降
如果增加了索引,但是他影响了ORACLE正确的选择表连接时的驱动顺序,那么可能会导致buffer hit 显 著增高
如果你的命中率变化幅度很大,说明你要改变SQL模式

Quote:
Shared Pool Statistics Begin End ------ ------ Memory Usage %: 33.79 57.02 % SQL with executions>1: 62.62 73.24 % Memory for SQL w/exec>1: 64.55 78.72

Shared Pool相关统计数据
Memory Usage %:共享池内存使用率,应该稳定在70%-90%间,太小浪费内存,太大则内存不足。
% SQL with executions>1:执行次数大于1的sql比率,若太小可能是没有使用bind variables。
% Memory for SQL w/exec>1:也即是memory for sql with execution > 1:执行次数大于1的sql
消耗内存/所有sql消耗的内存

4、首要等待事件
常见等待事件说明:
oracle等待事件是衡量oracle运行状况的重要依据及指示,主要有空闲等待事件和非空闲等待事件, TIMED_STATISTICS = TRUE 那么等待事件按等待的时间排序= FALSE那么事件按等待的数量排序.运行statspack期间必须session上设置TIMED_STATISTICS = TRUE.
空闲等待事件是oracle正等待某种工作,在诊断和优化数据库时候,不用过多注意这部分事件,
非空闲等待事件专门针对oracle的活动,指数据库任务或应用程序运行过程中发生的等待,这些等待事件是我们在调整数据库应该关注的。
比较影响性能常见等待事件
db file scattered read
该事件通常与全表扫描有关。因为全表扫描是被放入内存中进行的进行的,
通常情况下它不可能被放入连续的缓冲区中,所以就散布在缓冲区的缓存中。该指数的数量过大说明缺少索引或者限制了索引的使用(也可以调整optimizer_index_cost_adj) 。这种情况也可能是正常的,因为执行全表扫描可能比索引扫描效率更高。当系统存在这些等待时,需要通过检查来确定全表扫描是否必需的来调整。如果经常必须进行全表扫描,而且表比较小, 把该表存人keep池.如果是大表经常进行全表扫描,那么应该是olap系统,而不是oltp的.
db file sequential read
该事件说明在单个数据块上大量等待,该值过高通常是由于表间连接顺序很糟糕,或者使用了非选择性索引。通过将这种等待与statspack报表中已知其它问题联系起来(如效率不高的sql),通过检查确保索引扫描是必须的,并确保多表连接的连接顺序来调整, DB_CACHE_SIZE可以决定该事件出现的频率
buffer busy wait
当缓冲区以一种非共享方式或者如正在被读入到缓冲时,就会出现该等待.该值不应该大于1%,确认是不是由于热点块造成(如果是可以用反转索引,或者用更小块大小)
latch free
常跟应用没有很好的应用绑定有关. 闩锁是底层的队列机制(更加准确的名称应该是互斥机制),用于保护系统全局区(SGA)共享内存结构闩锁用于防止对内存结构的并行访问。如果闩锁不可用,就会记录一次闩锁丢失。绝大多数得闩锁问题都与使用绑定变量失败(库缓存闩锁)、生成重作问题(重执行分配闩锁)、缓存的争用问题(缓存LRU链) 以及缓存的热数据宽块(缓存链)有关。当闩锁丢失率高于0.5%时,需要调整这个问题。
log buffer space
日志缓冲区写的速度快于LGWR写REDOFILE的速度,可以增大日志文件大小,增加日志缓冲区的大小,或者使用更快的磁盘来写数据。
logfile switch
通常是因为归档速度不够快,需要增大重做日志
log file sync
当一个用户提交或回滚数据时,LGWR将会话得重做操作从日志缓冲区填充到日志文件中,用户的进程必须等待这个填充工作完成。在每次提交时都出现,如果这个等待事件影响到数据库性能,那么就需要修改应用程序的提交频率, 为减少这个等待事件,须一次提交更多记录,或者将重做日志REDO LOG 文件访在不同的物理磁盘上。
Enqueue 最有可能是多个用户同时修改同一个块,如果没有空闲的ITL空间,就会出现数据库块级锁.

TOP SQL
调整首要的25个缓冲区读操作和首要的25个磁盘读操作做的查询,将可对系统性能产生5%到5000%的增益.

Instance Activity Stats for DB: CRMTEMP Instance: crmtemp Snaps: 3 -11

Statistic Total per Second per Trans
--------------------------------- ------------------ -------------- ------------
CPU used by this session 291,318 98.1 13.0
CPU used when call started 291,318 98.1 13.0
CR blocks created 1,784 0.6 0.1
Cached Commit SCN referenced 0 0.0 0.0
Commit SCN cached 0 0.0 0.0
DBWR buffers scanned 985,112 331.6 44.0
DBWR checkpoint buffers written 948 0.3 0.0
DBWR checkpoints 0 0.0 0.0
dirty buffers inspected 483 0.2 0.0 --脏缓冲的个数
free buffer inspected 8,154 2.7 0.4 --如果数量很大,说明缓冲区过小
sorts (disk) 0 0.0 0.0 --不应当大于1-5%
sorts (memory) 15,365 5.2 0.7
sorts (rows) 1,445,018 823.0 109.2
summed dirty queue length 24,667 8.3 1.1



Powered by pLog