MySQL EXPLAIN

explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。

使用方法,在select语句前加上explain就可以了:

如:

  1. explain select surname,first_name form a,b where a.id=b.id

EXPLAIN列的解释:

table:显示这一行的数据是关于哪张表的

type:这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL

possible_keys:显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句

key: 实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引

key_len:使用的索引的长度。在不损失精确性的情况下,长度越短越好

ref:显示索引的哪一列被使用了,如果可能的话,是一个常数

rows:MYSQL认为必须检查的用来返回请求数据的行数

Extra:关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢

extra列返回的描述的意义

Distinct:一旦MYSQL找到了与行相联合匹配的行,就不再搜索了

Not exists: MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了

Range checked for each Record(index map:#):没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一

Using filesort: 看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行

Using index: 列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候

Using temporary 看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上

Where used 使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)

system 表只有一行:system表。这是const连接类型的特殊情况

const:表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待

eq_ref:在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用

ref:这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好

range:这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况

index: 这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)

ALL:这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免

mysql> explain select * from user where user=’root’\G

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: user

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 5

Extra: Using where

1 row in set (0.00 sec)

SHOW PROCESSLIST介绍和使用

processlist命令的输出结果显示了有哪些线程在运行,可以帮助识别出有问题的查询语句,两种方式使用这个命令。
1.      进入mysql/bin目录下输入mysqladmin processlist;
2.      启动mysql,输入show processlist;
如果有SUPER权限,则可以看到全部的线程,否则,只能看到自己发起的线程(这是指,当前对应的MySQL帐户运行的线程)。

mysql> show processlist

-> ;

+—-+——-+———————-+——+———+——+——-+——————+

| Id | User  | Host                 | db   | Command | Time | State | Info             |

+—-+——-+———————-+——+———+——+——-+——————+

| 34 | ucjmh | 192.168.56.101:54031 | NULL | Query   |    0 | NULL  | show processlist |

+—-+——-+———————-+——+———+——+——-+——————+

1 row in set (0.00 sec)

mysql> exit

Bye

[root@ucjmh ~]# mysql -uroot -poracle

Welcome to the MySQL monitor.  Commands end with ; or \g.

Your MySQL connection id is 35

Server version: 5.5.41-log Source distribution

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.

mysql> show processlist;

+—-+——+———–+——+———+——+——-+——————+

| Id | User | Host      | db   | Command | Time | State | Info             |

+—-+——+———–+——+———+——+——-+——————+

| 35 | root | localhost | NULL | Query   |    0 | NULL  | show processlist |

+—-+——+———–+——+———+——+——-+——————+

1 row in set (0.00 sec)

各列的含义和用途:

id,标识会话id 类似于oracle中的sid,你要kill一个语句的时候很有用。

user列,显示单前用户,如果不是root,这个命令就只显示你权限范围内的sql语句。

host列,显示这个语句是从哪个ip的哪个端口上发出的。用来追踪出问题语句的用户。

db列,显示这个进程目前连接的是哪个数据库。

command列,显示当前连接的执行的命令,一般就是休眠(sleep),查询(query),连接(connect)。

time列,此这个状态持续的时间,单位是秒。

state列,显示使用当前连接的sql语句的状态,很重要的列,后续会有所有的状态的描述,请注意,state只是语句执行中的某一个状态,一个sql语句,已查询为例,可能需要经过copying to tmp table,Sorting result,Sending data等状态才可以完成,

info列,显示这个sql语句,因为长度有限,所以长的sql语句就显示不全,但是一个判断问题语句的重要依据。Checking table
正在检查数据表(这是自动的)。
Closing tables
正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的操作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。
Connect Out
复制从服务器正在连接主服务器。
Copying to tmp table on disk
由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存。
Creating tmp table
正在创建临时表以存放部分查询结果。
deleting from main table
服务器正在执行多表删除中的第一部分,刚删除第一个表。
deleting from reference tables
服务器正在执行多表删除中的第二部分,正在删除其他表的记录。
Flushing tables
正在执行FLUSH TABLES,等待其他线程关闭数据表。
Killed
发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。
Locked
被其他查询锁住了。
Sending data
正在处理Select查询的记录,同时正在把结果发送给客户端。
Sorting for group
正在为GROUP BY做排序。
Sorting for order
正在为ORDER BY做排序。
Opening tables
这个过程应该会很快,除非受到其他因素的干扰。例如,在执Alter TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。
Removing duplicates
正在执行一个Select DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。
Reopen table
获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。
Repair by sorting
修复指令正在排序以创建索引。
Repair with keycache
修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。
Searching rows for update
正在讲符合条件的记录找出来以备更新。它必须在Update要修改相关的记录之前就完成了。
Sleeping
正在等待客户端发送新请求.
System lock
正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加–skip-external-locking参数来禁止外部系统锁。
Upgrading lock
Insert DELAYED正在尝试取得一个锁表以插入新记录。
Updating
正在搜索匹配的记录,并且修改它们。
User Lock
正在等待GET_LOCK()。
Waiting for tables
该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个表。以下几种情况下会产生这个通知:FLUSH TABLES tbl_name, Alter TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。
waiting for handler insert
Insert DELAYED已经处理完了所有待处理的插入操作,正在等待新的请求。
大部分状态对应很快的操作,只要有一个线程保持同一个状态好几秒钟,那么可能是有问题发生了,需要检查一下。
这个命令中最关键的就是state列,mysql列出的状态主要有以下几种:

Checking table
正在检查数据表(这是自动的)。
Closing tables
正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的操作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。
Connect Out
复制从服务器正在连接主服务器。
Copying to tmp table on disk
由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存。
Creating tmp table
正在创建临时表以存放部分查询结果。
deleting from main table
服务器正在执行多表删除中的第一部分,刚删除第一个表。
deleting from reference tables
服务器正在执行多表删除中的第二部分,正在删除其他表的记录。
Flushing tables
正在执行FLUSH TABLES,等待其他线程关闭数据表。
Killed
发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。
Locked
被其他查询锁住了。
Sending data
正在处理Select查询的记录,同时正在把结果发送给客户端。
Sorting for group
正在为GROUP BY做排序。
Sorting for order
正在为ORDER BY做排序。
Opening tables
这个过程应该会很快,除非受到其他因素的干扰。例如,在执Alter TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。
Removing duplicates
正在执行一个Select DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。
Reopen table
获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。
Repair by sorting
修复指令正在排序以创建索引。
Repair with keycache
修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。
Searching rows for update
正在讲符合条件的记录找出来以备更新。它必须在Update要修改相关的记录之前就完成了。
Sleeping
正在等待客户端发送新请求.
System lock
正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加–skip-external-locking参数来禁止外部系统锁。
Upgrading lock
Insert DELAYED正在尝试取得一个锁表以插入新记录。
Updating
正在搜索匹配的记录,并且修改它们。
User Lock
正在等待GET_LOCK()。
Waiting for tables
该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个表。以下几种情况下会产生这个通知:FLUSH TABLES tbl_name, Alter TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。
waiting for handler insert
Insert DELAYED已经处理完了所有待处理的插入操作,正在等待新的请求。
大部分状态对应很快的操作,只要有一个线程保持同一个状态好几秒钟,那么可能是有问题发生了,需要检查一下。

在mysql的官方文档中列出了所有可能的状态。

8.14.2 General Thread States

  •  After createThis occurs when the thread creates a table (including internal temporary tables), at the end of the function that creates the table. This state is used even if the table could not be created due to some error.
  •  AnalyzingThe thread is calculating a MyISAM table key distributions (for example, forANALYZE TABLE).
  •  checking permissionsThe thread is checking whether the server has the required privileges to execute the statement.
  •  Checking tableThe thread is performing a table check operation.
  •  cleaning upThe thread has processed one command and is preparing to free memory and reset certain state variables.
  •  closing tablesThe thread is flushing the changed table data to disk and closing the used tables. This should be a fast operation. If not, you should verify that you do not have a full disk and that the disk is not in very heavy use.
  •  converting HEAP to MyISAMThe thread is converting an internal temporary table from a MEMORY table to an on-disk MyISAM table.
  •  copy to tmp tableThe thread is processing an ALTER TABLE statement. This state occurs after the table with the new structure has been created but before rows are copied into it.
  •  Copying to group tableIf a statement has different ORDER BY and GROUP BY criteria, the rows are sorted by group and copied to a temporary table.
  •  Copying to tmp tableThe server is copying to a temporary table in memory.
  •  Copying to tmp table on diskThe server is copying to a temporary table on disk. The temporary result set has become too large (see Section 8.4.4, “How MySQL Uses Internal Temporary Tables”). Consequently, the thread is changing the temporary table from in-memory to disk-based format to save memory.
  •  Creating indexThe thread is processing ALTER TABLE ... ENABLE KEYS for a MyISAMtable.
  • Creating sort indexThe thread is processing a SELECT that is resolved using an internal temporary table.
  •  creating tableThe thread is creating a table. This includes creation of temporary tables.
  •  Creating tmp tableThe thread is creating a temporary table in memory or on disk. If the table is created in memory but later is converted to an on-disk table, the state during that operation will be Copying to tmp table on disk.
  •  deleting from main tableThe server is executing the first part of a multiple-table delete. It is deleting only from the first table, and saving columns and offsets to be used for deleting from the other (reference) tables.
  •  deleting from reference tablesThe server is executing the second part of a multiple-table delete and deleting the matched rows from the other tables.
  •  discard_or_import_tablespaceThe thread is processing an ALTER TABLE ... DISCARD TABLESPACE or ALTER TABLE ... IMPORT TABLESPACE statement.
  •  endThis occurs at the end but before the cleanup of ALTER TABLECREATE VIEWDELETEINSERTSELECT, or UPDATE statements.
  •  executingThe thread has begun executing a statement.
  •  Execution of init_commandThe thread is executing statements in the value of the init_command system variable.
  •  freeing itemsThe thread has executed a command. Some freeing of items done during this state involves the query cache. This state is usually followed by cleaning up.
  •  Flushing tablesThe thread is executing FLUSH TABLES and is waiting for all threads to close their tables.
  •  FULLTEXT initializationThe server is preparing to perform a natural-language full-text search.
  •  initThis occurs before the initialization of ALTER TABLEDELETEINSERT,SELECT, or UPDATE statements. Actions taken by the server in this state include flushing the binary log, the InnoDB log, and some query cache cleanup operations.

    For the end state, the following operations could be happening:

    • Removing query cache entries after data in a table is changed
    • Writing an event to the binary log
    • Freeing memory buffers, including for blobs
  •  KilledSomeone has sent a KILL statement to the thread and it should abort next time it checks the kill flag. The flag is checked in each major loop in MySQL, but in some cases it might still take a short time for the thread to die. If the thread is locked by some other thread, the kill takes effect as soon as the other thread releases its lock.
  •  LockedThe query is locked by another query.
  •  logging slow queryThe thread is writing a statement to the slow-query log.
  •  NULLThis state is used for the SHOW PROCESSLIST state.
  •  loginThe initial state for a connection thread until the client has been authenticated successfully.
  •  Opening tablesOpening tableThe thread is trying to open a table. This is should be very fast procedure, unless something prevents opening. For example, an ALTER TABLE or aLOCK TABLE statement can prevent opening a table until the statement is finished. It is also worth checking that your table_cache value is large enough.
  •  optimizingThe server is performing initial optimizations for a query.
  •  preparingThis state occurs during query optimization.
  •  Purging old relay logsThe thread is removing unneeded relay log files.
  •  query endThis state occurs after processing a query but before the freeing itemsstate.
  •  Reading from netThe server is reading a packet from the network.
  •  Removing duplicatesThe query was using SELECT DISTINCT in such a way that MySQL could not optimize away the distinct operation at an early stage. Because of this, MySQL requires an extra stage to remove all duplicated rows before sending the result to the client.
  •  removing tmp tableThe thread is removing an internal temporary table after processing a SELECT statement. This state is not used if no temporary table was created.
  •  renameThe thread is renaming a table.
  •  rename result tableThe thread is processing an ALTER TABLE statement, has created the new table, and is renaming it to replace the original table.
  •  Reopen tablesThe thread got a lock for the table, but noticed after getting the lock that the underlying table structure changed. It has freed the lock, closed the table, and is trying to reopen it.
  •  Repair by sortingThe repair code is using a sort to create indexes.
  •  Repair doneThe thread has completed a multi-threaded repair for a MyISAM table.
  •  Repair with keycacheThe repair code is using creating keys one by one through the key cache. This is much slower than Repair by sorting.
  •  Rolling backThe thread is rolling back a transaction.
  •  Saving stateFor MyISAM table operations such as repair or analysis, the thread is saving the new table state to the .MYI file header. State includes information such as number of rows, the AUTO_INCREMENT counter, and key distributions.
  •  Searching rows for updateThe thread is doing a first phase to find all matching rows before updating them. This has to be done if the UPDATE is changing the index that is used to find the involved rows.
  • Sending dataThe thread is reading and processing rows for a SELECT statement, and sending data to the client. Because operations occurring during this this state tend to perform large amounts of disk access (reads), it is often the longest-running state over the lifetime of a given query.
  •  setupThe thread is beginning an ALTER TABLE operation.
  •  Sorting for groupThe thread is doing a sort to satisfy a GROUP BY.
  •  Sorting for orderThe thread is doing a sort to satisfy a ORDER BY.
  •  Sorting indexThe thread is sorting index pages for more efficient access during a MyISAMtable optimization operation.
  •  Sorting resultFor a SELECT statement, this is similar to Creating sort index, but for nontemporary tables.
  •  statisticsThe server is calculating statistics to develop a query execution plan. If a thread is in this state for a long time, the server is probably disk-bound performing other work.
  •  System lockThe thread is going to request or is waiting for an internal or external system lock for the table. If this state is being caused by requests for external locks and you are not using multiple mysqld servers that are accessing the same MyISAM tables, you can disable external system locks with the --skip-external-locking option. However, external locking is disabled by default, so it is likely that this option will have no effect. For SHOW PROFILE, this state means the thread is requesting the lock (not waiting for it).
  •  Table lockThe next thread state after System lock. The thread has acquired an external lock and is going to request an internal table lock.
  •  updateThe thread is getting ready to start updating the table.
  •  UpdatingThe thread is searching for rows to update and is updating them.
  •  updating main tableThe server is executing the first part of a multiple-table update. It is updating only the first table, and saving columns and offsets to be used for updating the other (reference) tables.
  •  updating reference tablesThe server is executing the second part of a multiple-table update and updating the matched rows from the other tables.
  •  User lockThe thread is going to request or is waiting for an advisory lock requested with a GET_LOCK() call. For SHOW PROFILE, this state means the thread is requesting the lock (not waiting for it).
  •  Waiting for release of readlockThe thread is waiting for a global read lock obtained by another thread (withFLUSH TABLES WITH READ LOCK) to be released.
  •  Waiting for tablesWaiting for tableThe thread got a notification that the underlying structure for a table has changed and it needs to reopen the table to get the new structure. However, to reopen the table, it must wait until all other threads have closed the table in question.

    This notification takes place if another thread has used FLUSH TABLES or one of the following statements on the table in question: FLUSH TABLEStbl_nameALTER TABLERENAME TABLEREPAIR TABLEANALYZE TABLE, or OPTIMIZE TABLE.

  •  Waiting on condA generic state in which the thread is waiting for a condition to become true. No specific state information is available.
  •  Waiting to get readlockThe thread has issued a FLUSH TABLES WITH READ LOCK statement to obtain a global read lock and is waiting to obtain the lock.
  •  Writing to netThe server is writing a packet to the network.

[root@ucjmh ~]# cat kill_lock.sql
kill 43;
kill 55;
[root@ucjmh ~]# cat locked.txt
43 root localhost hi_db Query 3825 Waiting for table metadata lock insert into t values(4)
55 root localhost NULL Query 0 NULL show processlist
[root@ucjmh ~]# cat ger_lock.sh
#!/bin/bash
rm -rf locked.txt
rm -rf kill_lock.sql
mysql -uroot -poracle -e “show processlist”|grep -i “Query” >> locked.txt;
for line in $(awk ‘{printf $1″\n”}’ locked.txt)
do
echo “kill ${line};”>>kill_lock.sql
done

MySQL隐式转换

mysql> create table users(num int not null, id varchar(30) not null, password varchar(30) not null, primary key(num));

Query OK, 0 rows affected (0.00 sec)

mysql> insert into users values(1, ‘admin’, ‘ad1234’);

Query OK, 1 row affected (0.00 sec)

mysql> insert into users values(2, ‘wh1ant’, ‘wh1234’);

Query OK, 1 row affected (0.00 sec)

mysql> insert into users values(3, ‘secuholic’, ‘se1234’);

Query OK, 1 row affected (0.00 sec)

mysql> select * from users where id=0;

+—–+———–+———-+

| num | id        | password |

+—–+———–+———-+

|   1 | admin     | ad1234   |

|   2 | wh1ant    | wh1234   |

|   3 | secuholic | se1234   |

+—–+———–+———-+

3 rows in set, 3 warnings (0.00 sec)

mysql> show warnings

-> ;

+———+——+———————————————–+

| Level   | Code | Message                                       |

+———+——+———————————————–+

| Warning | 1292 | Truncated incorrect DOUBLE value: ‘admin’     |

| Warning | 1292 | Truncated incorrect DOUBLE value: ‘wh1ant’    |

| Warning | 1292 | Truncated incorrect DOUBLE value: ‘secuholic’ |

