.Net程序员学用Oracle系列(8):触发器、任务、序列、连接

《.Net程序员学用Oracle系列:导航目录》

本文大纲

触发器

创建触发器

CREATE OR REPLACE TRIGGER trg_sync_staff90
-- 功能说明
AFTER INSERT OR UPDATE OR DELETE ON t_staff
FOR EACH ROW
DECLARE
  -- 定义变量
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  IF INSERTING THEN
    -- 定义DML语句
  ELSIF UPDATING THEN
    -- 定义DML语句
  ELSIF DELETING THEN
    -- 定义DML语句
  END IF;
END;

禁用触发器 & 启用触发器 & 删除触发器

ALTER TABLE t_staff DISABLE ALL TRIGGERS; -- 禁用 t_staff 表上所有的触发器
ALTER TABLE t_staff ENABLE ALL TRIGGERS;  -- 启用 t_staff 表上所有的触发器
ALTER TRIGGER trg_sync_staff90 DISABLE;   -- 禁用触发器 trg_sync_staff90
ALTER TRIGGER trg_sync_staff90 ENABLE;    -- 启用触发器 trg_sync_staff90
DROP TRIGGER trg_sync_staff90;            -- 删除触发器 trg_sync_staff90

任务

DBMS_JOB 包介绍

创建任务的方法有很多,在实际使用过程中,我发现用 DBMS_JOB.SUBMIT 包来创建是比较方便的,语法是 DBMS_JOB.SUBMIT(jobno, what, next_date, interval),示例:

DECLARE jobno NUMBER;
BEGIN
  DBMS_JOB.SUBMIT(jobno, 'sp_sync_staff90;', SYSDATE, 'TRUNC(SYSDATE) + 1 + 2/24');
  COMMIT;
END;

估计有人会感到疑惑,为什么创建任务的语句中 job 参数不给具体值,下面就来简要介绍下 DBMS_JOB.SUBMIT 各个参数及方法的用途和使用说明,如下:

  • job:任务编号,系统会自动分配。我尝试过给一个自己喜欢的数字,但系统不认,还是自动分配了。
  • what:任务要执行的操作(如调用过程等),可通过 DBMS_JOB.WHAT(jobno,what) 修改。
  • next_date:任务的下一次执行时间,可通过 DBMS_JOB.NEXT_DATE(jobno,next_date) 修改。
  • interval:任务执行时间间隔,可通过 DBMS_JOB.INTERVAL(jobno,interval) 修改。
  • 启动 job:DBMS_JOB.RUN(jobno)。
  • 禁用 job:DBMS_JOB.BROKEN(jobno,broken,next_date),broken 参数需要提供布尔值,next_date 参数有默认值(SYSDATE),不需要给值,broken 为 true 表示禁用任务,为 false 表示启用任务,如 DBMS_JOB.BROKEN(135,TRUE) 表示禁用 jobno 为 135 的任务。
  • 删除 job:DBMS_JOB.REMOVE(jobno)。

以上关于 DBMS_JOB 的这些 PLSQL 都不能像普通 SQL 语句一样直接运行,需要写在 BEGIN 和 END 中间,示例:

BEGIN
  DBMS_JOB.BROKEN(135,TRUE);
  COMMIT;
END;

如果想看一下整个数据库目前的任务情况,可以通过 user_jobs 视图来查询,示例:

SELECT job jobno,what,to_char(next_date, 'yyyy-mm-dd hh24:mi:ss') next_date,interval FROM user_jobs;

下面来简要说明一下 user_jobs 视图各主要字段的含义,如下:

  • job:NUMBER 类型,任务的唯一编号。
  • what:VARCHAR2(4000),任务操作内容。
  • next_date:DATE 类型,下一次执行任务的时间。
  • interval:VARCHAR2(200),任务执行时间间隔。
  • log_user:提交任务的用户。
  • priv_user:赋予任务权限的用户。
  • schema_user:对任务作语法分析的用户模式。
  • last_date:最后一次成功运行任务的时间。
  • last_sec:如 hh24:mm:ss 格式的 last_date 日期的小时,分钟和秒。
  • this_date:正在运行任务的开始时间,如果没有运行任务则为 null。
  • this_sec:如 hh24:mm:ss 格式的 this_date 日期的小时,分钟和秒。