+———+——+———————————————–+

3 rows in set (0.00 sec)

mysql> select * from users where id=’0′;

Empty set (0.00 sec)

mysql> select * from users where 0=id;

+—–+———–+———-+

| num | id        | password |

+—–+———–+———-+

|   1 | admin     | ad1234   |

|   2 | wh1ant    | wh1234   |

|   3 | secuholic | se1234   |

+—–+———–+———-+

3 rows in set, 3 warnings (0.00 sec)

mysql> insert into users values(‘ucjmh’,’ucjmh’,’ucjmh’);

Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> show warnings;

+———+——+————————————————————+

| Level   | Code | Message                                                    |

+———+——+————————————————————+

| Warning | 1366 | Incorrect integer value: ‘ucjmh’ for column ‘num’ at row 1 |

+———+——+————————————————————+

1 row in set (0.00 sec)

mysql> select * from users;

+—–+———–+———-+

| num | id        | password |

+—–+———–+———-+

|   0 | ucjmh     | ucjmh    |

|   1 | admin     | ad1234   |

|   2 | wh1ant    | wh1234   |

|   3 | secuholic | se1234   |

+—–+———–+———-+

4 rows in set (0.00 sec)

如果是在oracle中直接会报ora-01722

dbms_stats.import_table_stats不可以把统计信息导给别的表

今天在itpub看到一个问题 说是使用dbms_stats.import_table_stats并不会导入统计信息 然后做了一个实验如下

SQL> create table test as select * from dba_objects;

Table created.

SQL> create index idx_test on test(object_id);

Index created.

SQL> begin
dbms_stats.gather_table_stats(ownname          => ‘SCOTT’,
tabname          => ‘TEST’,
estimate_percent => 100,
degree           => 2,
cascade          => true);
end;  2    3    4    5    6    7
8  /

PL/SQL procedure successfully completed.

SQL> create table test_temp as select * from test;

Table created.

SQL> create index idx_test_temp on test_temp(object_id);

Index created.

SQL> select table_name,
num_rows,
blocks,
empty_blocks,
avg_space,
avg_row_len,
sample_size
from user_tables
where table_name in (‘TEST’, ‘TEST_TEMP’);  2    3    4    5    6    7    8    9

TABLE_NAME                         NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
—————————— ———- ———- ———— ———-
AVG_ROW_LEN SAMPLE_SIZE
———– ———–
TEST                                    75253        1098                0           0
97          75253

TEST_TEMP

SQL> begin
— dbms_stats.drop_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
dbms_stats.create_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
dbms_stats.export_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’,cascade => true);
dbms_stats.delete_table_stats(ownname=>’scott’,tabname=>’test_temp’);
dbms_stats.import_table_stats(ownname=>’scott’,tabname=>’test_temp’,stattab=>’stat_test_temp’);
end;  2    3    4    5    6    7
8  /

PL/SQL procedure successfully completed.

SQL> select table_name,
num_rows,
blocks,
empty_blocks,
avg_space,
avg_row_len,
sample_size
from user_tables
where table_name in (‘TEST’, ‘TEST_TEMP’);  2    3    4    5    6    7    8    9

TABLE_NAME                         NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
—————————— ———- ———- ———— ———-
AVG_ROW_LEN SAMPLE_SIZE
———– ———–
TEST                                    75253        1098                0           0
97          75253

TEST_TEMP

以上是你的实验

再往下看

SQL> begin
dbms_stats.drop_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
dbms_stats.create_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
dbms_stats.export_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’,cascade => true);
dbms_stats.delete_table_stats(ownname=>’scott’,tabname=>’test’);
—  dbms_stats.import_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’);
end;  2    3    4    5    6    7
8  /

PL/SQL procedure successfully completed.

SQL> select table_name,
num_rows,
blocks,
empty_blocks,
avg_space,
avg_row_len,
sample_size
from user_tables
where table_name in (‘TEST’, ‘TEST_TEMP’);  2    3    4    5    6    7    8    9

TABLE_NAME                         NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
—————————— ———- ———- ———— ———-
AVG_ROW_LEN SAMPLE_SIZE
———– ———–
TEST

TEST_TEMP

SQL> begin
— dbms_stats.drop_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
—  dbms_stats.create_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
–dbms_stats.export_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’,cascade => true);
—  dbms_stats.delete_table_stats(ownname=>’scott’,tabname=>’test’);
dbms_stats.import_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’);
end;  2    3    4    5    6    7
8  /

PL/SQL procedure successfully completed.

SQL> select table_name,
num_rows,
blocks,
empty_blocks,
avg_space,
avg_row_len,
sample_size
from user_tables
where table_name in (‘TEST’, ‘TEST_TEMP’);  2    3    4    5    6    7    8    9

TABLE_NAME                         NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
—————————— ———- ———- ———— ———-
AVG_ROW_LEN SAMPLE_SIZE
———– ———–
TEST                                    75253        1098                0           0
97          75253

TEST_TEMP

由此可见这个功能不是用于把一张表的统计信息给别的表 而是用于发生了进行不同统计信息的性能测试
我们再看一个实验

先备份

SQL> begin
dbms_stats.drop_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
dbms_stats.create_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
–dbms_stats.export_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’,cascade => true);
—  dbms_stats.delete_table_stats(ownname=>’scott’,tabname=>’test’);
–dbms_stats.import_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’);
end;  2    3    4    5    6    7
8  /

PL/SQL procedure successfully completed.

SQL> drop table test purge;

Table dropped.

SQL> create table test as select * from dba_objects;

Table created.

SQL> create index idx_test on test(object_id);

Index created.

SQL> select table_name,
num_rows,
blocks,
empty_blocks,
avg_space,
avg_row_len,
sample_size
from user_tables
where table_name in (‘TEST’, ‘TEST_TEMP’);  2    3    4    5    6    7    8    9

TABLE_NAME                         NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
—————————— ———- ———- ———— ———-
AVG_ROW_LEN SAMPLE_SIZE
———– ———–
TEST

TEST_TEMP

SQL> begin
–dbms_stats.drop_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
— dbms_stats.create_stat_table(ownname=>’scott’,stattab=>’stat_test_temp’);
–dbms_stats.export_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’,cascade => true);
—  dbms_stats.delete_table_stats(ownname=>’scott’,tabname=>’test’);
dbms_stats.import_table_stats(ownname=>’scott’,tabname=>’test’,stattab=>’stat_test_temp’);
end;  2    3    4    5    6    7
8  /

PL/SQL procedure successfully completed.

SQL> select table_name,
num_rows,
blocks,
empty_blocks,
avg_space,
avg_row_len,
sample_size
from user_tables
where table_name in (‘TEST’, ‘TEST_TEMP’);  2    3    4    5    6    7    8    9

TABLE_NAME                         NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
—————————— ———- ———- ———— ———-
AVG_ROW_LEN SAMPLE_SIZE
———– ———–
TEST

TEST_TEMP

SecureFile LOBs and BasicFile LOBs

oracle11g推出以后有了一种新的LOBS的存储模式叫做SecureFile LOBs 与之区别 把以前使用的叫做basicFile Lobs
根据官方文档上的说法 secure是哪哪都好。提供了压缩 重复消除 加密等新的功能 但是basicFile Lobs依然是default的
secureFime的用法是通过在LOB存储子句后添加SECUREFILE关键字来创建
最简单的一种写法:
SQL> CREATE TABLE images (
2 id NUMBER,
3 i_data CLOB
4 )
5 LOB(i_data) STORE AS BASICFILE;

Table created

SQL> CREATE TABLE images2 (id NUMBER,
2 i_data CLOB
3 )
4 LOB(i_data) STORE AS SECUREFILE;

Table created.
———————————————————————————
Securefile列标明了是否为SecureFile类型的LOB
SQL> SELECT TABLE_NAME,SEGMENT_NAME,INDEX_NAME,SECUREFILE FROM DBA_LOBS WHERE TABLE_NAME like ‘IMAGES%’;

TABLE_NAME SEGMENT_NAME
—————————— ——————————
INDEX_NAME SEC
—————————— —
IMAGES SYS_LOB0000076951C00002$$
SYS_IL0000076951C00002$$ NO

IMAGES2 SYS_LOB0000076948C00002$$
SYS_IL0000076948C00002$$ YES
Securefile

使用Securefile LOB的表也是自动生成LOB segment和LOB index的。
但是此时LOB index只有在使用重复消除功能时才会使用,在其他情况下不会使用