interval 参数设置案例:

  • 每分钟(的0秒)执行一次:interval => 'TRUNC(SYSDATE,''mi'') + 1/(24*60)'
  • 每小时(的0分0秒)执行一次:interval => 'TRUNC(SYSDATE,''hh24'') + 1/24'
  • 每隔 7 天执行一次:interval => 'TRUNC(SYSDATE) + 7 + 1/24'
  • 每天凌晨两点执行一次:interval => 'TRUNC(SYSDATE) + 1 + 2/24'
  • 每周一凌晨 1 点执行一次:interval => 'TRUNC(NEXT_DAY(SYSDATE,''monday'')) + 1/24'
  • 每月 5 号凌晨 1 点执行一次:interval => 'TRUNC(LAST_DAY(SYSDATE)) + 5 + 1/24'
  • 每季度第一天凌晨 1 点执行一次:interval => 'TRUNC(ADD_MONTHS(SYSDATE,3),''Q'') + 1/24'
  • 每年 1 月 1 号凌晨 1 点执行一次:interval => 'ADD_MONTHS(TRUNC(SYSDATE,''yyyy''),12) + 1/24'

序列

我接触的两个基于 Oracle 开发的项目都从不用序列,以至于我都没有使用序列的实战经验。写这个章节前我特意查了下序列的定义,我觉得我们可以把序列当成是数字工厂,因为它唯一的功能就是生产等间隔的数值。

用过 Oracle 的人应该都知道,Oracle 没有提供类似于 SQL Server 或 MySQL 中自动增长列的功能,如果我们出于对性能或空间等其它因素的考虑,需要使用自动增长列,则可以通过序列来实现类似功能。

创建序列

创建序列的标准语法如下:

CREATE SEQUENCE seq_name [INCREMENT BY n] [START WITH n] [{MAXVALUE n/NOMAXVALUE}] [{MINVALUE n/NOMINVALUE}] [{CYCLE/NOCYCLE}] [{CACHE n/NOCACHE}];

语法选项说明:

  • INCREMENT BY n:n 表示序列中连续两个值之间的间隔,也称作步长。如果 n 是正数则表示递增,如果 n 是负数则表示递减,默认是 1。
  • START WITH n:n 表示序列的起始值,即序列的第一个值,默认是 1,递增时 n 是 minValue,递减时 n 是 maxValue。
  • MAXVALUE n:n 表示序列的最大值,也可以选择 NOMAXVALUE,即不设置最大值,默认是 999999999999999999999999999。
  • MINVALUE n:n表示序列的最小值,也可以选择 NOMINVALUE,即不设置最小值,默认是 1。
  • CYCLE 和 NOCYCLE 分别表示当序列的值达到极限值后循环取值和不循环取值。
  • CACHE n:n 定义存放序列的内存块的大小,默认为 20(个数字)。NOCACHE 表示不对序列进行内存缓冲。对序列进行内存缓冲,可以改善序列的性能。

使用序列 & 删除序列

  • 使用序列
    • seq_name.CURRVAL:返回序列的当前值。
    • seq_name.NEXTVAL:返回序列的下一个值。
  • 以下情况之一不能使用序列:
    • 1、在 DELETE、SELECT、UPDATE 的子查询中。
    • 2、在视图或物化事物的查询中。
    • 3、SELECT 查询中使用了 DISTINCT 操作符。
    • 4、SELECT 查询中有 GROUP BY 或 ORDER BY。

如果想查询一下数据中到底有那些序列,语法如下:

SELECT * FROM USER_SEQUENCES;
SELECT * FROM ALL_SEQUENCES;
SELECT * FROM DBA_SEQUENCES;

删除序列,示例:

DROP SEQUENCE seq_name;

连接

Oracle 中有个叫做 Database link 的东东,翻译成中文应该是数据库连接,为了称呼方便,下文统一称之为 dblink。在我跟公司一个技术专家对话时,对方提到“数据库连接”,于是我上网查了下:dblink 是定义一个数据库到另一个数据库的路径的对象,dblink 允许你查询远程表及执行远程程序。

dblink 有两种类型的,分别是公用的和私有的。一个公用数据库链接对于数据库中的所有用户都是可用的,而一个私有链接仅对创建它的用户可用。由一个用户给另外一个用户授权私有数据库链接是不可能的,一个数据库链接要么是公用的,要么是私有的。在任何分布式环境里,dblink 都是必要的。另外要注意的是 dblink 是单向的连接。

创建 dblink

创建 dblink 之前先得确定三件事,第一本地数据库和远程数据库之间的网络是可以正常连接的,第二创建 dblink 的账号必须得有 CREATE DATABASE LINK 或 CREATE PUBLIC DATABASE LINK 的权限,第三用来登录到远程数据库的帐号必须得有 CREATE SESSION 权限。

实践告诉我,创建 dblink 的正确语法如下:

CREATE [PUBLIC] DATABASE LINK dblink_name CONNECT TO user_name IDENTIFIED BY user_pwd USING 'connect_string';

其中 connect_string 有两种写法,示例:

-- 第一种写法
CREATE PUBLIC DATABASE LINK dblink168
CONNECT TO office
IDENTIFIED BY 123456
USING '192.168.1.168:1521/orcl';
-- 第二种写法
CREATE PUBLIC DATABASE LINK dblink168
CONNECT TO office
IDENTIFIED BY 123456
USING '(DESCRIPTION = (
        ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.168)(PORT = 1521))
    )(
        CONNECT_DATA = (SERVICE_NAME = orcl)
    )
)';

网上很多文章中给出的创建语法里都不包含指定密码这一项,还说如果不指定,则使用当前的用户名和口令登录到远程数据库,我反复实验了好多次,都是直接报语法错误。不过我倒是发现了一个特点,指定密码创建成功之后,再去查看 dblink 定义的 SQL,会发现密码指令项不见了。我猜应该是 Oracle 做了特殊的加密处理,所以如果你要创建 dblink,不必担心显示指定密码后别人会看到,造成安全泄漏。

使用 dblink & 删除 dblink

  • 使用 dblink

我觉得 dblink 真正的魅力之一便是使用方便,无论增删改查那种语句,只需要在表名后面跟上 @dblink_name 就能操作远程数据库了。如要查询 168 上用户表中女员工的数量,示例:

SELECT COUNT(1) FROM t_staff@dblink168 t WHERE t.gender=0;

测试中我也发现一个小问题,假如我要查询 168 上的服务器时间,按理说写法应该如下:

SELECT SYSDATE FROM DUAL@dblink168; -- 结果显示出来的时间仍是本地数据库的服务器时间

如果为了命名更加统一,或者不想让对方知道 dblink 的名字,也可以通过视图或同义词包装一下,示例:

CREATE VIEW v_name AS SELECT * FROM table_name@dblink_name;
CREATE SYNONYM table_name FOR table_name@dblink_name;

跟 dblink 有关的几个视图,介绍如下:

SELECT * FROM dba_db_links; -- 查询当前数据库实例中所有 dblink
SELECT * FROM v$dblink;     -- 查询当前数据库示例中正在打开状态的 dblink
SELECT * FROM user_sys_privs WHERE PRIVILEGE LIKE '%LINK%'; -- 查询跟 dblink 有关的系统权限
  • 删除 dblink

对于非 PUBLIC 类型的 dblink,只有 owner 自己才能删除,非 PUBLIC 类型的 dblink 没有这个要求。删除 dblink 的示例:

DROP [PUBLIC] DATABASE LINK dblink168;

总结

本文主要介绍了 Oracle 中相对不那么常用的一些对象的 SQL 操作,但在实际做项目过程中,有时候它们又非常实用,所以有必要总结一下,以免用到的时候不记得语法,还得去查资料。

《.Net程序员学用Oracle系列:导航目录》

本文声明:如果您认为这篇文章还可以或对您有帮助,请点击文章末尾的“推荐”按钮。欢迎转载、演绎或用于商业目的,但必须保留本文的署名韩宗泽,并且要在明显位置给出原文链接!本人初写博客,水平有限,若有不当之处,敬请批评指正,谢谢!

上一篇:c#扩展方法的理解(二:接口)


下一篇:Angular JS学习之指令