SQL> CREATE TABLE images2 (id NUMBER,
2 i_data CLOB
3 )
4 LOB(i_data) STORE AS SECUREFILE
5 ;
CREATE TABLE images2 (id NUMBER,
*
ERROR at line 1:
ORA-43853: SECUREFILE lobs cannot be used in non-ASSM tablespace “SYSTEM”
要注意,Securefile LOB只能在ASSM的表空间(自动管理的表空间)里创建,
不过既然从9i起ASSM表空间就是默认设置了,一般这里不会有多大问题。
只是要求SecureLOB所在的LOB列数据需要存放在ASSM表空间中,而包含LOB列的那个表可以不是
CREATE TABLE images2 (id NUMBER,i_data CLOB) LOB(i_data) STORE AS SECUREFILE (tablespace ucjmh);
SQL> conn / as sysdba
Connected.
SQL> CREATE TABLE images2 (id NUMBER,i_data CLOB) LOB(i_data) STORE AS SECUREFILE (tablespace ucjmh);

Table created.

————————————————————————————————–
SQL> show parameter DB_SECUREFILE

NAME TYPE VALUE
———————————— ———– ——————————
db_securefile string PERMITTED

这个值的取值范围有:
DB_SECUREFILE = { NEVER | PERMITTED | ALWAYS | IGNORE }
PERMITTED 是默认的 就是当你指定是什么的时候就是什么
FORCE 是ORA-43853的来源 意思就是不管你是否指定用SecureFile 创建的时候都是用Securefile 如果不是在ASSM的表空间 那么就报ORA-43853
ALWAYS 意思就是不管你是否指定用SecureFile 创建的时候都是用Securefile 但是如果你是非ASSM的表空间 那就是BASICFILE 如果非ASSM的时候你还显示的指定了用SecureFiLE 那也报43853
NEVER 是不管怎么样都是BasicFile 如果指定了一些加密或压缩之类的参数那么就报ORA-43853
IGNORE 是不管怎么样都是BasicFile 如果指定了一些加密或压缩之类的参数也不报错。

chunk:
在BasicFile的LOB中,Chunk的大小是一定的,最小跟DB Block的大小一样,最大为32KB
–一个chunk最多只保留一行LOB数据,也就是说,如果你设置了32K的CHUNK,但是如果LOB字段大小只有4K,也将占用32K的空间
而在SecureFile中,chunk的size是可变的,由Oracle自动动态分配,最小跟DB Block的大小一样,最大为64MB
–指定的值最好是数据库块的倍数,而且指定的值不能大于表空间区间中NEXT的值

当指定enable storage in row的时候,当lob size =4000 bytes的时候,将存储在lob段里面,其存储方式和表段存储方式完全不一样,使用的是chunk为最小单位的存储,没有行迁移和行链接的概念。
如果设置了enable storage in row 那么oracle会自动将小于4000bytes的数据存储在行内, 这是ORACLE的默认值,
对于大于4000字节的lob字段保存在lob段(同disable storage in row),在表段将保留36-84字节的控制信息。
对于disable storage in row,Oracle将lob字段分开保存在lob段中,而仅仅在行位置保留20字节的指针。
对于相当于disable storage in row的这部分(也就是单独保存在LOB段的这部分数据),UNDO仅仅是记录指针与相关lob索引改变,如果发生更新操作等DML操作,原始数据将保留在LOB段
DISABLE STORAGE IN ROW:如果DISABLE这个属性,那么lob数据会在行外存储,行内只存储该lob值得指针,而且这个属性在表
storage as(cache|nocahce)表示是否允许lob段经过buffer cache并缓存。默认是nocache,表示直接读与直接写,不经过数据库的data buffer。所以,默认情况下,对于单独保存在LOB段的这部分数据,在发生物理读的时候,是直接读,如direct path read (lob)

storage as(nocache logging |nocache nologging),logging/nologging属性只对nocache方式生效,默认是logging,如果是nologging方式,对于 保存在行外的log部分,在update等DML操作时将不记录redo日志

PCTVERSION integer、RETENTION:都是ORACLE用来管理LOB字段镜像数据的。在LOB 数据的更新过程中,
ORACLE没有用UNDO TABLESPACE空间,而是从LOB字段所在的表空间里划分一段空间来做镜像空间的,
这个空间的大小由PCTVERSION参数控制,默认值为10,代表划分表空间的10%作为镜像空间,
每个镜像空间的单元大小由CHUNK参数指定,pctversion可以使用在manual undo mode和automatic undo mode 环境中
retention应用了automatic undo mode中的undo_retention通过时间来管理lob镜像空间.
pctversion和retention不能同时被指定.建议数据库在automatic undo mode下使用retention参数

SecureFile的COMPRESS选项在表或分区一级上开启了对LOB内容的压缩,使用关键字MEDIUM和HIGH表示压缩的等级,
如果没有指定压缩等级,就默认为MEDIUM,对LOB内容进行压缩会增加系统开销,
因此使用高等级的压缩可能会对系统性能产生不良影响,SecureFile LOB的压缩功能不影响表压缩,反之亦然
SecureFile LOB的加密功能依赖于钱夹或硬件安全模型(HSM)掌管加密密钥,钱夹设置与透明数据加密(TDE)和表空间加密描述的一样,
因此在尝试下面的例子前先完成那两个实验。SecureFile的ENCRYPT选项执行块级别的LOB内容加密,
一个可选的USING子句定义了使用哪种加密算法(3DES168, AES128, AES192, 或AES256),默认使用AES192算法,
NO SALT选项对于SecureFile加密不可用,加密是应用在每一列上的,因此它会影响所有使用LOB的分区,DECRPT选项用于明确地阻止加密
加密是不受imp/exp或表空间传输支持的,因此必须使用impdp/exddp来传输数据

BasicFile和SecureFile LOB共享了部分基础的缓存和日志选项,常见的缓存选项有:
CACHE – LOB数据被放在缓冲区中。
CACHE READES – 仅读取LOB数据过程中它放在缓冲区中,写操作时不放进去。
NOCACHE – LOB数据不放在缓冲区中,这是BasicFile和SecureFile LOB的默认值。
基本的日志选项有:
LOGGING – 创建和修改LOB时产生完全重做日志,这是默认设置。
NOLOGGING – 操作不记录在重做日志中,因此不能恢复,在首次创建和巨大的载入过程中有用。
而且,SecureFile LOB还有一个日志选项FILESYSTEM_LIKE_LOGGING,只记录元数据,在出现故障后仍然允许段的恢复。
CACHE选项意味着LOGGING,因此你不能将CACHE与NOLOGGING或FILESYSTEM_LIKE_LOGGING合在一起使用。
如果要移动表
ALTER TABLE table_name LOB (lob_item) STORE AS [lob_segment]
(
TABLESPACE tablespace_name
(STORAGE…..)
ENABLE|DISABLE STORAGE IN ROW
CHUNK integer
PCTVERSION integer
RETENTION
FREEPOOLS integer
CACHE|NOCACHE|CACHE READS
INDEX lobindexname

(TABLESPACE tablesapce_name

((STORAGE…..))
)

Oracle Varray变量

[oracle@ucjmh ~]$ sqlplus / as sysdba

SQL*Plus: Release 11.2.0.3.0 Production on Tue Jul 7 09:01:40 2015

Copyright (c) 1982, 2011, Oracle. All rights reserved.

Connected to:

Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 – Production

With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> set serveroutput on

SQL> DECLARE

— declare fixed array

TYPE arry_num IS VARRAY(10) OF NUMBER;

arry_top arry_num;

v_n int;

BEGIN

— init array

arry_top := arry_num(1,2,3);

dbms_output.put_line(arry_top.LIMIT());

dbms_output.put_line(arry_top.count());

dbms_output.put_line(arry_top.FIRST());

–dbms_output.put_line(arry_top.NEXT());

— dbms_output.put_line(arry_top.PRIOR());

dbms_output.put_line(arry_top.LAST());

dbms_output.put_line(arry_top(2));

FOR I IN 1..arry_top.COUNT() LOOP

v_n:=arry_top(I);

dbms_output.put_line(v_n);

DBMS_OUTPUT.PUT_LINE(‘arry_top(‘||I||’)=’ || arry_top(I));

END LOOP;

END;

/

10

3

1

3

2

1

arry_top(1)=1

2

arry_top(2)=2

3

arry_top(3)=3

PL/SQL procedure successfully completed.

SQL>

声明和初始化VARRAY变量

你不可以直接声明一个VARRAY变量。必须先声明一个包含这个数组最大容量的类型:
1. TYPE MY_ARRAY_TYPE IS VARRAY(10) OF NUMBER;
然后,你就可以用这个类型声明VARRAY变量:
1. V MY_ARRAY_TYPE;
在你对这个数组进行操作以前,必须先初始化该数组。你既可以在声明它的时候对其初始化,也可以在声明后对其赋值。下面展示了在声明的同时对其进行初始化:
1. V MY_ARRAY_TYPE := MY_ARRAY_TYPE ();
从指定的条目处取值

把条目的数目作为下标。下标可以是返回整数值(该值小于或等于数组条目数)的任意表达式,如:
1. K := V(3); 2. I := 2; 3. L := V(I+1);
得到数组的容量

对VARRAY变量使用COUNT()方法。注意,你不需要指定圆括号,因为这个方法没有输入参数:
1. N_ENTRIES := V.COUNT();
这告诉你这个数组中正在使用的条目数。当VARRAY类型被声明的时候,其最大的容量也就被定义了。你可以用LIMIT()方法得到该容量:
1. ARRAY_CAPACITY := V.LIMIT();
遍历VARRAY中的值

你可以使用多种技术。最简单的是使用FOR循环:
1. FOR I IN 1..V.COUNT() LOOP 2. DBMS_OUTPUT.PUT_LINE(‘V(‘||I||’)=’ || V(I)); 3. END LOOP;
你也可以使用FIRST()和LAST()方法。FIRST()返回数组的第一个条目的下标(总是1),LAST()返回数组的最后一个条目的下标(与COUNT方法相同)。
1. FOR I IN V.FIRST()..V.LAST() LOOP 2. DBMS_OUTPUT.PUT_LINE(‘V(‘||I||’)=’ || V(I)); 3. END LOOP;
你也可以使用PRIOR(n)和NEXT(n)方法,这两个方法分别返回给定条目的前一个和后一个条目的下标。例如,下面的代码用来向后遍历整个数组:
1. I := V.COUNT(); 2. WHILE I IS NOT NULL LOOP 3. DBMS_OUTPUT.PUT_LINE(‘V(‘||I||’)=’ || V(I)); 4. I := V.PRIOR(I); 5. END LOOP;
PRIOR(n)和n-1是一样的,NEXT(n)和n+1是一样的,但是PRIOR(1)和NEXT(V.COUNT())则返回NULL。
对VARRAY的扩展

使用EXTEND(k)方法。这个方法可以在VARRAY的最后追加k个新的条目。如果k没有被指定,只增加一个条目。新增的条目没有值(默认为NULL),但是你可以对它们进行初始化。COUNT()和LAST()方法现在可以反映VARRAY新的容量。以下代码向数组中添加两个条目并对其进行初始化:
1. I := V.LAST(); 2. V.EXTEND(2); 3. V(I+1) := 5; 4. V(I+2) := 6;
注意,你对VARRAY的扩展不可以超过其最大容量(通过LIMIT()方法得到),且在对VARRAY扩展前必须要对其进行初始化。以下代码是无效的:
1. VT MY_ARRAY_TYPE; 2. VT.EXTEND(5);
下面的代码是有效的:
1. VT MY_ARRAY_TYPE; 2. VT := MY_ARRAY_TYPE(); 3. VT.EXTEND(5);
对VARRAY的缩减

使用TRIM(k)方法。这个方法在VARRAY的尾部删除最后k个条目。当k没有被指定时,删除最后一个条目。已被删除的条目的值将丢失。COUNT()和LAST()方法反映了新的容量。下面从VARRAY中删除最后一个条目:
1. V.TRIM;
你也可以用以下方式删除数组中的所有条目:
1. V.TRIM(V.COUNT());
或者用具有效果相同的DELETE()方法。该方法删除数组中的所有条目,并把其容量设置为0(也就是说,V.COUNT()现在的返回值为0)。
1. V.DELETE()

使用SQL实现对邮政编码分类规则

早晨在大阪工作的学长发信息过来要想用SQL实现对日本邮政编码分类的规则.本来学长只需要做概要设计即可,详细设计和开发都是外包给越南人做,但是担心越南的童鞋方法不能够满意,索性就直接把关键的步骤都自己实现掉,想想也是为了世界和平做了不少贡献;

规则是这样的:
1.日本邮编是7位的;
2.最终的结果集只需要两列(前缀 个数);
3.第一优先级是尾数为[00]的邮政编码;
4.第二优先级是前5位数字相同的邮政编码,只展现出现次数大于10次的结果,如果不足10次的邮政编码进入下一优先级统计;
5.第三优先级是前4位数字相同的邮政编码,规则同上;
6.第四优先级是前3位数字相同的邮政编码,规则同上;
7.第五优先级是剩余其它的邮政编码;

— 测试的数据,生成20W个邮政编码;
DROP TABLE TBPOSTTEST;
CREATE TABLE TBPOSTTEST AS
SELECT LPAD(ROUND(DBMS_RANDOM.VALUE (1, 9999999)) , 7, 0) postcode FROM DUAL
CONNECT BY LEVEL <= 200000;

— 实现的sql语句;
SELECT PREFIX, COUNT FROM (
WITH
P0P0 AS (SELECT ‘____00′ AS “PREFIX”, COUNT(*) c0 FROM TBPOSTTEST WHERE SUBSTR(postcode, LENGTH(postcode)-1) = ’00’),
P1P5 AS (SELECT SUBSTR(postcode, 0, 5) cp5, COUNT(*) c5 FROM TBPOSTTEST WHERE SUBSTR(postcode, LENGTH(postcode)-1) <> ’00’ GROUP BY SUBSTR(postcode, 0, 5)),
P2P4 AS (SELECT SUBSTR(cp5, 0, 4) cp4, SUM(c5) c4 FROM P1P5 WHERE c5 < 10 GROUP BY SUBSTR(cp5, 0, 4)),
P3P3 AS (SELECT SUBSTR(cp4, 0, 3) cp3, SUM(c4) c3 FROM P2P4 WHERE c4 < 10 GROUP BY SUBSTR(cp4, 0, 3))
SELECT ‘0’ AS “PRIORITY”, PREFIX, c0 AS “COUNT” FROM P0P0
UNION ALL
SELECT ‘1’, cp5 , c5 FROM P1P5 WHERE c5 >= 10
UNION ALL
SELECT ‘2’, cp4 , c4 FROM P2P4 WHERE c4 >= 10
UNION ALL
SELECT ‘3’, cp3 , c3 FROM P3P3 WHERE c3 >= 10
UNION ALL
SELECT ‘4’, ‘OTHER’ , SUM(c3) c2 FROM P3P3 WHERE c3 < 10)
ORDER BY PRIORITY;

— 测试的DEMO, 把条件改为大于2条;
INSERT INTO TBPOSTTEST VALUES(‘123400’);
INSERT INTO TBPOSTTEST VALUES(‘123500’);
INSERT INTO TBPOSTTEST VALUES(‘123450’);
INSERT INTO TBPOSTTEST VALUES(‘123451’);
INSERT INTO TBPOSTTEST VALUES(‘123452’);
INSERT INTO TBPOSTTEST VALUES(‘123453’);
INSERT INTO TBPOSTTEST VALUES(‘123454’);
INSERT INTO TBPOSTTEST VALUES(‘123444’);
INSERT INTO TBPOSTTEST VALUES(‘123555’);
INSERT INTO TBPOSTTEST VALUES(‘123566’);
INSERT INTO TBPOSTTEST VALUES(‘124444’);
COMMIT;

SELECT PREFIX, COUNT FROM (
WITH
P0P0 AS (SELECT ‘____00′ AS “PREFIX”, COUNT(*) c0 FROM TBPOSTTEST WHERE SUBSTR(postcode, LENGTH(postcode)-1) = ’00’),
P1P5 AS (SELECT SUBSTR(postcode, 0, 5) cp5, COUNT(*) c5 FROM TBPOSTTEST WHERE SUBSTR(postcode, LENGTH(postcode)-1) <> ’00’ GROUP BY SUBSTR(postcode, 0, 5)),
P2P4 AS (SELECT SUBSTR(cp5, 0, 4) cp4, SUM(c5) c4 FROM P1P5 WHERE c5 < 2 GROUP BY SUBSTR(cp5, 0, 4)),
P3P3 AS (SELECT SUBSTR(cp4, 0, 3) cp3, SUM(c4) c3 FROM P2P4 WHERE c4 < 2 GROUP BY SUBSTR(cp4, 0, 3))
SELECT ‘0’ AS “PRIORITY”, PREFIX, c0 AS “COUNT” FROM P0P0
UNION ALL
SELECT ‘1’, cp5 , c5 FROM P1P5 WHERE c5 >= 2
UNION ALL
SELECT ‘2’, cp4 , c4 FROM P2P4 WHERE c4 >= 2
UNION ALL
SELECT ‘3’, cp3 , c3 FROM P3P3 WHERE c3 >= 2
UNION ALL
SELECT ‘4’, ‘OTHER’ , SUM(c3) c2 FROM P3P3 WHERE c3 < 2)
ORDER BY PRIORITY;

— 测试结果;
PREFIX        COUNT
—————— ———-
____00               2
12345                 5
1235                   2
OTHER                2

创建带参数的视图

PROMPT CREATE OR REPLACE PACKAGE pkg_session

CREATE OR REPLACE PACKAGE pkg_session IS
TYPE parm_type IS TABLE OF varchar2(1000) index BY VARCHAR2(30);
PROCEDURE set_val(p_idx IN VARCHAR2,p_value IN VARCHAR2);
PROCEDURE set_val(p_idx IN VARCHAR2,p_value IN NUMBER);
PROCEDURE set_val(p_idx IN VARCHAR2,p_value IN DATE);
FUNCTION get_val(p_idx IN VARCHAR2) RETURN VARCHAR2 ;
FUNCTION get_val_number(p_idx IN VARCHAR2) RETURN NUMBER;
FUNCTION get_val_date(p_idx IN VARCHAR2) RETURN DATE;
PRAGMA RESTRICT_REFERENCES(get_val,WNDS,WNPS);
PRAGMA RESTRICT_REFERENCES(get_val_number,WNDS,WNPS);
PRAGMA RESTRICT_REFERENCES(get_val_date,WNDS,WNPS);
END pkg_session;
/

CREATE OR REPLACE PACKAGE BODY pkg_session IS
sv_parameters parm_type;
PROCEDURE set_val(p_idx IN VARCHAR2,p_value IN VARCHAR2)
IS
BEGIN
sv_parameters(TRIM(UPPER(p_idx))) := SUBSTR(p_value,1,1000);
RETURN;
END set_val;

PROCEDURE set_val(p_idx IN VARCHAR2,p_value IN NUMBER)
IS
BEGIN
set_val(p_idx,TO_CHAR(p_value));
RETURN;
END set_val;

PROCEDURE set_val(p_idx IN VARCHAR2,p_value IN DATE)
IS
BEGIN
set_val(p_idx,TO_CHAR(p_value,’YYYYMMDDHH24MISS’));
RETURN;
END set_val;

FUNCTION get_val(p_idx IN VARCHAR2) RETURN VARCHAR2
IS
BEGIN
RETURN sv_parameters(TRIM(UPPER(p_idx)));
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END get_val;

FUNCTION get_val_number(p_idx IN VARCHAR2) RETURN NUMBER
IS
BEGIN
RETURN TO_NUMBER(get_val(p_idx));
END get_val_number;

FUNCTION get_val_date(p_idx IN VARCHAR2) RETURN DATE
IS
BEGIN
RETURN TO_DATE(get_val(p_idx),’YYYYMMDDHH24MISS’);
END get_val_date;

END pkg_session;
/

CREATE OR REPLACE VIEW VW_TEST
AS SELECT * FROM t WHERE ROWNUM<=pkg_session.get_val_number('P_ROWNUM');

— 未设参数时没有数据
SELECT * FROM VW_TEST;

— 设置参数:

EXEC pkg_session.set_val('P_ROWNUM',10);

— 以下会返回10行:
SELECT * FROM VW_TEST;

–两个条件 并且当不设置值的时候不启用
CREATE OR REPLACE VIEW VW_TEST2
AS SELECT * FROM t WHERE ROWNUM exec pkg_session.set_val(‘P_NAME’,’UCJMH’);

PL/SQL procedure successfully completed.

SQL> select * from vw_test2;

USERNAME
——————————
UCJMH
这个时候就只会有一行数据了

在给定时间间隔,求间隔内出现的记录数

在itpub里看到一个很有意思的sql

一个表中记录了各个地点的报警开始时间,结束时间。

现在想统计,各个地点报警次数和时长,时长=结束时间-开始时间。

要求,同一个地点,开始时间间隔超过10分钟的,这个地点要在结果中再出现一次,10分钟之内的假如有多条,统计条数和报警时长,时长=10分钟内各条时长之和。

create table T_DEMO
(
id VARCHAR2(32) not null,
place_id VARCHAR2(32),
s_time DATE,
e_time DATE
);

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘1’, ‘1’, to_date(’21-08-2014 01:24:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 01:24:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘2’, ‘1’, to_date(’21-08-2014 01:25:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 01:25:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘3’, ‘1’, to_date(’21-08-2014 01:35:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 01:35:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘4’, ‘1’, to_date(’21-08-2014 01:39:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 01:39:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘5’, ‘1’, to_date(’21-08-2014 02:05:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 02:05:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘6’, ‘1’, to_date(’21-08-2014 02:06:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 02:06:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘7’, ‘1’, to_date(’21-08-2014 02:12:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 02:12:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘8’, ‘1’, to_date(’21-08-2014 02:14:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 02:14:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (‘9’, ‘1’, to_date(’21-08-2014 02:50:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 02:50:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (’10’, ‘1’, to_date(’21-08-2014 02:52:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 02:52:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (’11’, ‘1’, to_date(’21-08-2014 02:57:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’21-08-2014 02:57:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (’12’, ‘1’, to_date(’25-08-2014 09:13:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’25-08-2014 09:13:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (’13’, ‘1’, to_date(’25-08-2014 09:18:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’25-08-2014 09:18:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (’14’, ‘1’, to_date(’25-08-2014 09:30:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’25-08-2014 09:30:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (’15’, ‘1’, to_date(’25-08-2014 09:37:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’25-08-2014 09:37:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (’16’, ‘1’, to_date(’25-08-2014 09:47:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’25-08-2014 09:47:01′, ‘dd-mm-yyyy hh24:mi:ss’));

insert into t_demo (ID, PLACE_ID, S_TIME, E_TIME)

values (’17’, ‘2’, to_date(’25-08-2014 10:09:00′, ‘dd-mm-yyyy hh24:mi:ss’), to_date(’25-08-2014 10:09:01′, ‘dd-mm-yyyy hh24:mi:ss’));

WITHt2AS

(SELECT  a.place_id,

a.s_time,

a.e_time,

a.id,

floor((a.s_time – to_date(‘2000-01-01′,’YYYY-MM-DD’)) * 24 * 60 / 10) last_cha

FROM t_demo a)

SELECT

place_id 地点,

SUM(e_time – s_time) * 24 * 60 * 60 “时长:秒”,

min(s_time)  开始时间,

COUNT(*) 次数

FROM t2

GROUP BY place_id, last_cha;

导入导出工具04–Oralce数据库中表数据导出为TXT格式

Oracle数据库中表数据导出为TXT格式

  1. 使用SPOOL命令把结果输出到文本中;
  2. 使用编写的C语言的脚本;
——————————- 使用SPOOL命令把结果输出到文本中 ——————————-
— 1.编写脚本为/tmp/orauldr.sql;
set echo on            — 是否显示执行的命令内容
set feedback off       — 是否显示提示符,即多少条记录打一个点
set heading off        — 是否显示字段的名称
set verify off         — 是否显示替代变量被替代前后的语句
set trimspool off      — 去字段空格
set pagesize 1000      — 页面大小
set linesize 50        — linesize设定尽量根据需要来设定,大了生成的文件也大
define fil= ‘/tmp/exp.txt’
prompt *** Spooling to &fil
spool &fil
select col1 || ‘,’ || username || ‘,’ || ‘,’|| password from tbtest; — 格式自己拼接出来,比较灵活,导入也不容易出错
spool off;
— 2.在SQL*PLUS中执行;
SQL>@ /tmp/orauldr.sql;
——————————- 使用SPOOL命令把结果输出到文本中 ——————————-
——————————- 使用编写的C语言的脚本 ——————————-
第三方使用C语言写的工具蛮多的,可以联系sonne.k.wang@gmailcom交流;
——————————- 使用编写的C语言的脚本 ——————————-

导入导出工具03–可传输表空间

可传输表空间
  1. 用途:
    1. 用于不同平台的数据迁移;
    2. 用于不同版本数据库之间的迁移;
  2. 限制:
    1. 不支持XMLSchema类型的数据;
    2. 只是把表空间上的数据传输到目标,用户的对象(序列,视图,包,过程,触发器)需要手动创建
  3. 实验目标:创建测试表空间TBS_TT1,并传输到目标服务器;
  4. 检查是否满足表空间传输的条件:
    1. 查看可传输表空间支持数据库平台和当前数据库的版本;                                              
    2. 查看要传输的表空间集是否是自包含的:
      1. 如果某个表空间集引用了其他表空间的对象,则这个表空间不是自包含的,否则就是自包含;
      2. 只有自包含的表空间集才可以用传输表空间技术;
      3. 使用dbms_tts.transport_set_check来验证表空间是否是自包含的,要把需要表空间传输的表空间列表都写上,中间用逗号分割;
      4. TRANSPORT_SET_VIOLATIONS表是一个临时表,必须在执行dbms_tts.transport_set_check的session中查看,如果有记录返回,则表示表空间集不是自包含的;
      5. 检查的结果;                                                  
    3. 查看数据库的字符集;                                                                                                             
  5. 在源数据库导出表空间;
    1. 创建目录对象:CREATE DIRECTORY dir_tt AS ‘/u01/tt’;                     
    2. 把对应的表空间设置为只读模式:ALTER TABLESPACE tbs_tt1 READ ONLY;                                
    3. 使用数据泵导出,只是导出表空间的元信息:expdp \’sys/oracle as sysdba\’ directory=dir_tt transport_tablespaces=tbs_tt1 dumpfile=tt_tbs_tt1.dmp logfile=tt.log job_name=’job_tt’;                                                      
  6. 转换数据文件格式:
    1. 如果源数据库和目标数据库所在的平台不同需要进行此步骤,把表空间的数据转化为目标平台格式;
    2. rman提供了此功能,可以在源数据库进行也可以在目标数据库进行:
      1. 在源数据库转换:convert tablespace ts1,ts2… to platform <destination platform> format ‘/path/%U’;
      2. 在目标数据库转换:convert datafile df1,df2… from platform <source platform> format ‘/path/%U’;
  7. 把导出的表空间的元信息和表空间的数据文件传到目标数据库服务器,数据文件放到数据库的目录下,元数据放到目录对象下;
  8. 修改源数据库表空间为读写模式:ALTER TABLESPACE tbs_tt1 READ WRITE;            
  9. 在目标服务器导入表空间:
    1. 创建目录对象:CREATE DIRECTORY dir_tt AS ‘/u01/tt’;                      
    2. 在目标数据库上创建表空间上的用户和并授予权限,否则会报错;                         
    3. 如果之前进行了平台的转换操作的话,此时可能需要使用rman对数据文件进行重命名了;
    4. 如果源数据库和目标数据库的块大小不同的话,需要在目标数据库添加源数据库块的cache_size:ALTER SYSTEM SET db_4k_cache_size = 5M;
    5. 把数据导入到目标数据库中:impdp \’sys/oracle as sysdba\’ directory=dir_tt dumpfile=tt_tbs_tt1.dmp transport_datafiles=’/u01/app/oracle/oradata/ORCL/tbs_tt1.dbf’ logfile= tt.log job_name=’job_tt’;                                  
    6. 修改表空间为读写模式:ALTER TABLESPACE tbs_tt1 READ WRITE;(SELECT tablespace_name, status FROM dba_tablespaces WHERE tablespace_name = ‘TBS_TT1’;)           
    7. 修改用户的默认表空间:ALTER USER u1 DEFAULT TABLESPACE tbs_tt1;(SELECT username, default_tablespace FROM dba_users WHERE username = ‘U1’;)          
  10. 验证数据;
— 测试数据;
— 创建表空间;
CREATE TABLESPACE TBS_TT1 DATAFILE ‘/u01/app/oracle/oradata/ORCL/tbs_tt1.dbf’ SIZE 20M AUTOEXTEND ON EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;
— 创建用户并授权;
CREATE USER u1 IDENTIFIED BY u1 DEFAULT TABLESPACE tbs_tt1;
GRANT CONNECT, RESOURCE TO u1;
— 创建表,序列,并插入测试数据;
CREATE TABLE u1.t1(ID INTEGER, c_lob CLOB) TABLESPACE tbs_tt1;
CREATE SEQUENCE u1.seq_t1;
INSERT INTO u1.t1(ID, c_lob) VALUES (u1.seq_t1.NEXTVAL, to_clob(‘传输表空间-表1中BLOB字段’));
COMMIT;
— 创建视图;
CREATE VIEW v_t1(ID, NAME) AS SELECT ID, to_char(c_lob) FROM u1.t1;
SELECT * FROM v_t1;
— 创建过程;
CREATE OR REPLACE PROCEDURE pr_t1 AS
BEGIN
NULL;
END;
— 查看可传输表空间支持数据库平台和当前数据库的版本;
SELECT d.NAME, i.host_name, i.version, d.platform_name, endian_format
FROM v$transportable_platform tp, v$database d, v$instance i
WHERE tp.platform_name = d.platform_name AND d.db_unique_name = i.instance_name;
— 查看表空间是否是自包含的;
EXECUTE dbms_tts.transport_set_check(‘TBS_TT1’, TRUE, TRUE);
SELECT * FROM transport_set_violations;
— 查看数据库的字符集;
SELECT max(decode(parameter,’NLS_LANGUAGE’, VALUE, NULL)) || ‘_’ ||
max(decode(parameter,’NLS_TERRITORY’, VALUE, NULL)) || ‘.’ ||
max(decode(parameter,’NLS_CHARACTERSET’, VALUE, NULL)) AS “NLS_LANG”
FROM nls_database_parameters;
— 数据文件平台转换的脚本;
RMAN> run{
allocate channel c1 device type disk connect ‘sys/oracle@orcl_source’;
convert tablespace example,users to platform ‘Linux IA (32-bit)’ Format ‘/backup/dmp/%U’;
release channel c1;}

由于bitmap造成的insert相互阻塞

CREATE TABLE t
(
NAME VARCHAR2(20)
);

CREATE BITMAP INDEX idx_b_name ON t(NAME);

INSERT INTO t VALUES(‘U’);
INSERT INTO t VALUES(‘C’);

select sid from v$mystat where rownum<2;  –198

select addr, sid, type, id1,id2, lmode,request,block from v$lock where sid=198 or sid=200;
1 000007FFBDC55D50 198 AE 100 0 4 0 0
2 000000001E2E9600 198 TM 77149 0 3 0 0
3 000007FFB9CE8488 198 TX 65561 1133 6 0 0
–这个时候登录第二个session
select sid from v$mystat where rownum<2;
200

INSERT INTO t VALUES(‘U’);
select addr, sid, type, id1,id2, lmode,request,block from v$lock where sid=198 or sid=200;

1 000007FFBDC55D50 198 AE 100 0 4 0 0
2 000007FFBDC56588 200 AE 100 0 4 0 0
3 0000000026980C10 198 TM 77149 0 3 0 0
4 000007FFB9CE8488 198 TX 196617 1336 6 0 0

这个时候在SESSION2中
INSERT INTO t VALUES(‘E’);

select addr, sid, type, id1,id2, lmode,request,block from v$lock where sid=198 or sid=200;

1 000007FFBDC55D50 198 AE 100 0 4 0 0
2 000007FFBDC56588 200 AE 100 0 4 0 0
3 000000002697E370 200 TM 77149 0 3 0 0
4 000000002697E370 198 TM 77149 0 3 0 0
5 000007FFB9CC9678 200 TX 65568 1131 6 0 0
6 000007FFB9CE8488 198 TX 196617 1336 6 0 0
SEssion不阻塞

这个时候继续在session2 做插入

INSERT INTO t VALUES(‘U’);
1 000007FFBDC55D50 198 AE 100 0 4 0 0
2 000007FFBDC56588 200 AE 100 0 4 0 0
3 000007FFBDC56658 200 TX 196617 1336 0 4 0
4 0000000026980C10 200 TM 77149 0 3 0 0
5 0000000026980C10 198 TM 77149 0 3 0 0
6 000007FFB9CC9678 200 TX 65568 1131 6 0 0
7 000007FFB9CE8488 198 TX 196617 1336 6 0 1

这个时候Insert发生了阻塞 session2在等seesion1释放资源

在这个时候发生一个insert 阻碍了 INSERT 的操作  造成它的原因就是 BITMAP
SELECT OBJECT_ID FROM User_Objects WHERE object_name=’IDX_B_NAME’ ;   –77150

alter session set events ‘immediate trace name treedump level INDEX_OBJECT_ID’;

alter session set events ‘immediate trace name treedump level 52563’

Bitmap深入研究(一)

环境:
ORACLE:
BANNER
—————————————————————-
Oracle Database 10g Enterprise Edition Release 10.2.0.2.0 – Prod
PL/SQL Release 10.2.0.2.0 – Production
CORE    10.2.0.2.0      Production
TNS for Linux: Version 10.2.0.2.0 – Production
NLSRTL Version 10.2.0.2.0 – Production

OS:

LSB Version:    :core-3.1-ia32:core-3.1-noarch:graphics-3.1-ia32:graphics-3.1-noarch
Distributor ID: EnterpriseEnterpriseServer
Description:    Enterprise Linux Enterprise Linux Server release 5.4 (Carthage)
Release:        5.4
Codename:       Carthage
研究Bitmap索引的存储和特性

SQL> conn scott/scott
Connected.
SQL> CREATE TABLE t –创建一张测试表
2  (
3  NAME VARCHAR2(20)
4  );
Table created.

SQL> CREATE BITMAP INDEX idx_b_name ON t(NAME); –创建一个测试的索引

Index created.
SQL> select sid from v$mystat where rownum<2;  –查看自己的sid

SID
———-
159

–查看trace的文件所在
select a.value||b.symbol||c.instance_name||’_ora_’||d.spid||’.trc’ trace_file from
(select value from v$parameter where name=’user_dump_dest’) a,
(select substr(value,-6,1) symbol from v$parameter where name=’user_dump_dest’) b,
(select instance_name from v$instance) c,
(select spid from v$session s,v$process p,v$mystat m where s.paddr=p.addr
and s.sid=m.sid and m.statistic#=0) d;

TRACE_FILE
——————————————————————————–
/u01/app/oracle/admin/orcl/udump/orcl_ora_9707.trc
查看文件内容

*** ACTION NAME:() 2015-03-03 08:25:59.728
*** MODULE NAME:(SQL*Plus) 2015-03-03 08:25:59.728
*** SERVICE NAME:(SYS$USERS) 2015-03-03 08:25:59.728
*** SESSION ID:(159.7) 2015-03-03 08:25:59.728
—– begin tree dump   这个是空的时候
leaf: 0x100018c 16777612 (0: nrow: 0 rrow: 0)
—– end tree dump
*** 2015-03-03 08:28:08.940
—– begin tree dump   insert into t values(‘U’); commit;
leaf: 0x100018c 16777612 (0: nrow: 1 rrow: 1)
—– end tree dump
*** 2015-03-03 08:29:20.359
—– begin tree dump insert into t values(‘c’); commit;
leaf: 0x100018c 16777612 (0: nrow: 2 rrow: 2)
—– end tree dump
*** 2015-03-03 08:30:09.053
—– begin tree dump 又insert into t values(‘U’); commit;
leaf: 0x100018c 16777612 (0: nrow: 2 rrow: 2)
—– end tree dump
*** 2015-03-03 08:38:07.763
—– begin tree dump  delete from t where name=’c’; commit;
leaf: 0x100018c 16777612 (0: nrow: 2 rrow: 1)
—– end tree dump
*** 2015-03-03 08:40:35.384
—– begin tree dump  –insert into t select OBJECT_TYPE from user_objects;
leaf: 0x100018c 16777612 (0: nrow: 3 rrow: 3)
—– end tree dump

—– begin tree dump   insert into t SELECT object_type FROM dba_objects; / commit;
branch: 0x100018c 16777612 (0: nrow: 6, level: 1)
leaf: 0x1000190 16777616 (-1: nrow: 11 rrow: 11)
leaf: 0x100018f 16777615 (0: nrow: 2 rrow: 2)
leaf: 0x100018d 16777613 (1: nrow: 17 rrow: 17)
leaf: 0x100028c 16777868 (2: nrow: 3 rrow: 3)
leaf: 0x100018e 16777614 (3: nrow: 2 rrow: 2)
leaf: 0x100028d 16777869 (4: nrow: 11 rrow: 11)
—– end tree dump 这个时候可以发现整个树的结构都发生了变化 不再是单纯只有一个叶子节点了

仔细观察最后一次dump出来的结构 我们可以得出以下结论
1.原来的叶子节点发生了分裂而不是迁移。(b-tree也是一样) 根节点永远不会变
2.叶子节点从-1开始的顺序不代表在数据文件上也是连续的块

观察倒数第二个的nrow=3 我们去验证是否是这样
SQL> select DISTINCT name from t;

NAME
——————–
U
INDEX
TABLE

第一列中的
1.branch表示分支节点(包括根节点),
2.leaf则表示叶子节点
第二列表示十六进制表示的节点的地址;
第三列表示十进制表示的节点的地址;
第四列表示相对于前一个节点的位置,根节点从0开始计算,其他分支节点和叶子节点从-1开始计算;
第五列的nrow表示当前节点中所含有的索引条目的数量。比如我们可以看到含有的nrow为2,表示节点中含有2个索引条目,
分别指向2个分支节点
第六列中的
1.rrow表示有效的索引条目(因为索引条目如果被删除,不会立即被清除出索引块中。
所以nrow减rrow的数量就表示已经被删除的索引条目数量)的数量,
2.level表示分支节点的层级,对于叶子节点来说level都是0

以种方式以树状形式转储整个索引。同时,我们可以转储一个索引节点来看看其中存放了些什么

从上面我们可以看到索引块(根)的地址为:16777612 .我们先将其转换为文件号以及数据块号

select dbms_utility.data_block_address_file(16777615) “file”,dbms_utility.data_block_address_block(16777615) “BLOCK” from dual;

FILE  BLOCK
—————
4     396

— 注: 反推的话可以使用  select dbms_utility.make_data_block_address(4,396) from dual;
我们首先看一下根节点 然后再看一下叶子节点

SQL> alter system dump datafile 4 block 396;

System altered.
–内容如下

*** 2015-03-03 09:03:48.908
Start dump data blocks tsn: 4 file#: 4 minblk 396 maxblk 396
buffer tsn: 4 rdba: 0x0100018c (4/396)
scn: 0x0000.000739b7 seq: 0x02 flg: 0x04 tail: 0x39b70602
frmt: 0x02 chkval: 0x3905 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x0DECE400 to 0x0DED0400
DECE400 0000A206 0100018C 000739B7 04020000  [………9……]
DECE410 00003905 00000002 0000CD53 000739B7  [.9……S….9..]
DECE420 00000000 00320001 01000189 002C0006  [……2…….,.]
DECE430 00000102 00800B8F 00020136 00008000  [……..6…….]
DECE440 000739B7 00000000 00000000 04800001  [.9…………..]
DECE450 00000001 00260005 1F171F3D 01000190  [……&.=…….]
DECE460 00000004 00001F78 1F6C1F4B 1F5D1F44  [….x…K.l.D.].]
。。。
。。。
省略部分内容
。。。
。。。
Block header dump:  0x0100018c
Object id on Block? Y
seg/obj: 0xcd53  csc: 0x00.739b7  itc: 1  flg: E  typ: 2 – INDEX
brn: 0  bdba: 0x1000189 ver: 0x01 opc: 0
inc: 0  exflg: 0

Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0006.02c.00000102  0x00800b8f.0136.02  C—    0  scn 0x0000.000739b7

Branch block dump
=================
header address 233628748=0xdece44c
kdxcolev 1
KDXCOLEV Flags = – – –
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=— is converted=Y
kdxconco 4
kdxcosdc 1
kdxconro 5
kdxcofbo 38=0x26
kdxcofeo 7997=0x1f3d
kdxcoavs 7959
kdxbrlmc 16777616=0x1000190
kdxbrsno 4
kdxbrbksz 8056
kdxbr2urrc 0
row#0[8011] dba: 16777615=0x100018f
col 0; len 10; (10):  4a 41 56 41 20 43 4c 41 53 53
col 1; len 1; (1):  01
col 2; TERM
row#1[8044] dba: 16777613=0x100018d
col 0; len 6; (6):  4a 41 56 41 20 44
col 1; TERM
row#2[8004] dba: 16777868=0x100028c
col 0; len 1; (1):  53
col 1; TERM
row#3[8029] dba: 16777614=0x100018e
col 0; len 7; (7):  53 59 4e 4f 4e 59 4d
col 1; len 1; (1):  01
col 2; TERM
row#4[7997] dba: 16777869=0x100028d
col 0; len 1; (1):  54
col 1; TERM
—– end of branch block dump —–
End dump data blocks tsn: 4 file#: 4 minblk 396 maxblk 396

–kdxcolev表示索引层级号,这里由于我们转储的是根节点所以其层级号为1(当非叶子节点时层级也有可能为2 3 4递增)对叶子节点来说该值为0
–kdxcolok表示该索引上是否正在发生修改块结构的事务;
–kdxcoopc表示内部操作代码;
–kdxconco表示索引条目中列的数量;
–kdxcosdc表示索引结构发生变化的数量,当你修改表里的某个索引键值时,该值增加;
–kdxconro表示当前索引节点中索引条目的数量,但是注意,不包括kdxbrlmc指针;
–kdxcofbo表示当前索引节点中可用空间的起始点相对当前块的位移量;
–kdxcofeo表示当前索引节点中可用空间的最尾端的相对当前块的位移量;
–kdxcoavs表示当前索引块中的可用空间总量,也就是用kdxcofeo减去kdxcofbo得到的。
–kdxbrlmc表示分支节点的地址,该分支节点存放了索引键值小于row#0(在转储文档后半部分显示)所含有的最小值的所有节点信息;
–kdxbrsno表示最后一个被修改的索引条目号,这里看到是0,表示该索引是新建的索引;
–kdxbrbksz表示可用数据块的空间大小。实际从这里已经可以看到,即便是PCTFREE设置为0,也不能用足8192字节。
接下去看到的是row#0的内容
这部分内容就是在根节点中所记录的索引条目,总共是5个[0-4]条目。再加上kdxbrlmc所指向的第一个分支节点,
我们知道该根节点中总共存放了6个分支节点的索引条目 也对应了树形结构里nrow的值

每个索引条目都指向一个分支节点。
其中col 1表示所链接的分支节点的地址,该值经过一定的转换以后实际就是row#所在行的dba的值。
如果根节点下没有其他的分支节点,则col 1为TERM 比如上面的dba: 16777613=0x100018d的col 1; TERM

col 0表示该分支节点所链接的最小键值。其转换方式非常复杂 是要从十六进制转换为十进制
我们可以做下试验

row#0[8011] dba: 16777615=0x100018f
col 0; len 10; (10):  4a 41 56 41 20 43 4c 41 53 53
SELECT to_number(‘4a’,’XX’) FROM dual; –74
SELECT to_number(’41’,’XX’) FROM dual;–65
SELECT to_number(’56’,’XX’) FROM dual;–86
SELECT to_number(’41’,’XX’) FROM dual;–65
SELECT to_number(’20’,’XX’) FROM dual;–32
SELECT to_number(’43’,’XX’) FROM dual;–67
SELECT to_number(‘4c’,’XX’) FROM dual;–76
SELECT to_number(’41’,’XX’) FROM dual;–65
SELECT to_number(’53’,’XX’) FROM dual;–83
SELECT to_number(’53’,’XX’) FROM dual;–83
索引键值是char类型的,所以对每个值都运用chr函数就可以得到被索引键值为:
SELECT CHR(74) FROM dual;  –J
SELECT CHR(65) FROM dual;  –A
SELECT CHR(86) FROM dual;  –V
SELECT CHR(65) FROM dual;  –A
SELECT CHR(32) FROM dual;  — (空格)
SELECT CHR(67) FROM dual;  –C
SELECT CHR(76) FROM dual;  –L
SELECT CHR(65) FROM dual;  –A
SELECT CHR(83) FROM dual;  –S
SELECT CHR(83) FROM dual;  –S
–我擦 竟然是JAVA CLASS , JAVA CLASS就是dba为16777615的索引块所链接的最小键值

然后再dump一下看到结果就是我们要的10进制还没转成16进制的值
select dump(‘JAVA CLASS’) from dual;
Typ=96 Len=10: 74,65,86,65,32,67,76,65,83,83

那么我们上面dump出来了根节点 接下来继续dump叶子节点  我们就dump刚刚的16777615这个块吧。

select dbms_utility.data_block_address_file(16777615) “file”,dbms_utility.data_block_address_block(16777615) “BLOCK” from dual;
file      BLOCK
———- ———-
4        399

alter system dump datafile 4 block 399;
文件如下:
*** 2015-03-03 10:26:29.354
Start dump data blocks tsn: 4 file#: 4 minblk 399 maxblk 399
buffer tsn: 4 rdba: 0x0100018f (4/399)
scn: 0x0000.00073fd7 seq: 0x01 flg: 0x04 tail: 0x3fd70601
frmt: 0x02 chkval: 0x0703 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x0DECE400 to 0x0DED0400
DECE400 0000A206 0100018F 00073FD7 04010000  [………?……]
DECE410 00000703 00000002 0000CD53 00073FD7  [……..S….?..]
DECE420 00000000 00320002 01000189 00080004  [……2………]
…..
Block header dump:  0x0100018f
Object id on Block? Y
seg/obj: 0xcd53  csc: 0x00.73fd7  itc: 2  flg: E  typ: 2 – INDEX
brn: 0  bdba: 0x1000189 ver: 0x01 opc: 0
inc: 0  exflg: 0

Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0004.008.000000d0  0x00800041.009e.01  CB–    0  scn 0x0000.000739b2
0x02   0x0001.009.000000db  0x008002da.00be.09  C—    0  scn 0x0000.000739ba

Leaf block dump
===============
header address 233628772=0xdece464
kdxcolev 0
KDXCOLEV Flags = – – –
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=— is converted=Y
kdxconco 4
kdxcosdc 1
kdxconro 2
kdxcofbo 40=0x28
kdxcofeo 2853=0xb25
kdxcoavs 3233
kdxlespl 0
kdxlende 0
kdxlenxt 16777613=0x100018d
kdxleprv 16777616=0x1000190
kdxledsz 0
kdxlebksz 8032
row#0[3667] flag: ——, lock: 0, len=3945
col 0; len 10; (10):  4a 41 56 41 20 43 4c 41 53 53
col 1; len 6; (6):  01 00 01 db 00 b0
….
row#1[2853] flag: ——, lock: 0, len=814
col 0; len 10; (10):  4a 41 56 41 20 43 4c 41 53 53
col 1; len 6; (6):  01 00 02 3e 01 00
col 2; len 6; (6):  01 00 02 80 01 4f
col 3; len 785; (785):
cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55
55 55 ce 55 55 55 55 55 55 05 ff 18 55 55 55 55 55 55 55 55 cf 55 55 55 55
55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55
55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 ce
55 55 55 55 55 55 05 ff 18 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55
55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55
55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 ce 55 55 55
55 55 55 05 ff 18 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55
55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55
c9 55 05 ff 2d 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55
55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf
55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 ce 55 55 55 55 55 55 05
ff 18 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55
55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55
55 55 55 55 55 cf 55 55 55 55 55 55 55 55 ce 55 55 55 55 55 55 05 ff 18 55
—– end of leaf block dump —–
End dump data blocks tsn: 4 file#: 4 minblk 399 maxblk 399

发现内容与根节点完全类似,只不过该索引块中所包含的索引条目(指向叶子节点)的数量只有两个

我们再拿其中一个叶子来分析一下
row#1[2853] flag: ——, lock: 0, len=814
col 0; len 10; (10):  4a 41 56 41 20 43 4c 41 53 53
col 1; len 6; (6):  01 00 02 3e 01 00
col 2; len 6; (6):  01 00 02 80 01 4f
col 3; len 785; (785):
cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55 55 55 cf 55 55 55 55 55 55

其中flag表示标记,比如删除标记等;
而lock表示锁定信息。col 0表示索引键值,其算法与我们在前面介绍分支节点时所说的算法一致。

继续转化进制后使用chr
4a 41 56 41 20 43 4c 41 53 53
74 65 86 65 32 67 76 65 83 83
J  A  V  A     C  L  A  S  S

col 1
在b-tree的时候表示rowid
在bitmap的时候表示rowid的起始位置
01 00 01 db 00 b0
010001db00b0

SELECT to_number(‘010001db00b0′,’xxxxxxxxxxxx’) FROM dual;
select dbms_utility.data_block_address_file(to_number(‘010001DB’,’xxxxxxxxxxxx’)) “file”,
dbms_utility.data_block_address_block(to_number(‘010001DB’,’xxxxxxxxxxxx’)) “BLOCK” from dual;

4        475
col 2
在b-tree的时候没有
在bitmap中表示rowid的结束位置
01000280014f

col3表示位图编码 在b-tree里一样也没有
select to_number(‘0180001c’,’xxxxxxxxxxxx’) from dual;

中间的ca和开头的cf,不是索引的bitmap,而是类似数据头之类的东西,具体数值代表什么我还不清楚,不过似乎每64位就会出现一个数据头
SELECT
sys.pkg_number_trans.f_hex_to_bin(’55’) as c2,
from dual;

1010101

其中不足8位的前面用0补齐
–连接起来全是二进制的数据  也就代表了是不是JAVA CLASS

SELECT SYS.pkg_number_trans.f_oct_to_hex(‘01000280014f’) FROM dual;

在数据很少的时候单一块的时候很容易验证 但是数据多了之后怎么在块与块之间进行

我们来做一个单一的吧
DROP TABLE t1 PURGE;
CREATE TABLE t1
(
ID NUMBER
);
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2);
INSERT INTO t1 VALUES(3);
INSERT INTO t1 VALUES(4);
INSERT INTO t1 VALUES(3);
INSERT INTO t1 VALUES(2);
INSERT INTO t1 VALUES(1);
COMMIT;
select dbms_rowid.rowid_relative_fno(rowid)file_id, dbms_rowid.rowid_block_number(rowid)block_id,
dbms_rowid.rowid_row_number(rowid) row# from t1;

FILE_ID   BLOCK_ID       ROW#
———- ———- ———-
4        664          0
4        664          1
4        664          2
4        664          3
4        664          4
4        664          5
4        664          6

在同一个数据块上
alter session set events ‘10608 trace name context forever, level 10608’;

CREATE BITMAP INDEX IDX_B_T1 ON T1(ID);

alter session set events ‘10608 trace name context off’;

10608事件用来跟踪创建bitmap索引的过程

内容如下:
qerbiARwo: bitmap size is 8168
qerbiIPI default pctfree=10
qerbiIPI length=0
qerbiAllocate pfree=127 space=8168
qerbiStart first start
qerbiRop: rid=01000298.0000, new=Y , key: (2):  c1 02   –表示这是一个新值 产生一个的新的位图表示
qerbiCmpSz notfound pctfree=10
qerbiCmpSz adjblksize=7351 length=0
qerbiRop keysize=4 maxbm=3529
kdibcoinit(b7ddb044): srid=01000298.0000
qerbiRop: rid=01000298.0001, new=Y , key: (2):  c1 03
kdibcoinit(b7ddafc8): srid=01000298.0001
qerbiRop: rid=01000298.0002, new=Y , key: (2):  c1 04
kdibcoinit(b7ddaf4c): srid=01000298.0002
qerbiRop: rid=01000298.0003, new=Y , key: (2):  c1 05
kdibcoinit(b7ddaed0): srid=01000298.0003
qerbiRop: rid=01000298.0004, new=N, key: (2):  c1 04  –不是新值
qerbiRop: rid=01000298.0005, new=N, key: (2):  c1 03
qerbiRop: rid=01000298.0006, new=N, key: (2):  c1 02
kdibcoend(b7ddb044): erid=01000298.0007status=3
qerbiCon: key: (2):  c1 02
srid=01000298.0 erid=01000298.7 bitmap: (2):  c8 41
kdibcoend(b7ddafc8): erid=01000298.0007status=3
qerbiCon: key: (2):  c1 03
srid=01000298.0 erid=01000298.7 bitmap: (2):  c8 22
kdibcoend(b7ddaf4c): erid=01000298.0007status=3
qerbiCon: key: (2):  c1 04
srid=01000298.0 erid=01000298.7 bitmap: (2):  c8 14
kdibcoend(b7ddaed0): erid=01000298.0007status=3
qerbiCon: key: (2):  c1 05
srid=01000298.0 erid=01000298.7 bitmap: (1):  03
qerbiFreeMemory: Work heap is used.

上面是创建bitmap索引的过程。我们先把被索引的列的值换算成十六进制:

select dump(4),dump(3),dump(2),dump(1) from dual;
Typ=96 Len=2: 85,99 Typ=96 Len=3: 74,109,104
1 Typ=2 Len=2: 193,5 Typ=2 Len=2: 193,4 Typ=2 Len=2: 193,3 Typ=2 Len=2: 193,2
5 4 3 2的16进制是05 04 03 02 同时对应了上面的生成key部分的值
qerbiCon: key: (2):  c1 02
srid=01000298.0 erid=01000298.7 bitmap: (2):  c8 41    从上面的推论我们得出02表示1
start rowid和end rowid的格式分两部分,中间用点隔开,
点左边的表示文件号(从左边第一个字节开始的4个字节表示)和数据块号(从左边第五个字节开始的4个字节表示),
点右边表示数据块里的行号。这里的显示可以看到,
这几条记录都位于相同的数据块里。这里的0100表示文件号
select dbms_utility.data_block_address_file(to_number(‘01000298′,’xxxxxxxxxxxx’)) “file”,
dbms_utility.data_block_address_block(to_number(‘01000298′,’xxxxxxxxxxxx’)) “BLOCK” from dual;

4 664
根据我们刚刚的插入按照预想的应该是:1000001

得到结果果然是:
SELECT
sys.pkg_number_trans.f_hex_to_bin(’41’) as c2
from dual;
1000001

MySQL查询当前版本

1.使用Mysql -V
[root@ucjmh ~]# mysql -V
mysql Ver 14.14 Distrib 5.5.41, for Linux (i686) using readline 5.1
2.在mysql中输入命令–>status
mysql> status
————–
mysql Ver 14.14 Distrib 5.5.41, for Linux (i686) using readline 5.1

Connection id: 25
Current database:
Current user: root@localhost
SSL: Not in use
Current pager: stdout
Using outfile: ”
Using delimiter: ;
Server version: 5.5.41 MySQL Community Server (GPL)
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: latin1
Db characterset: utf8
Client characterset: utf8
Conn. characterset: utf8
UNIX socket: /var/lib/mysql/mysql.sock
Uptime: 3 hours 4 min 36 sec

Threads: 2 Questions: 161 Slow queries: 0 Opens: 78 Flush tables: 3 Open tables: 24 Queries per second avg: 0.014
3.在help中找到
[root@ucjmh ~]# mysql –help | grep Distrib
mysql Ver 14.14 Distrib 5.5.41, for Linux (i686) using readline 5.1

4.使用version()函数
mysql> select version();
+———–+
| version() |
+———–+
| 5.5.41 |
+———–+
1 row in set (0.00 sec)

Oracle SPA介绍

Sql Performance Analyzer (SPA)
这是oracle 11g的新特性 可对一部分sql结果进行性能分析 适用于数据升级 迁移做前后的比较
分析的结果会永远存在的数据库内部。和数据库重演不同的是spa只能分析sql语句且只有查询语句
步骤如下:
1.在生产库捕获具体语句
2.把捕获的语句打包传输到测试环境
3.创建SPA任务在测试环境
4.生成改变之前的执行信息
5.做改变(升级或者打补丁或者改参数或者等等。。)
6.生成改变之后的执行信息
7.对于两份信息(可生成报告文件)

CREATE USER ucjmh IDENTIFIED BY ucjmh;
GRANT DBA TO ucjmh;
conn ucjmh/ucjmh –创建一个用户
–建几张表
CREATE TABLE uc_objects AS SELECT * FROM dba_objects;
CREATE TABLE uc_tables AS SELECT * FROM dba_tables;
CREATE TABLE uc_users AS SELECT * FROM dba_users;
–做一些查询
select /*UCJMH*/ * from uc_objects;
select /*UCJMH*/ * from uc_tables;
select /*UCJMH*/ * from uc_users;
–生成一个sqlset
begin
sys.dbms_sqltune.create_sqlset(
sqlset_name => ‘uc_sts’,
sqlset_owner => ‘UCJMH’);
end;
/

PL/SQL procedure successfully completed.

–查看一下是否生成成功
SQL> select name from user_sqlset;

NAME
——————————
uc_sts

–把需要的sql放到这个sqlset里
declare
stscur dbms_sqltune.sqlset_cursor;
begin
open stscur for
select value(P) from table(
dbms_sqltune.select_cursor_cache(
‘sql_text like ”select /*UCJMH*/%”’,
null,null,null,null,null,null,’ALL’)) P;
dbms_sqltune.load_sqlset(
sqlset_name =>’uc_sts’, –这里是区分大小写的 如果你上面写的小写 不要想当然认为ORACLE会内部转换成大写
populate_cursor => stscur,
sqlset_owner =>’UCJMH’);
end;

PL/SQL procedure successfully completed

SQL> select sql_text from user_sqlset_statements where sqlset_name=’uc_sts’;

SQL_TEXT
——————————————————————————–
select /*UCJMH*/ * from uc_objects
select /*UCJMH*/ * from uc_users
select /*UCJMH*/ * from uc_tables

–或者
SQL> select sql_Text from table(dbms_sqltune.select_sqlset(‘uc_sts’));

SQL_TEXT
——————————————————————————–
select /*UCJMH*/ * from uc_objects
select /*UCJMH*/ * from uc_users
select /*UCJMH*/ * from uc_tables

创建暂存表保存STS:
exec DBMS_SQLTUNE.CREATE_STGTAB_SQLSET(‘UC_STS_TAB’,’UCJMH’);

SQL> select count(*) from uc_sts_tab;

COUNT(*)
———-
0
–上面的步骤只是创建了一个用来暂存set的表 但是并没有真的已经把sql放进去
–把STS加载到暂存表里:
exec DBMS_SQLTUNE.PACK_STGTAB_SQLSET(‘uc_sts’,’UCJMH’,’UC_STS_TAB’,’UCJMH’);

PL/SQL procedure successfully completed.

SQL> select count(*) from uc_sts_tab;

COUNT(*)
———-
6

–不是应该是3条sql吗 问什么是6行记录呢?仔细看看可以发现每个sql_id占两行 一行是sql 一行是统计信息和执行记划等信息

把暂存表导出生产环境 导入测试环境(我这里测试环境没有东西 我直接把整个用户导过去 如果你们其它表已经存在了可以只导一张暂存表)

create or replace directory dmp as ‘/home/oracle/dmp’;
EXIT
expdp ucjmh/ucjmh directory=dmp dumpfile=uc.dmp
export ORACLE_SID=emrep
sqlplus / AS SYSDBA
create or replace directory dmp as ‘/home/oracle/dmp’;
EXIT
impdp system/oracle directory=dmp dumpfile=uc.dmp

把STS从暂存表里放入数据字典里:
exec DBMS_SQLTUNE.UNPACK_STGTAB_SQLSET(‘uc_sts’,’UCJMH’,TRUE,’UC_STS_TAB’,’UCJMH’); –true表示如果有的话是否替换

PL/SQL procedure successfully completed.

–创建一个spa任务
SQL> var tname varchar2(20);
SQL> exec :tname:= dbms_sqlpa.create_analysis_task(sqlset_name => ‘uc_sts’, task_name => ‘MYSPA’);

PL/SQL procedure successfully completed.

–执行任务从而构建before change data
SQL> exec dbms_sqlpa.execute_analysis_task(task_name => :tname, execution_type => ‘TEST EXECUTE’, execution_name => ‘before’);

PL/SQL procedure successfully completed.

–生成before change的报表
SQL> set long 99999
SQL> SELECT dbms_sqlpa.report_analysis_task(task_name => :tname, type=>’text’, section=>’summary’) FROM dual;

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
GENERAL INFORMATION SECTION
——————————————————————————-
Tuning Task Name : MYSPA
Tuning Task Owner : UCJMH
Workload Type : SQL Tuning Set
Execution Count : 1
Current Execution : before
Execution Type : TEST EXECUTE
Scope : COMPREHENSIVE
Completion Status : COMPLETED
Started at : 11/01/2014 19:27:01

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
Completed at : 11/01/2014 19:27:01
SQL Tuning Set (STS) Name : uc_sts
SQL Tuning Set Owner : UCJMH
Number of Statements in the STS : 3
Number of SQLs Analyzed : 3
Number of SQLs in the Report : 3

——————————————————————————-
SUMMARY SECTION
——————————————————————————-
SQL Statements Ordered by Elapsed Time

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
——————————————————————————-
Parse Elapsed CPU Buffer Optimizer
object ID SQL ID Time (s) Time (s) Time (s) Gets Cost
———- ————- ——– ——– ——– ——– ———
3 5d0v1n7gt8j2y .014787 .016058 .015664 1013 284
5 g9shuu1cxbqqz .006716 .001369 .001333 76 23
4 71tf6tnk513p5 .002394 .000084 .000111 3 3

——————————————————————————-

–做你要做的变化,比如修改参数、升级硬件、升级操作系统等等。
SQL> exec dbms_stats.gather_schema_stats(USER);

PL/SQL procedure successfully completed.
构建after change data
SQL> EXEC dbms_sqlpa.execute_analysis_task(task_name => :tname, execution_type => ‘TEST EXECUTE’, execution_name => ‘after’);

PL/SQL procedure successfully completed.

生成after change的报表
SQL> SELECT dbms_sqlpa.report_analysis_task(task_name => :tname,type=>’text’, section=>’summary’) FROM dual;

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
GENERAL INFORMATION SECTION
——————————————————————————-
Tuning Task Name : MYSPA
Tuning Task Owner : UCJMH
Workload Type : SQL Tuning Set
Execution Count : 2
Current Execution : after
Execution Type : TEST EXECUTE
Scope : COMPREHENSIVE
Completion Status : COMPLETED
Started at : 11/01/2014 19:30:29

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
Completed at : 11/01/2014 19:30:29
SQL Tuning Set (STS) Name : uc_sts
SQL Tuning Set Owner : UCJMH
Number of Statements in the STS : 3
Number of SQLs Analyzed : 3
Number of SQLs in the Report : 3

——————————————————————————-
SUMMARY SECTION
——————————————————————————-
SQL Statements Ordered by Elapsed Time

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
——————————————————————————-
Parse Elapsed CPU Buffer Optimizer
object ID SQL ID Time (s) Time (s) Time (s) Gets Cost
———- ————- ——– ——– ——– ——– ———
7 5d0v1n7gt8j2y .000147 .017201 .016886 1012 284
9 g9shuu1cxbqqz .000244 .001269 .001444 75 23
8 71tf6tnk513p5 .000423 .000064 0 2 3

——————————————————————————-

–比较before和after
EXEC dbms_sqlpa.execute_analysis_task(task_name => :tname, execution_type => ‘COMPARE PERFORMANCE’);

–生成分析报表
SQL> SELECT dbms_sqlpa.report_analysis_task(task_name => :tname, type=>’text’, section=>’summary’) FROM dual;

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
GENERAL INFORMATION SECTION
——————————————————————————-
Tuning Task Name : MYSPA
Tuning Task Owner : UCJMH
Workload Type : SQL Tuning Set
Execution Count : 2
Current Execution : after
Execution Type : TEST EXECUTE
Scope : COMPREHENSIVE
Completion Status : COMPLETED
Started at : 11/01/2014 19:30:29

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
Completed at : 11/01/2014 19:30:29
SQL Tuning Set (STS) Name : uc_sts
SQL Tuning Set Owner : UCJMH
Number of Statements in the STS : 3
Number of SQLs Analyzed : 3
Number of SQLs in the Report : 3

——————————————————————————-
SUMMARY SECTION
——————————————————————————-
SQL Statements Ordered by Elapsed Time

DBMS_SQLPA.REPORT_ANALYSIS_TASK(TASK_NAME=>:TNAME,TYPE=>’TEXT’,SECTION=>’SUMMARY’
——————————————————————————–
——————————————————————————-
Parse Elapsed CPU Buffer Optimizer
object ID SQL ID Time (s) Time (s) Time (s) Gets Cost
———- ————- ——– ——– ——– ——– ———
7 5d0v1n7gt8j2y .000147 .017201 .016886 1012 284
9 g9shuu1cxbqqz .000244 .001269 .001444 75 23
8 71tf6tnk513p5 .000423 .000064 0 2 3

——————————————————————————-

删除任务:
select * from DBA_SQLSET_REFERENCES where SQLSET_NAME=’uc_sts’;
exec dbms_sqltune.drop_tuning_task(‘MYSPA’);

删除暂存表和STS:
drop table hr.hsj_sts_tab purge;
exec dbms_sqltune.drop_sqlset(‘uc_sts’);

Oracle UTL_FILE

With the UTL_FILE package, PL/SQL programs can read and write operating system text files. UTL_FILE provides a restricted version of operating system stream file I/O.
FOPEN
描述:打开一个文件,基本上在我们对文件进行读写动作之前都需要先执行这个function来打开文件先。
语法:
UTL_FILE.FOPEN (  location IN VARCHAR2,
filename IN VARCHAR2,
open_mode IN VARCHAR2,
max_linesize IN BINARY_INTEGER DEFAULT 1024)  RETURN FILE_TYPE;
参数:location   略。
Filename  略。
open_mode  指明文件打开的模式。有如下几种:
■r –只读(文本)
■ w – 只写(本文)
■ a – 追加(文本)
■ rb – 只读(字节)
■ wb – 只写(字节)
■ ab – 追加(字节)
(注:当使用模式:a或者ab的时候,如果文件不存在,则会以write模式创建此文件)
max_linesize   指定文件文本每一行存放的最大字符数。
返回值:FOPEN返回一个接下来我们的程序将要使用到的文件的指针

FCLOSE
功能:关闭一个打开的文件。
语法:UTL_FILE.FCLOSE (file IN OUT FILE_TYPE);
参数:1. file->调用FOPEN或者FOPEN_NVCHAR返回的活动中的文件指针。
注意事项:当FCLOSE执行的时候,如果还有缓冲数据没有及时写入到文件中,
那么程序就会raise一个异常:WRITE_ERROR。可以在PUT_LINE的时候加上参数autoflush => TRUE;或者在每次PUT之后执行:FFLUSH。

FCLOSE_ALL
功能:此procedure将会关闭本次session所有打开的文件。它用来紧急情况的清理功能,例如当PL/SQL程序在EXCEPTION部分退出时。
语法:UTL_FILE.FCLOSE_ALL;
注意事项:FCLOSE_ALL不会修改所打开的文件的状态,也就是说执行了FCLOSE_ALL后,再用IS_OPEN去检测文件,结果还是打开状态,
但是之后,这些文件任然是不能去read或者write的。而FCLOSE执行后,相关的文件则完全关闭了

FCOPY
功能:此procedure复制一个文件的连续部分内容或者全部内容到一个新创建的文件。
如果参数start_line和end_line省略的话,默认地会复制整个文件。此操作会将源文件以read模式打开,将目标文件以write模式打开。
语法:
UTL_FILE.FCOPY ( src_location    IN VARCHAR2,
src_filename  IN VARCHAR2,
dest_location  IN VARCHAR2,
dest_filename IN VARCHAR2,
start_line         IN BINARY_INTEGER DEFAULT 1,
end_line          IN BINARY_INTEGER DEFAULT NULL);
参数:src_location来源文件的目录名。取值来源是视图ALL_DIRECTORIES的DIRECTORY_NAME;
src_filename  将要被复制的来源文件
dest_location 被创建的目标文件存放的目录名。
dest_filename 从来源文件创建的目标文件。
start_line  要复制的内容起始行号,默认为1,表示从第一行开始复制。
end_line 要复制的内容的终止行号,默认NULL,表示文件的末尾。
FFLUSH
描述:FFLUSH强制将缓冲的数据写入文件。因为通常待写入文件的数据都是都在缓冲存储位置。当有必要去read一个任然处于打开状态的文件时,
FFLUSH就起作用了,例如在调试程序中,可以将调试的消息及时冲到文件中,已便于我们马上就能read这些内容。
语法:
UTL_FILE.FFLUSH (file IN FILE_TYPE);

FGETATTR
描述:FGETATTR读取磁盘上的文件并返回文件的属性。
语法:UTL_FILE.FGETATTR( location IN VARCHAR2,
filename IN VARCHAR2,
fexists OUT BOOLEAN,
file_length OUT NUMBER,
block_size OUT BINARY_INTEGER);
参数:location 此处略去X个字。
filename此处略去X个字。
fexists 返回的属性1:文件是否存在
file_length 返回的属性2:文件字节长度,如果文件不存在,则返回NULL。
block_size  文件系统块的字节大小。

FGETPOS
描述:此函数返回一个文件中当前的偏移位置。
语法:
UTL_FILE.FGETPOS (file IN FILE_TYPE) RETURN PLS_INTEGER;
注意事项:如果file没有打开,则会抛出异常

FREMOVE
描述:此procedure在你有充足的权限之下,删除一个磁盘上的文件。
语法:
UTL_FILE.FREMOVE ( location IN VARCHAR2,
filename IN VARCHAR2);

FRENAME
描述:此procedure将一个存在的文件重命名,类似unix命令:mv
语法:
UTL_FILE.FRENAME ( src_location   IN VARCHAR2,
src_filename  IN VARCHAR2,
dest_location  IN VARCHAR2,
dest_filename IN VARCHAR2,
overwrite        IN BOOLEAN DEFAULT FALSE);

GET_LINE
描述:此procedure从一个打开的文件中读取一行文本,直到遇到换行符。
语法:
UTL_FILE.GET_LINE ( file     IN FILE_TYPE,
buffer OUT VARCHAR2,
len      IN PLS_INTEGER DEFAULT NULL);
参数:len 从文本中读取一次的长度,默认是null,oracle就取FOPEN时的max_linesieze。

IS_OPEN
描述:顾名思义。
语法:UTL_FILE.IS_OPEN (file IN FILE_TYPE)  RETURN BOOLEAN;
1.PUT
描述:PUT写入内容到文件中。(每写一次,不带换行符)
语法:UTL_FILE.PUT (file IN FILE_TYPE, buffer IN VARCHAR2);

PUT_LINE
描述:PUT_LINE写入内容到文件中。(每写一次,末尾都加一个换行符)
语法:
UTL_FILE.PUT_LINE ( file          IN FILE_TYPE,
buffer      IN VARCHAR2,
autoflush IN BOOLEAN DEFAULT FALSE);
PUTF
描述:    写入格式化的内容到文件中。好比C语言的printf()
语法:
UTL_FILE.PUTF ( file IN FILE_TYPE,
format IN VARCHAR2,
[arg1 IN VARCHAR2 DEFAULT NULL,
. . .
arg5 IN VARCHAR2 DEFAULT NULL]);
参数:format 包含格式化字符[\n,%s]的内容。
\n:代表一个换行符。
%s:用arg1~5的值去代替。

 

 

做一个例子如下:

CREATE OR REPLACE PROCEDURE SQL_TO_CSV
(
P_QUERY IN VARCHAR2,                        — PLSQL文
P_DIR IN VARCHAR2,                          — 导出的文件放置目录
P_FILENAME IN VARCHAR2                      — CSV名
)
IS
L_OUTPUT UTL_FILE.FILE_TYPE;
L_THECURSOR INTEGER DEFAULT DBMS_SQL.OPEN_CURSOR;
L_COLUMNVALUE VARCHAR2(4000);
L_STATUS INTEGER;
L_COLCNT NUMBER := 0;
L_SEPARATOR VARCHAR2(1);
L_DESCTBL DBMS_SQL.DESC_TAB;
P_MAX_LINESIZE NUMBER := 32000;
BEGIN
–OPEN FILE
L_OUTPUT := UTL_FILE.FOPEN(P_DIR, P_FILENAME, ‘A’, P_MAX_LINESIZE);
–DEFINE DATE FORMAT
EXECUTE IMMEDIATE ‘ALTER SESSION SET NLS_DATE_FORMAT=”YYYY-MM-DD HH24:MI:SS”’;
–OPEN CURSOR
DBMS_SQL.PARSE( L_THECURSOR, P_QUERY, DBMS_SQL.NATIVE );
DBMS_SQL.DESCRIBE_COLUMNS( L_THECURSOR, L_COLCNT, L_DESCTBL );
–DUMP TABLE COLUMN NAM
FOR I IN 1 .. L_COLCNT LOOP
UTL_FILE.PUT( L_OUTPUT, L_SEPARATOR || ‘”’ || L_DESCTBL(I).COL_NAME || ‘”’ );
DBMS_SQL.DEFINE_COLUMN( L_THECURSOR, I, L_COLUMNVALUE, 4000 );
L_SEPARATOR := ‘,’;
END LOOP;
UTL_FILE.NEW_LINE( L_OUTPUT );
–EXECUTE THE QUERY STATEMENT
L_STATUS := DBMS_SQL.EXECUTE(L_THECURSOR);
–DUMP TABLE COLUMN VALUE
WHILE ( DBMS_SQL.FETCH_ROWS(L_THECURSOR) > 0 ) LOOP
L_SEPARATOR := ”;
FOR I IN 1 .. L_COLCNT LOOP
DBMS_SQL.COLUMN_VALUE( L_THECURSOR, I, L_COLUMNVALUE );
UTL_FILE.PUT( L_OUTPUT, L_SEPARATOR || ‘”’ ||
TRIM(BOTH ‘ ‘ FROM REPLACE(L_COLUMNVALUE,’”’,’””’)) || ‘”’);
L_SEPARATOR := ‘,’;
END LOOP;
UTL_FILE.NEW_LINE( L_OUTPUT );
END LOOP;
–CLOSE CURSOR
DBMS_SQL.CLOSE_CURSOR(L_THECURSOR);
–CLOSE FILE
UTL_FILE.FCLOSE( L_OUTPUT );
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
/

— 创建存放文件的目录
CREATE OR REPLACE DIRECTORY MYDIR AS ‘C:\’;

create table t as select * from dba_users;

— 执行块
begin
sql_to_csv(‘select * from T ‘,’MYDIR’,’EXAMPLE2.CSV’);
dbms_lock.sleep(10) ;
sql_to_csv(‘select * from T ‘,’MYDIR’,’EXAMPLE3.CSV’);
end;
/

Limit SQL*Plus operation

SQL*Plus Security
限制哪些用户在SQL*Plus里能够执行哪些命令以及不能执行哪些命令

oracle是用system用户下的PRODUCT_USER_PROFILE (PUP)表来进行的限制。提供了产品级别的安全,用户级安全(SQL GRANT和REVOKE命令和用户角色)相互补充。

select * from product_user_profile;
如果这张表不存在可以使用  @?/sqlplus/admin/pupbld
表结构如下:
SQL> desc product_user_profile
Name                     Type
——————— —————
PRODUCT                 VARCHAR2(30)                  –必须包含产品的名称(SQL * Plus)。不能输入通配符或NULL
USERID                  VARCHAR2(30)                  –必须包含用户的用户名(大写)来说,你想禁用命令。为多个用户禁用命令,使用SQL通配符(%)或多个条目 HR,HR1  HR%  %
ATTRIBUTE               VARCHAR2(240)                 –必须包含的名称(大写)SQL、SQL * Plus或者PL / SQL命令禁用(例如,RUN)   如果你禁用一个角色,他可以是这个角色的字符串
SCOPE                   VARCHAR2(240)                 –空就好了
NUMERIC_VALUE           NUMBER(15,2)                  –空就好了
CHAR_VALUE              VARCHAR2(240)                 –禁用一个SQL、SQL * Plus,或PL / SQL命令是”DISABLED”  如果你禁用一个角色,它必须包含角色的名字  不可以使用通配符(%)
DATE_VALUE              DATE                          –空就好了
LONG_VALUE              LONG                          –空就好了

SQL*Plus   HR      HOST                           DISABLED
SQL*Plus   %       INSERT                         DISABLED
SQL*Plus   %       UPDATE                         DISABLED
SQL*Plus   %       DELETE                         DISABLED

insert into product_user_profile(PRODUCT,USERID,ATTRIBUTE,CHAR_VALUE )values(‘SQL*Plus’,’HR’,’HOST’,’DISABLED’);
insert into product_user_profile(PRODUCT,USERID,ATTRIBUTE,CHAR_VALUE )values(‘SQL*Plus’,’%’,’INSERT’,’DISABLED’);
insert into product_user_profile(PRODUCT,USERID,ATTRIBUTE,CHAR_VALUE )values(‘SQL*Plus’,’%’,’UPDATE’,’DISABLED’);
insert into product_user_profile(PRODUCT,USERID,ATTRIBUTE,CHAR_VALUE )values(‘SQL*Plus’,’%’,’DELETE’,’DISABLED’);
COMMIT;

–Disabling HOST disables the operating system alias for HOST, such as $ on Windows, and ! on UNIX
–不推荐禁用退出/退出。如果禁用,终止命令行会话通过发送一个EOF字符如Ctrl + D在UNIX或Ctrl + Z在Windows。终止一个Windows的GUI和文件>退出会话。否则,终止会话终止的SQL * Plus流程。
–如果禁用,使用退出/退出终止当前运行的脚本iSQL *加上也禁用。如果禁用,退出操作只要OSERROR每当SQLERROR也禁用。
–当禁用了help的时候同时也禁用了?
–禁用SQL * Plus set 命令也禁用SET CONSTRAINTS, SET ROLE and SET TRANSACTION
–禁用SQL * Plus start也禁用@ / @@
–禁用BEGIN 和 DECLARE 并不妨碍使用SQL * Plus  EXECUTE  去 运行PL / SQL。EXECUTE必须单独禁用
SQL*PLUS 可以限制的命令有:
ACCEPT   ,DEFINE ,PASSWORD ,SHUTDOWN     ,APPEND
DEL      ,PAUSE  ,SPOOL    ,ARCHIVE LOG  ,DESCRIBE
PRINT    ,START (@, @@)
ATTRIBUTE       ,DISCONNECT         ,PROMPT                          ,STARTUP
BREAK           ,EDIT               ,RECOVER                         ,STORE
BTITLE          ,EXECUTE            ,REMARK                          ,TIMING
CHANGE          ,EXIT/QUIT          ,REPFOOTER                       ,TTITLE
CLEAR           ,GET                ,REPHEADER                       ,UNDEFINE
COLUM           ,NHELP (?)          ,RUN                             ,VARIABLE
COMPUTE         ,HOST
SAVE            ,WHENEVER OSERROR
CONNECT  ,INPUT           ,SET      ,WHENEVER SQLERROR
COPY     ,LIST (;)        ,SHOW     ,XQUERY

SQL命令可以限制的有:
ALTER    ,DELETE      , MERGE   ,  SET CONSTRAINTS
ANALYZE  ,DISASSOCIATE, NOAUDIT ,  SET ROLE
ASSOCIATE,DROP        , PURGE   ,  SET TRANSACTION
AUDIT    ,EXPLAIN     , RENAME  ,  TRUNCATE
CALL     ,FLASHBACK   , REVOKE  ,  UPDATE
COMMENT  ,GRANT       , ROLLBACK,  VALIDATE
COMMIT   ,INSERT      , SAVEPOINT, CREATE,LOCK, SELECT

pl/sql可以限制的有
BEGIN    DECLARE
我们做个例子:

1.登录到system
conn system/oracle
2.添加禁用
INSERT INTO PRODUCT_USER_PROFILE
VALUES (‘SQL*Plus’, ‘HR’, ‘SELECT’, NULL, NULL, ‘DISABLED’, NULL, NULL);
3.登录到hr并查询
SQL> conn hr/hr
Connected.
SQL> select * from dual;
SP2-0544: Command “select” disabled in Product User Profile
SQL> ho ls
SP2-0544: Command “host” disabled in Product User Profile
4.在SYSTEM里去掉刚刚的限制
DELETE FROM PRODUCT_USER_PROFILE WHERE USERID = ‘HR’;

但是在3的时候我们如果用PL/SQL DEVELOPER或者其它的IDE工具是没有任何问题的
当然 PUP也可以在角色上进行限制:
可以使用SQL命令来创建和控制角色为你提供安全访问数据库表。通过创建一个角色,然后控制谁有权访问它,确保只有特定的用户可以访问特定的数据库特权
创建两个role
–一个不带密码
create role ROLE1;
–一个带密码
create role role2 identified by oracle;

grant create session,create table,create view to ROLE1;

grant create session,create table,create view,create procedure to role2;

create user ucjmh identified by ucjmh;

grant ROLE1,ROLE2 to ucjmh;

–修改ucjmh用户的缺省角色,只让role1在登陆的时候生效

alter user ucjmh default role role1;
select * from product_user_profile
然后insert
insert into product_user_profile(PRODUCT,USERID,ATTRIBUTE,CHAR_VALUE )values(‘SQL*Plus’,’%’,’ROLES’,’ROLE1′);
这个时候登录:
SP2-0557: Error in disabling roles in product user profile.
Connected.

这个时候 我们再insert role2
insert into product_user_profile(PRODUCT,USERID,ATTRIBUTE,CHAR_VALUE )values(‘SQL*Plus’,’UCJMH’,’ROLES’,’ROLE2′);

SQL> conn ucjmh/ucjmh
Connected.
SQL> select * from session_roles;

no rows selected

然后可以看出来其实在登录的时候oracle内部做了:
SET ROLE ALL EXCEPT ROLE1, ROLE2;

DBMS_ERRLOG.CREATE_ERROR_LOG记录错误日志

DBMS_ERRLOG包提供了一个程序,使您可以创建一个错误日志表,
以便DML操作后可以继续遇到错误而不是终止和回滚。这使您能够节省时间和系统资源。

–首先创建一张测试表 A
CREATE TABLE a (ID NUMBER PRIMARY KEY);

BEGIN
— DBMS_ERRLOG.create_error_log(dml_table_name => ‘A’);
DBMS_ERRLOG.create_error_log(dml_table_name => ‘A’,err_log_table_name => ‘A_LOG’);
END;

–这个时候去查 可以发现多出来了表ERR$_A和A_LOG
SELECT * FROM tab

–两张表的表结构完全相同
SELECT * FROM ERR$_A;
SELECT * FROM A_LOG;

ORA_ERR_NUMBER$        NUMBER           –错误记录数
ORA_ERR_MESG$          VARCHAR2(2000)   –错误信息
ORA_ERR_ROWID$         ROWID            –错误记录的rowid(仅对update或delete)
ORA_ERR_OPTYP$         VARCHAR2(2)      –操作类型(I-INSERT,U-UPDATE,D-DELETE)
ORA_ERR_TAG$           VARCHAR2(2000)   –自定义标志说明
A_ID                    VARCHAR2(4000)   –原表字段

其中前五个字段是固定的,后边字段根据原表确定.
但是原表和对应error log表的字段类型是不完全匹配的,
其转换规则如下:
NUMBER          –>         VARCHAR2(4000)
CHAR/VARCHAR2(n)    –>     VARCHAR2(4000)
NCHAR/NVARCHAR2(n)   –>      NVARCHAR2(4000)
DATE/TIMESTAMP       –>          VARCHAR2(4000)
RAW                –>         RAW(2000)
ROWID              –>           UROWID
LONG/LOB     –>     LOB

DBMS_ERRLOG.CREATE_ERROR_LOG (
dml_table_name            IN VARCHAR2,–DML的名称表基础上的错误日志表
err_log_table_name        IN VARCHAR2 := NULL,–您将创建错误日志表的名称。默认的是(最多25字符)DML表前缀的名称’ERR$_‘。例子如下:dml_table_name: ‘EMP’, err_log_table_name: ‘ERR$_EMP’
err_log_table_owner       IN VARCHAR2 := NULL,–错误日志表的所有者的名称。您可以指定dml_table_name的所有者
err_log_table_space       IN VARCHAR2 := NULL,–错误日志表将被创建的表空间。如果没有指定,默认表空间为用户拥有将使用DML错误日志表
skip_unsupported          IN BOOLEAN := FALSE);–设置为TRUE时,不支持的列类型错误日志将被跳过,而不是添加到错误日志表。设置为FALSE时,一个不受支持的列类型将导致程序终止。

INSERT INTO a VALUES(1) LOG ERRORS
–INTO A_LOG  可以指定到自己的表里 也可以不写 默认就是ERR$_的
REJECT LIMIT UNLIMITED; –当insert into多行记录时 最多可以容忍多少错误。

–当然 直接这样也是可以的
INSERT INTO a VALUES(1) LOG ERRORS

Oracle包、函数、过程的加密和解密

首先我们来看一下oracle对包、函数、过程的加密

oracle内部提供了两种加密方式

我们先来看第一种:

在没有内置OCI之前,ORACLE只提供WRAP操作系统工具。

我们来简单的看一个例子:

SQL> ho

[oracle@ucjmh ~]$ vi function.sql

SQL> ho cat function.sql

create or replace function dl2ml(hy in varchar)

return varchar

as

x varchar(2);

begin

select

case

when hy >=’01’ and hy<= ’05’ then 1

when hy >=’06’ and hy<= ’11’ then 2

when hy >=’13’ and hy<= ’43’ then 3

when hy >=’44’ and hy<= ’46’ then 4

when hy >=’47’ and hy<= ’50’ then 5

when hy >=’51’ and hy<= ’59’ then 6

when hy >=’60’ and hy<= ’62’ then 7

when hy  =’63’ or  hy = ’65’ then 8

when hy >=’66’ and hy<= ’67’ then 9

when hy >=’68’ and hy<= ’71’ then 10

when hy >=’72’ and hy<= ’72’ then 11

when hy >=’73’ and hy<= ’74’ then 12

when hy >=’75’ and hy<= ’78’ then 13

when hy >=’79’ and hy<= ’81’ then 14

when hy >=’82’ and hy<= ’83’ then 15

when hy >=’84’ and hy<= ’84’ then 16

when hy >=’85’ and hy<= ’87’ then 17

when hy >=’88’ and hy<= ’92’ then 18

when hy >=’93’ and hy<= ’98’ then 19

else null

end

into x from dual;

return x;

end;

/

我们首先创始一个文件 里面是一个简单的函数

然后使用操作系统级别的命令进行加密

SQL> @function.sql

Function created.

SQL> ho

[oracle@ucjmh ~]$ wrap iname=function.sql oname=function.pld

PL/SQL Wrapper: Release 11.2.0.3.0- Production on Fri May 01 00:31:44 2015

Copyright (c) 1993, 2009, Oracle.  All rights reserved.

Processing function.sql to function.pld

[oracle@ucjmh ~]$ cat function.pld

create or replace function dl2ml wrapped

a000000

369

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

8

335 199

ndEQSpIvhV202FWf2jLyxpFnjQMwg1zrr0hGyo5EUOI6GMTHyUnF4S3rRsXrfy11DmGfmkPS

tcQ9Nue/OhW9vrvTuRMesVKf0vjY+UGcxijwSrxfuyzUD1jEHovrR0cVM1dx5fbGGc3GfbTg

SQVk2HhtAZooHn1TQ80riTTKhkALI+3MzBisPE7I1t7HakJ8XEgwkBPoy+j/Luup3tbe3YVo

42UE9/37WALL6NcuuovoO6z3+lA4SokDsMd7LmhMrwBLEtXWs3DDSJGKB5yt8ZQOBOOE69Oj

iS6iaQ2OqcOiUA13rQk+tok7yrcY0NRreURk6AkbTbo1G5T2BtMv0hpeRGlTQJ71g9yC2mKn

aN8uR0U38EiQ3rCYTyTetEd3lapLv8FC/psoXEq+ww==

/

[oracle@ucjmh ~]$ exit

exit

SQL> @funtion.pld

SP2-0310: unable to open file “funtion.pld”

SQL> @function.pld

Function created.

SQL> select dl2ml(’45’) from dual;

DL2ML(’45’)

——————————————————————————–

4

SQL> select text from user_source where name=’DL2ML’;

TEXT

——————————————————————————–

function dl2ml wrapped

a000000

369

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

8

335 199

ndEQSpIvhV202FWf2jLyxpFnjQMwg1zrr0hGyo5EUOI6GMTHyUnF4S3rRsXrfy11DmGfmkPS

tcQ9Nue/OhW9vrvTuRMesVKf0vjY+UGcxijwSrxfuyzUD1jEHovrR0cVM1dx5fbGGc3GfbTg

SQVk2HhtAZooHn1TQ80riTTKhkALI+3MzBisPE7I1t7HakJ8XEgwkBPoy+j/Luup3tbe3YVo

42UE9/37WALL6NcuuovoO6z3+lA4SokDsMd7LmhMrwBLEtXWs3DDSJGKB5yt8ZQOBOOE69Oj

iS6iaQ2OqcOiUA13rQk+tok7yrcY0NRreURk6AkbTbo1G5T2BtMv0hpeRGlTQJ71g9yC2mKn

aN8uR0U38EiQ3rCYTyTetEd3lapLv8FC/psoXEq+ww==

接下来我们再来看第二种加密方式:

在ORACLE 10.2之后,ORACLE提供了内置的OCI,为用户提供代码加密,其中,DBMS_DDL.WRAP和DBMS_DDL.CREATE_WRAPPED分别提供了加密、加密并且创建功能

还是来看个例子:

DBMS_DDL.WRAP使用示例

1:文本加密

select dbms_ddl.wrap(‘create or replace procedure TESTPRO is begin null end;’) from dual;

DBMS_DDL.WRAP(‘CREATEORREPLACEPROCEDURETESTPROISBEGINNULLEND;’)

————————————————————–

create or replace procedure TESTPRO wrapped

a000000

369

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

7

25 59

8g4YR51zp6Rm0t2Q5m7K9QrNB6wwg5nnm7+fMr2ywFznUrLLveeb6qV0K7jAMv7SXqWZgQjM

uIHHLcmmpmq0vfo=

2:已经创建过的代码对象加密

首先先创建一个过程

SQL> create or replace procedure TESTPRO1 is

V_str   varchar2(100);

begin

v_str:=’This is  string.’;

dbms_output.put_line(v_str);

end TESTPRO1;

/

Procedure created.

然后 从数据库获取源码 并且加密

SQL> DECLARE

l_source  sys.DBMS_SQL.VARCHAR2A;

l_wrap    sys.DBMS_SQL.VARCHAR2A;

BEGIN

SELECT case line

when 1 then ‘Create or replace ‘||Type|| ‘ ‘||text

else TEXT

end case  bulk collect into l_source from dba_source where name=’TESTPRO1’ ORDER BY LINE;

 

l_wrap := SYS.DBMS_DDL.WRAP(ddl => l_source,

lb  => 1,

ub  => l_source.count);

FOR i IN 1 .. l_wrap.count LOOP

DBMS_OUTPUT.put_line(l_wrap(i));

END LOOP;

END;

/  2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17

Create or replace PROCEDURE procedure wrapped

a000000

369

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

7

ab

c6

wET87k8GVIxXUUglEZEexfhS/xAwg5nnm7+fMr2ywFwWFpeu3C4+YvJc51Kyy73nm3jDpXSL

CabWS86PyMovzQRdiu4EkRo4EXAO3rL39cAy/tLW5YQJeQEKHPH3k9im

op6VbOlYQikj/DyF

l3Fuz9aGc4SE4eaE0qlXGcbrclwd2JTJwqwKT5lofzx0pkJtxUQ=

PL/SQL procedure successfully completed.

注:

dbms_sql.varchar2a提供最大为每行32k的支持

dbms_sql.varchar2s提供每行256字节的支持

ORACLE 10.2.0.1可能会出现22921 bug.

dbms_ddl.create_wrapped同上一样,只是形成加密之后,进行创建

这是oracle提供的两种加密的方式  但是o并没有提供解密的方式 o在rdbms目录下也有大量的pld的文件 可能是不想破解吧

但是聪明的使用者已经研究出来了怎么破解  我们来看一下:

首先我们要明白 oracle是怎么加密的

Oracle加密的原理就是先对源码进行lz压缩lzstr,然后对压缩数据进行SHA-1运算得到40位的加密串shstr,然后将加密串与压缩串拼接得到shstr+lzstr,然后对拼接后的字符串进行Oracle双字符转换(转换表)。最后将转换后的字符串进行base64编码,最终得到wrap的加密串。

既然我们通过SQL可以这样对某过程做DBMS_DDL.WRAP加密可以得到密文

那么对这部份密文的正文部份进行BASE64解码的值与未加密正文直接进行LZ压缩后的值必然是一一对应的,且两个串的长度也是相等的对密文

进行BASE64解码后,将对应的密文的正文部份按字节替换成替换表中预先算出来的字节,最后直接按LZ算法进行解压,

替换表正确的情况下,明文就应该出来了。通过这种假设,肯定就能得到替换表,替换表是按字

节来计算的,所以应该有二个列,其中一列代表BASE64解码后的字节值(十六进制00到FF),另一列代表替换列(BASE64列不能出现重复值是一个前提)。

BASE64编码地球人都知道,在ORACLE中有现存的工具包进行编码和解码,我们将用到BASE64的解码,具体包是:sys.utl_encode.base64_decode。用的时候还需要另一个过程来将字符串转换为RAW格式:sys.utl_raw.cast_to_raw

LZ压缩很常见,不过懂得内部算法的人很少,ORACLE中也有现存的工具包,我这里用的是老外的一个JAVA包。在

使用这个LZ工具包时,涉及到一个压缩级别参数,这个等级参数不一样,压缩得到的字符串完全一不样。有人可能要问,这样搞

岂不是没法得到替换表了吗?是的,但也不完全正确。因为可供选择的等级参数有限,俺们还能从0等级开始一个一个进行测试,

看到底哪个参数是ORACLE系统用的来WRAP的。嘿嘿,ORACLE用的是“9”等级。

创建过程或包时如果没有CREATE部份,ORACLE肯定要报错;同样DBMS_DDL.WRAP也不能缺少这个“create”,

否则就要报错。但对于过程或包的SOURCE,查阅系统视图DBA_SOURCE的TEXT列就知道了,肯定没有CREATE这一句。

说到密文的正文部份,首先要看下面的例子:SQL>select dbms_ddl.wrap(‘create procedure a’) from dual;

create procedure a wrapped

a000000

354

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

7

c 38

8BgMHdmA3Qg9IbJmntlZoZQoHwcwg5nnm7+fMr2ywFxakaamb40d1Q=

这里要解释一下,加密后的代码中354与DB的版本有关,abcd后的7与创建的对象类型有关,也就是7为存储过程,另外的c 38有其他意义,这里就不多说了。从8BgMH开始,BASE64解码后的前二十个字节是SHA1-HASH值,所以解码后的第二十一个字节开始就是正文了

为了下一节的实践活动,嘿嘿,我们先把JAVA包创建好,用以进行LZ压缩与解压,如下所示(全部用SYS用户来做):create or replace java source named UNWRAPPER

as

import java.io.*;

import java.util.zip.*;

public class UNWRAPPER

{

public static String Inflate( byte[] src )

{

try

{

ByteArrayInputStream bis = new ByteArrayInputStream( src );

InflaterInputStream iis = new InflaterInputStream( bis );

StringBuffer sb = new StringBuffer();

for( int c = iis.read(); c != -1; c = iis.read() )

{

sb.append( (char) c );

}

return sb.toString();

} catch ( Exception e )

{

}

return null;

}

public static byte[] Deflate( String src, int quality )

{

try

{

byte[] tmp = new byte[ src.length() + 100 ];

Deflater defl = new Deflater( quality );

defl.setInput( src.getBytes( “UTF-8” ) );

defl.finish();

int cnt = defl.deflate( tmp );

byte[] res = new byte[ cnt ];

for( int i = 0; i < cnt; i++ )

res = tmp;

String hello = new String(res.toString().getBytes(“iso8859-1”), “GBK”);

return hello;

return res;

} catch ( Exception e )

{

}

return null;

}

}

/

alter java source UNWRAPPER compile

/

然后用包把JAVA声明进来:

create or replace package amosunwrapper

is

function deflate( src in varchar2 )

return raw;

function deflate( src in varchar2, quality in number )

return raw;

function inflate( src in raw )

return varchar2;

end;

/

create or replace package body amosunwrapper

is

function deflate( src in varchar2 )

return raw

is

begin

return deflate( src, 6 );

end;

function deflate( src in varchar2, quality in number )

return raw

as language java

name ‘UNWRAPPER.Deflate( java.lang.String, int ) return byte[]’;

function inflate( src in raw )

return varchar2

as language java

name ‘UNWRAPPER.Inflate( byte[] ) return java.lang.String’;

end;

/

创建好了工具,我们先来看看下面的SQL:with src AS ( select ‘PACKAGE a’ txt  from dual   ),

wrap as   ( select src.txt , dbms_ddl.wrap( ‘create ‘ || src.txt ) wrap  from src  ),

subst as  (select substr( utl_encode.base64_decode( utl_raw.cast_to_raw(rtrim( substr( wrap.wrap, instr( wrap.wrap, chr( 10 ), 1, 20 ) + 1 ), chr(10) )  ) ), 41 ) x,

amosunwrapper.deflate( wrap.txt || chr(0), 9 ) d

from wrap  )

select substr( x, r * 2 – 1, 2 )  c_base64,

substr( d, r * 2 – 1, 2 )  c_translatecode

from subst  , ( select rownum r from dual connect by rownum <= ( select length( x ) / 2 from subst ) );

结果如下:

C_BASE64 C_TRANSLATECODE

30 78

83 DA

99 0B

B8 70

F5 74

33 F6

9F 76

F5 74

BF 77

5C 55

5A 48

91 64

A6 00

A6 00

CB 0E

C4 B7

E1 02

48 6E

通过对结果的排序,没有出现同一个BASE64编码对应不同的十六进制的情况,因此我们知道了可以用这个SQL为基础,通过用不同的SOURCE串来产生替换表的内容。

首先建一个表来存储替换表的内容,然后写一段PLSQL块来生成替换表的内容:
CREATE TABLE SYS.IDLTRANSLATE(

C_BASE64DECODE  VARCHAR2(2) NOT NULL,

C_LZDEFLATECODE VARCHAR2(2)     NULL

)

/

declare

nCnt integer;

nLoop integer;

nSLoop integer;

nCharmax integer;

nCharmin  integer;

vChar     Varchar2(3);

cursor getchar is

with src AS ( select ‘PACKAGE ‘||vChar txt  from dual   ),

wrap as   ( select src.txt , dbms_ddl.wrap( ‘create ‘ || src.txt ) wrap  from src  ),

subst as  (select substr( utl_encode.base64_decode( utl_raw.cast_to_raw(rtrim( substr( wrap.wrap, instr( wrap.wrap, chr( 10 ), 1, 20 ) + 1 ), chr(10) )  ) ), 41 ) x,

amosunwrapper.deflate( wrap.txt || chr(0), 9 ) d

from wrap  )

select  substr( x, r * 2 – 1, 2 )  xr ,

substr( d, r * 2 – 1, 2 )  dr

from subst  , ( select rownum r from dual connect by rownum <= ( select length( x ) / 2 from subst ) );

begin

nCharmax:=97;

nCharmin:=122;

For nLoop In 97..122 Loop

For nSloop In 0..99 Loop

vChar := chr(nLoop)||to_char(nSloop);

For abc In getchar Loop

Select Count(*) Into nCnt From sys.idltranslate WHERE c_base64decode = abc.xr;

If nCnt < 1 Then

Insert INTO sys.idltranslate VALUES (abc.xr,abc.dr);

Commit;

Else

Select Count(*) Into ncnt From sys.idltranslate WHERE c_base64decode = abc.xr AND c_lzdeflatecode=abc.dr;

If nCnt < 1 Then

DBMS_OUTPUT.PUT_LINE(‘wrong orginal char:’||vchar||’         hex base64:’||abc.xr);

End If;

End If;

End Loop;

End Loop;

End Loop;

end;

运行上面这段SQL大概会产生1百多条记录,还未达到00-FF总共256条记录的要求,建议替换

select ‘PACKAGE ‘||vChar txt  from dual   中的PACKAGE关健字为procedure或者function类似的,继续运行直到替换表中有不重复的256条记录为止。

然后接下来看一个系统包

SQL> set serveroutput on;

Declare

vWrappedtext                Varchar2(32767);

vChar                                        Varchar2(2);

vRepchar                                Varchar2(2);

vLZinflatestr                        Varchar2(32767);

nLen                Integer;

nLoop        Integer;

nCnt                Integer;

Begin

select substr( utl_encode.base64_decode( utl_raw.cast_to_raw(rtrim( substr( TEXT, instr( TEXT, chr( 10 ), 1, 20 ) + 1 ), chr(10) )  ) ), 41 ) x

Into vWrappedtext

from DBA_SOURCE

Where owner=’SYS’

And Name = ‘DBMS_MONITOR’

And Type=’PACKAGE BODY’ ;

–DBMS_OUTPUT.PUT_LINE(vWrappedtext);

nLen := Length(vWrappedtext)/2 – 1;

vLZinflatestr :=”;

For nLoop In 0..nLen Loop

vChar := Substrb(vWrappedtext,nLoop*2+1,2);

SelectSQL>   2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20   21   22   Count(*) Into nCnt From SYS.IDLTRANSLATE Where C_BASE64DECODE=vChar;

If nCnt <> 1 Then

DBMS_OUTPUT.PUT_LINE(‘SUBSTATION TABLE WARNING: Count not find following char–‘||vChar);

Return;

Else

Select C_LZDEFLATECODE Into vRepchar From SYS.IDLTRANSLATE Where C_BASE64DECODE=vChar;

End If;

vLZinflatestr := vLZinflatestr || vRepchar;

–DBMS_OUTPUT.PUT_LINE(vLZinflatestr);

End Loop;

–DBMS_OUTPUT.PUT_LINE(vLZinflatestr);

DBMS_OUTPUT.PUT_LINE(amosunwrapper.inflate(vLZinflatestr));

End;

23   24   25   26   27   28   29   30   31   32   33   34   35

36  /

PACKAGE BODY dbms_monitor IS

PROCEDURE CLIENT_ID_STAT_ENABLE(CLIENT_ID IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_1”

WITH CONTEXT

PARAMETERS (CONTEXT,

CLIENT_ID   OCISTRING,

CLIENT_ID   INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE CLIENT_ID_STAT_DISABLE(CLIENT_ID IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_2”

WITH CONTEXT

PARAMETERS (CONTEXT,

CLIENT_ID     OCISTRING,

CLIENT_ID   INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE

SERV_MOD_ACT_STAT_ENABLE(SERVICE_NAME IN VARCHAR2,

MODULE_NAME  IN VARCHAR2,

ACTION_NAME  IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_3”

WITH CONTEXT

PARAMETERS (CONTEXT,

SERVICE_NAME

OCISTRING,

SERVICE_NAME    INDICATOR SB4,

MODULE_NAME     OCISTRING,

MODULE_NAME

INDICATOR SB4,

ACTION_NAME     OCISTRING,

ACTION_NAME     INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE SERV_MOD_ACT_STAT_DISABLE(SERVICE_NAME IN VARCHAR2,

MODULE_NAME  IN VARCHAR2,

ACTION_NAME  IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_4”

WITH CONTEXT

PARAMETERS (CONTEXT,

SERVICE_NAME

OCISTRING,

SERVICE_NAME    INDICATOR SB4,

MODULE_NAME     OCISTRING,

MODULE_NAME

INDICATOR SB4,

ACTION_NAME     OCISTRING,

ACTION_NAME     INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE CLIENT_ID_TRACE_ENABLE(CLIENT_ID IN VARCHAR2,

WAITS IN BOOLEAN,

BINDS IN BOOLEAN,

PLAN_STAT IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_5”

WITH CONTEXT

PARAMETERS (CONTEXT,

CLIENT_ID   OCISTRING,

CLIENT_ID     INDICATOR SB4,

WAITS   UB2,

WAITS   INDICATOR SB4,

BINDS

UB2,

BINDS   INDICATOR SB4,

PLAN_STAT   OCISTRING,

PLAN_STAT   INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE CLIENT_ID_TRACE_DISABLE(CLIENT_ID IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_6”

WITH CONTEXT

PARAMETERS (CONTEXT,

CLIENT_ID   OCISTRING,

CLIENT_ID   INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE SERV_MOD_ACT_TRACE_ENABLE(SERVICE_NAME IN VARCHAR2,

MODULE_NAME  IN VARCHAR2,

ACTION_NAME  IN VARCHAR2,

WAITS IN BOOLEAN,

BINDS IN BOOLEAN,

INSTANCE_NAME

IN VARCHAR2,

PLAN_STAT IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_7”

WITH CONTEXT

PARAMETERS

(CONTEXT,

SERVICE_NAME    OCISTRING,

SERVICE_NAME    INDICATOR SB4,

MODULE_NAME

OCISTRING,

MODULE_NAME     INDICATOR SB4,

ACTION_NAME     OCISTRING,

ACTION_NAME

INDICATOR SB4,

WAITS     UB2,

WAITS     INDICATOR SB4,

BINDS     UB2,

BINDS INDICATOR SB4,

INSTANCE_NAME   OCISTRING,

INSTANCE_NAME   INDICATOR SB4,

PLAN_STAT

OCISTRING,

PLAN_STAT     INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE

SERV_MOD_ACT_TRACE_DISABLE(SERVICE_NAME IN VARCHAR2,

MODULE_NAME  IN VARCHAR2,

ACTION_NAME  IN VARCHAR2,

INSTANCE_NAME IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_8”

WITH

CONTEXT

PARAMETERS (CONTEXT,

SERVICE_NAME    OCISTRING,

SERVICE_NAME    INDICATOR SB4,

MODULE_NAME OCISTRING,

MODULE_NAME     INDICATOR SB4,

ACTION_NAME     OCISTRING,

ACTION_NAME INDICATOR SB4,

INSTANCE_NAME   OCISTRING,

INSTANCE_NAME   INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE SESSION_TRACE_ENABLE(SESSION_ID IN BINARY_INTEGER,

SERIAL_NUM IN BINARY_INTEGER,

WAITS IN BOOLEAN,

BINDS IN BOOLEAN,

PLAN_STAT IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_9”

WITH CONTEXT

PARAMETERS (CONTEXT,

SESSION_ID    SB4,

SESSION_ID    INDICATOR

SB4,

SERIAL_NUM    SB4,

SERIAL_NUM    INDICATOR SB4,

WAITS   UB2,

WAITS

INDICATOR SB4,

BINDS   UB2,

BINDS   INDICATOR SB4,

PLAN_STAT   OCISTRING,

PLAN_STAT     INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE SESSION_TRACE_DISABLE(SESSION_ID IN BINARY_INTEGER,

SERIAL_NUM IN BINARY_INTEGER) IS

EXTERNAL

NAME “kewe_3gl_10”

WITH CONTEXT

PARAMETERS (CONTEXT,

SESSION_ID    SB4,

SESSION_ID    INDICATOR SB4,

SERIAL_NUM    SB4,

SERIAL_NUM    INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE DATABASE_TRACE_ENABLE(WAITS IN BOOLEAN,

BINDS IN BOOLEAN,

INSTANCE_NAME IN

VARCHAR2,

PLAN_STAT IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_11”

WITH CONTEXT

PARAMETERS

(CONTEXT,

WAITS     UB2,

WAITS     INDICATOR SB4,

BINDS     UB2,

BINDS INDICATOR SB4,

INSTANCE_NAME   OCISTRING,

INSTANCE_NAME   INDICATOR SB4,

PLAN_STAT

OCISTRING,

PLAN_STAT     INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

PROCEDURE DATABASE_TRACE_DISABLE(INSTANCE_NAME

IN VARCHAR2) IS

EXTERNAL

NAME “kewe_3gl_12”

WITH CONTEXT

PARAMETERS (CONTEXT,

INSTANCE_NAME   OCISTRING,

INSTANCE_NAME INDICATOR SB4)

LIBRARY DBMS_MONITOR_LIB;

END;

PL/SQL procedure success

上面直接调用不能大于4000个字节  我们写一个过程来封装一下 完善一下

set serveroutput on;create or replace procedure unwrap(o in varchar,n in varchar, t in varchar)

as

vWrappedtext                Varchar2(32767);

vtrimtext                Varchar2(32767);

vChar                                        Varchar2(2);

vRepchar                                Varchar2(2);

vLZinflatestr                        Varchar2(32767);

nLen                Integer;

nLoop        Integer;

nCnt                Integer;

type vartab is table of varchar2(2) index by varchar2(2);

mytbl vartab;

cursor getchar is select C_BASE64DECODE xr,C_LZDEFLATECODE dr from sys.idltranslate;

Begin

for i in getchar loop –sys.idltranslate表内容存到字符数组

mytbl(i.xr):=i.dr;

end loop;

vtrimtext:=”;

select count(*) into ncnt                         from DBA_SOURCE

Where owner=o

And Name = n

And Type=t ;

if ncnt >0 and ncnt <5 then

for i in 1..ncnt loop

if i=1 then

select rtrim( substr( TEXT, instr( TEXT, chr( 10 ), 1, 20 ) + 1 ), chr(10) )   –保存去掉换行的BASE64码正文

into vLZinflatestr

from DBA_SOURCE

Where owner=o

And Name = n

And Type=t and line=i;

else

select text into vLZinflatestr

from DBA_SOURCE

Where owner=o

And Name = n

And Type=t and line=i;

end if;

vtrimtext:=vtrimtext||vLZinflatestr;

end loop;

end if;

vtrimtext:=replace(vtrimtext,chr(10),”);

nLen := Length(vtrimtext)/64 ;

vWrappedtext :=”;

for i in 0..nLen  loop

if i< nLen   then

vWrappedtext:=vWrappedtext||utl_encode.base64_decode( utl_raw.cast_to_raw(substrb(vtrimtext,64*i+1 , 64 ))) ;

else

vWrappedtext:=vWrappedtext||utl_encode.base64_decode( utl_raw.cast_to_raw(substrb(vtrimtext,64*i+1  ))) ;

end if;

–DBMS_OUTPUT.PUT_LINE(vWrappedtext);

End Loop;

–vWrappedtext:=substr(vWrappedtext,41);

nLen := Length(vWrappedtext)/2 – 1;

vLZinflatestr :=”;

For nLoop In 20..nLen Loop –从第41字节开始

vChar := Substrb(vWrappedtext,nLoop*2+1,2);

/*

Select Count(*) Into nCnt From SYS.IDLTRANSLATE Where C_BASE64DECODE=vChar;

If nCnt <> 1 Then

DBMS_OUTPUT.PUT_LINE(‘SUBSTATION TABLE WARNING: Count not find following char–‘||vChar);

Return;

Else

Select C_LZDEFLATECODE Into vRepchar From SYS.IDLTRANSLATE Where C_BASE64DECODE=vChar;

End If;

*/

vLZinflatestr := vLZinflatestr || mytbl(vChar); –从字符数组匹配

–DBMS_OUTPUT.PUT_LINE(vLZinflatestr);

End Loop;

–DBMS_OUTPUT.PUT_LINE(vLZinflatestr);

DBMS_OUTPUT.PUT_LINE(amosunwrapper.inflate(vLZinflatestr));

End;

/

最后我们来一个解密一下一开始的那个函数SQL> exec unwrap(‘SYS’,’DL2ML’,’FUNCTION’);