Trigger(触发器)

触发器是指存放在数据库中被隐含执行的存储过程,它不同于过程和函数的是不需要用户显式的调用执行,当发生满足触发器定义的特定事件(如修改表、建立对象、登陆到数据库)时,Oracle会自动执行触发器的相应代码。在Oracle系统里,触发器类似过程和函数,都有声明,执行和异常处理的PL/SQL块。

触发器组成

触发器由触发事件、触发条件触发操作三部分组成。

● 触发事件
触发事件是指引起触发器被触发的SQL语句、数据库事件或用户事件。具体的触发事件如下:
⑴ 启动和关闭例程
⑵ Oracle错误消息
⑶ 用户登陆和断开会话
⑷ 特定表或视图的DML操作
⑸ 在任何方案上的DDL语句

● 触发条件(可选)
触发条件是指使用WHEN子句指定一个BOOLEAN表达式,当布尔表达式返回值为TRUE时,会自动执行触发器相应代码;当布尔表达式返回值为FALSE或UNKNOWN时,不会执行触发操作。

● 触发操作
触发操作是指包含SQL语句和其他执行代码的PL/SQL块。编写触发器执行代码时,需要注意以下限制:
⑴ 触发器代码的大小不能超过32K。如果确实需要使用大量代码建立触发器,可以先建立存储过程,然后在触发器中使用CALL语句调用存储过程。
⑵ 触发器代码只能包含SELECT、INSERT、UPDATE和DELETE语句,而不能包含DDL语句(CREATE、ALTER、DROP)和事务控制语句(COMMIT、ROLLBACK、SAVEPOINT)。

触发器类型

触发器在数据库里以独立的对象存储,触发器是当某个事件发生时自动地隐式运行。并且,触发器不能接收参数。
Oracle 8i之后触发器可以分为:DML触发器、替代触发器、系统触发器。

● DML触发器
在Oracle 8i之前只能基于DML建立触发器。当建立DML触发器时,需要指定触发时机(before或者after)、触发事件(INSERT、UPDATE、DELETE)、表名、触发类型、触发条件以及触发操作。
⑴ 触发时机:用于指定触发器的触发时间,可以指定before或after关键字,分别表示在执行DML操作之前或之后触发触发器。
⑵ 触发事件:触发事件用于指定导致触发器执行的DML操作,即INSERT、UPDATE和DELETE操作。触发事件既可以单个使用也可以组合。
⑶ 表名:指定DML触发器针对的特定表。
⑷ 触发类型:语句触发类型(默认),只会执行一次触发器代码;行触发类型,则会在每个被作用行上执行一次触发器代码。
⑸ 触发条件:触发条件用于指定执行触发器代码的条件,注意当编写DML触发器时,只允许在行触发器上指定触发条件
⑹ 触发操作:触发操作用于指定触发器执行代码。

一、语句触发器:
注意,使用语句触发器时,不能记录列数据的变化。语法:
CREATE [ OR REPLACE ] TRIGGER trigger_name
timing event1 [ OR event2 OR event3 ]
ON table_name
PL/SAL block;

1、建立Before语句触发器
示例:禁止在休息日改变雇员信息

SQL> CREATE OR REPLACE TRIGGER tr_emp
  2  BEFORE INSERT OR UPDATE OR DELETE ON emp 
  3  BEGIN
  4    IF to_char(sysdate, 'DY', 'nls_date_language=AMERICAN') 
  5       IN ('SAT', 'SUN') THEN 
  6       raise_application_error(-20001, '不能在休息日改变雇员信息');
  7    END IF;
  8  END;
  9  /

在建立了触发器tr_emp之后,如果星期六、星期日在EMP表上进行DML操作,则会显示错误信息:
SQL> UPDATE emp SET sal=1000 WHERE empno=7788;
UPDATE emp SET sal=1000 WHERE empno=7788
       *
第 1 行出现错误:
ORA-20001: 不能在休息日改变雇员信息
ORA-06512: 在 "SCOTT.TR_EMP", line 4
ORA-04088: 触发器 'SCOTT.TR_EMP' 执行过程中出错

2、使用条件谓词
当在触发器中同时包含多个触发事件(INSERT、UPDATE、DELETE)时,可以使用条件谓词在触发器代码中区分具体的触发事件:
INSERTING:当触发事件为INSERT时,该条件谓词返回TRUE,否则返回FALSE
UPDATING:当触发事件为UPDATE时,该条件谓词返回TRUE,否则返回FALSE
DELETING:当触发事件为DELETE时,该条件谓词返回TRUE,否则返回FALSE
在上述的示例中使用条件谓词:
SQL> CREATE OR REPLACE TRIGGER tr_emp
  2  BEFORE INSERT OR UPDATE OR DELETE ON emp
  3  BEGIN
  4    IF to_char(sysdate, 'DY', 'nls_date_language=AMERICAN')
  5       IN ('SAT', 'SUN') THEN
  6       CASE
  7       WHEN INSERTING THEN
  8       raise_application_error(-20001, '不能在休息日增加雇员');
  9       WHEN UPDATING THEN
 10       raise_application_error(-20002, '不能在休息日更新雇员');
 11       WHEN DELETING THEN
 12       raise_application_error(-20003, '不能在休息日解雇雇员');
 13       END CASE;
 14    END IF;
 15  END;
 16  /

触发器已创建

SQL> DELETE FROM emp WHERE empno=7788;
DELETE FROM emp WHERE empno=7788
            *
第 1 行出现错误:
ORA-20003: 不能在休息日解雇雇员
ORA-06512: 在 "SCOTT.TR_EMP", line 10
ORA-04088: 触发器 'SCOTT.TR_EMP' 执行过程中出错


SQL> UPDATE emp SET sal=0 WHERE empno=7788;
UPDATE emp SET sal=0 WHERE empno=7788
       *
第 1 行出现错误:
ORA-20002: 不能在休息日更新雇员
ORA-06512: 在 "SCOTT.TR_EMP", line 8
ORA-04088: 触发器 'SCOTT.TR_EMP' 执行过程中出错

3、建立AFTER语句触发器
示例:记录对emp表所做INSERT、UPDATE、DELETE次数

创建log_table表用于记录操作日志
SQL> CREATE TABLE log_table (
  2    name varchar2(20),
  3    ins INT,
  4    upd INT,
  5    del INT,
  6    starttime DATE,
  7    endtime DATE );

建立AFTER语句触发器,用于记录操作次数
SQL> CREATE OR REPLACE TRIGGER tr_log_emp
  2  AFTER INSERT OR UPDATE OR DELETE ON emp
  3  DECLARE
  4     v_temp INT;
  5  BEGIN
  6     SELECT count(*) INTO v_temp FROM log_table WHERE name='EMP';
  7     IF v_temp=0 THEN
  8        INSERT INTO log_table VALUES('EMP',0,0,0, SYSDATE, NULL);
  9     END IF;
 10     CASE
 11        WHEN INSERTING THEN
 12             UPDATE log_table SET ins=ins+1, endtime=SYSDATE WHERE name='EMP';
 13        WHEN UPDATING THEN
 14             UPDATE log_table SET upd=upd+1, endtime=SYSDATE WHERE name='EMP';
 15        WHEN DELETING THEN
 16             UPDATE log_table SET del=del+1, endtime=SYSDATE WHERE name='EMP';
 17     END CASE;
 18  END;
 19  /

触发器已创建

当我们在emp表上执行DML语句之后,便可以看到log_table表中已做了统计:
SQL> SELECT * FROM log_table;

NAME                        INS        UPD        DEL STARTTIME      ENDTIME
-------------------- ---------- ---------- ---------- -------------- --------------
EMP                           0          4          0 01-9月 -12     01-9月 -12

二、行触发器
行触发器是指执行DML操作时,每作用一行就触发一次的触发器。语法:
CREATE [ OR REPLACE ] TRIGGER trigger_name
timing event1 [ OR event2 OR event3 ]
ON table_name
[ REFERENCING OLD AD old | NEW AS new ]
FOR EACH ROW
[ WHEN condition ]
PL/SAL block;

1、建立BEFORE行触发器
示例:确保emp表雇员公司不能低于其原有工资

SQL> CREATE OR REPLACE TRIGGER tr_emp_sal
  2  BEFORE UPDATE OF sal ON emp
  3  FOR EACH ROW
  4  BEGIN
  5     IF :new.sal<:old.sal THEN
  6        raise_application_error(-20010, '工资不能低于原有工资');
  7     END IF;
  8  END;
  9  /

触发器已创建

SQL> UPDATE emp SET sal=1000 WHERE deptno=10;
UPDATE emp SET sal=1000 WHERE deptno=10
       *
第 1 行出现错误:
ORA-20010: 工资不能低于原有工资
ORA-06512: 在 "SCOTT.TR_EMP_SAL", line 3
ORA-04088: 触发器 'SCOTT.TR_EMP_SAL' 执行过程中出错

2、建立AFTER行触发器
示例:记录emp表中雇员工资变化

创建sal_change表用于记录工资变化情况:
SQL> CREATE TABLE sal_change (
  2  name varchar2(10),
  3  oldsal number(6,2),
  4  newsal number(6,2),
  5  time date);

表已创建。

建立AFTER行触发器用于向sal_change表中记录工资变化情况
SQL> CREATE OR REPLACE TRIGGER tr_sal_change
  2  AFTER UPDATE OF sal ON emp
  3  FOR EACH ROW
  4  DECLARE
  5     v_temp INT;
  6  BEGIN
  7     SELECT count(*) INTO v_temp FROM sal_change WHERE name=:old.ename;
  8     IF v_temp=0 THEN
  9        INSERT INTO sal_change VALUES(:old.ename, :old.sal, :new.sal, SYSDATE);
 10     ELSE
 11        UPDATE sal_change SET oldsal=:old.sal, newsal=:new.sal, time=SYSDATE WHERE name=:old.ename;
 12     END IF;
 13  END;
 14  /

触发器已创建

SQL> UPDATE emp SET sal=1000 WHERE deptno=10;

已更新3行。

SQL> SELECT * FROM sal_change;

NAME           OLDSAL     NEWSAL TIME
---------- ---------- ---------- --------------
CLARK            2450       1000 02-9月 -12
KING             5000       1000 02-9月 -12
MILLER           1300       1000 02-9月 -12


3、限制行触发器
为了使得在特定条件下执行行触发器代码,就需要使用 WHEN子句对触发条件加以限制。
示例:审计岗位为“SALESMAN”的雇员工资变化
SQL> CREATE OR REPLACE TRIGGER tr_sal_change
  2  AFTER UPDATE OF sal ON emp
  3  FOR EACH ROW
  4  WHEN (old.job='SALESMAN')
  5  DECLARE
  6     v_temp INT;
  7  BEGIN
  8     SELECT count(*) INTO v_temp FROM sal_change WHERE name=:old.ename;
  9     IF v_temp=0 THEN
 10        INSERT INTO sal_change VALUES(:old.ename, :old.sal, :new.sal, SYSDATE);
 11     ELSE
 12        UPDATE sal_change SET oldsal=:old.sal, newsal=:new.sal, time=SYSDATE WHERE name=:old.ename;
 13     END IF;
 14  END;
 15  /

触发器已创建

SQL> UPDATE emp SET sal=1000 WHERE deptno=30;

已更新6行。

SQL> SELECT * FROM sal_change;

NAME           OLDSAL     NEWSAL TIME
---------- ---------- ---------- --------------
ALLEN            1600       1000 02-9月 -12
WARD             1250       1000 02-9月 -12
MARTIN           1250       1000 02-9月 -12
TURNER           1500       1000 02-9月 -12

每张表最多可建立12 种类型的触发器,它们是:
BEFORE INSERT
BEFORE INSERT FOR EACH ROW
AFTER INSERT
AFTER INSERT FOR EACH ROW

BEFORE UPDATE
BEFORE UPDATE FOR EACH ROW
AFTER UPDATE
AFTER UPDATE FOR EACH ROW

BEFORE DELETE
BEFORE DELETE FOR EACH ROW
AFTER DELETE
AFTER DELETE FOR EACH ROW

● 替代触发器

对于简单视图,可以直接执行INSERT、UPDATE和DELETE等DML操作,但对于复杂视图是不行的。当视图符合以下任何一种情况时,都不运行直接执行DML操作:
⑴ 具有集合操作符(UNION、UNION ALL、INTERSECT、MINUS)
⑵ 具有分组函数(MIN、MAX、AVG、SUM、COUNT等)
⑶ 具有GROUP BY、CONNECT BY或START WITH等子句
⑷ 具有DISTINCT关键字
⑸ 具有连接查询

为了在具有以上情况的复杂视图上进行DML操作,必须要基于视图建立INSTEAD-OF触发器。建立INSTEAD-OF触发器有以下注意事项
⑴ INSTEAD OF选项只适用于视图
⑵ 当基于视图建立触发器时,不能指定BEFORE和AFTER选项
⑶ 在建立视图时没有指定WITH CHECK OPTION选项
⑷ 建立INSTEAD OF触发器时,必须知道FOR EACH ROW选项。

示例:

1、以emp表和dept表为例建立复杂视图(该用户要有建立视图的权限)
视图是逻辑表,本身没有任何数据。视图只是对应于一条SELECT语句,当查询视图时,其数据实际是从视图基表上取得

SQL> CREATE OR REPLACE VIEW v_dept_emp
  2  AS
  3  SELECT d.deptno, d.dname, e.empno, e.ename
  4  FROM scott.dept d, scott.emp e
  5  WHERE d.deptno = e.deptno;

View created


我们可以基于视图直接查询部门雇员信息,但不能直接进行DML操作。

SQL> SELECT * FROM v_dept_emp WHERE deptno=10;

DEPTNO DNAME          EMPNO ENAME
------ -------------- ----- ----------
    10 ACCOUNTING      7782 CLARK
    10 ACCOUNTING      7839 KING
    10 ACCOUNTING      7934 MILLER

SQL> INSERT INTO v_dept_emp VALUES (50, 'IT', 1000, 'ADMIN');

INSERT INTO v_dept_emp VALUES (50, 'IT', 1000, 'ADMIN')

ORA-01779: 无法修改与非键值保存表对应的列


2、建立INSTEAD OF触发器
为了在复杂视图上进行DML操作,必须要基于复杂视图建立INSTEAD OF触发器。

SQL> CREATE OR REPLACE TRIGGER tr_instead_of_dept_emp 
  2  INSTEAD OF INSERT ON v_dept_emp 
  3  FOR EACH ROW 
  4  DECLARE 
  5     v_temp INT;
  6  BEGIN
  7     SELECT count(*) INTO v_temp FROM dept WHERE deptno=:new.deptno;
  8     IF v_temp = 0 THEN 
  9        INSERT INTO dept (deptno, dname) VALUES (:new.deptno, :new.dname);
 10     END IF;
 11     SELECT count(*) INTO v_temp FROM emp WHERE empno=:new.empno;
 12     IF v_temp = 0 THEN 
 13        INSERT INTO emp (empno, ename, deptno) VALUES (:new.empno, :new.ename, :new.deptno);
 14     END IF;
 15  END;
 16  /

Trigger created


当建立了INSTEAD OF触发器之后,就可以在复杂视图v_dept_emp上进行DML操作了

SQL> INSERT INTO v_dept_emp VALUES (50, 'IT', 1000, 'ADMIN');

1 row inserted

SQL> INSERT INTO v_dept_emp VALUES (10, 'MANAGER', 1001, 'WORKER');

1 row inserted



无法对SYS 拥有的对象创建触发器
ORA-04089 cannot create triggers on objects owned by SYS
Cause: An attempt was made to create a trigger on an object owned by SYS.
Action: Do not create triggers on objects owned by SYS.

● 系统事件触发器

系统事件触发器是指基于Oracle系统事件(例如LOGIN和STARTUP)所建立的触发器。通过使用系统事件触发器,提供了跟踪系统或数据库变化的机制。

1、常用的系统事件属性函数:
⑴ ora_client_ip_address:用于返回客户端的IP地址
⑵ ora_database_name:用于返回当前数据库名
⑶ ora_des_encrypted_password:返回DES加密后的用户口令
⑷ ora_dict_obj_name:返回DDL操作所对应的数据库对象名
⑸ ora_dict_obj_name_list(name_list OUT ora_name_list_t):返回在事件中被修改的对象名列表
⑹ ora_dict_obj_owner:返回DDL操作所对应的对象的所有者名
⑺ ora_dict_obj_owner_list(owner_list OUT ora_name_list_t):返回在事件中被修改对象的所有者列表
⑻ ora_dict_obj_type:返回DDL操作所对应的数据库对象的类型
⑼ ora_grantee(user_list OUT ora_name_list_t):返回授权事件的授权者
⑽ ora_instance_num:用于返回例程号
⑾ ora_is_alter_column(column_name IN VARCHAR2):检测特定列是否被修改
⑿ ora_is_creating_nested_table:检测是否正在建立嵌套表
⒀ ora_is_servererror(error_number):检测是否返回了特定Oracle错误
⒁ ora_login_user:返回登陆用户名
⒂ ora_sysevent:返回触发触发器的系统事件名

下面给出系统触发器的种类和事件出现的时机(前或后):

事件          允许的时机 说明
STARTUP      AFTER 启动数据库实例之后触发
SHUTDOWN      BEFORE               关闭数据库实例之前触发(非正常关闭不触发)
SERVERERROR    AFTER                  数据库服务器发生错误之后触发
LOGON             AFTER                       成功登录连接到数据库后触发
LOGOFF BEFORE                 开始断开数据库连接之前触发
CREATE      BEFORE,AFTER        在执行CREATE语句创建数据库对象之前、之后触发
DROP BEFORE,AFTER 在执行DROP语句删除数据库对象之前、之后触发
ALTER BEFORE,AFTER 在执行ALTER语句更新数据库对象之前、之后触发
DDL BEFORE,AFTER 在执行大多数DDL语句之前、之后触发
GRANT BEFORE,AFTER 执行GRANT语句授予权限之前、之后触发
REVOKE BEFORE,AFTER 执行REVOKE语句收权限之前、之后触犯发
RENAME    BEFORE,AFTER     执行RENAME语句更改数据库对象名称之前、之后触犯发
AUDIT / NOAUDIT    BEFORE,AFTER    执行AUDITNOAUDIT进行审计或停止审计之前、之后触发

2、建立例程启动和关闭触发器
建立事件表event_table,用于记录例程启动和关闭的事件和时间,跟踪例程启动和关闭事件信息。

SQL> CREATE TABLE event_table (
  2  event varchar2(20),
  3  time date );

Table created


注意,例程启动触发器和例程关闭触发器只有特权用户才能建立,并且例程启动触发器只能使用AFTER关键字,例程关闭触发器只能使用BEFORE关键字。

SQL> CREATE OR REPLACE TRIGGER tr_startup
  2  AFTER STARTUP ON DATABASE
  3  BEGIN
  4     INSERT INTO scott.event_table VALUES(ora_sysevent, SYSDATE);
  5  END;
  6  /

Trigger created


SQL> CREATE OR REPLACE TRIGGER tr_shutdown
  2  BEFORE SHUTDOWN ON DATABASE
  3  BEGIN
  4     INSERT INTO scott.event_table VALUES (ora_sysevent, SYSDATE);
  5  END;
  6  /

Trigger created


建立tr_startup触发器之后,当打开数据库,会执行该触发器的代码。建立tr_shutdown触发器之后,当关闭例程之前,会执行该触发器的代码。但SHUTDOWN ABORT命令不会触发该触发器。

SQL> select * from scott.event_table;

EVENT                TIME
-------------------- --------------
SHUTDOWN             30-8月 -12
STARTUP              30-8月 -12


3、建立登陆和退出触发器
建立log_table用于记录登陆和退出用户相关信息

SQL> CREATE TABLE log_table (
  2  username VARCHAR2(20),
  3  login_time DATE,
  4  logoff_time DATE,
  5  address VARCHAR2(20));

表已创建。


注意,登陆触发器和退出触发器一定要以特权用户身份建立,并且登陆触发器只能使用AFTER关键字,退出触发器只能使用BEFORE关键字:

SQL> CREATE OR REPLACE TRIGGER tr_login
  2  AFTER LOGON ON DATABASE
  3  BEGIN
  4     INSERT INTO log_table(username, login_time, address) VALUES (ora_login_user, SYSDATE, ora_client_ip_address);
  5  END;
  6  /

触发器已创建

SQL> CREATE OR REPLACE TRIGGER tr_logoff
  2  BEFORE LOGOFF ON DATABASE
  3  BEGIN
  4     INSERT INTO log_table(username, logoff_time, address) VALUES (ora_login_user, SYSDATE, ora_client_ip_address);
  5  END;
  6  /

触发器已创建

在建立了触发器tr_login之后,当用户登陆到数据库之后,会执行其代码。在建立了tr_logoff触发器之后,当用户断开数据库连接之前,会执行其触发器代码。

4、建立DDL触发器
为了记录系统所发生的DDL事件(CREATE、ALTER、DROP等),可以建立DDL触发器。

建立ddl_table用于记录,用于存放DDL事件信息:
SQL> CREATE TABLE event_dll (
  2  event VARCHAR2(20),
  3  username VARCHAR2(20),
  4  owner VARCHAR2(20),
  5  objname VARCHAR2(20),
  6  objtype VARCHAR2(10),
  7  time DATE);

表已创建。

SQL> CREATE OR REPLACE TRIGGER tr_ddl
  2  AFTER DDL ON scott.schema
  3  BEGIN
  4     INSERT INTO event_dll VALUES(
  5     ora_sysevent, ora_login_user, ora_dict_obj_owner,
  6     ora_dict_obj_name, ora_dict_obj_type, SYSDATE);
  7  END;
  8  /

触发器已创建

系统触发器不能在sys方案中定义

在scott方案上进行DDL操作:
SQL> CREATE TABLE ddl_table AS SELECT * FROM dept WHERE 1=2;

表已创建。

SQL> DROP TABLE ddl_table;

表已删除。

查询event_dll表由触发器插入的数据:
SQL> SELECT * FROM event_dll;

EVENT                USERNAME             OWNER                OBJNAME              OBJTYPE    TIME
-------------------- -------------------- -------------------- -------------------- ---------- --------------
CREATE               SCOTT                SCOTT                DDL_TABLE            TABLE      30-8月 -12
DROP                 SCOTT                SCOTT                DDL_TABLE            TABLE      30-8月 -12

管理触发器

1、显示触发器信息
建立触发器时,Oracle会将触发器信息写入到数据字典中,通过查询数据字典视图 USER_TRIGGERS,可以显示当前用户所包含的所有触发器信息。
SQL> conn scott/tiger;
已连接。
SQL> SELECT trigger_name, status FROM user_triggers WHERE table_name='EMP';

TRIGGER_NAME                   STATUS
------------------------------ --------
TR_LOG_EMP                     ENABLED
TR_SAL_CHANGE                  ENABLED

2、禁止触发器
禁止触发器是指使触发器临时失效(disable),当触发器处于ENABLE状态时为激活状态。
SQL> alter trigger tr_log_emp disable;

触发器已更改

SQL> SELECT trigger_name, status FROM user_triggers WHERE table_name='EMP';

TRIGGER_NAME                   STATUS
------------------------------ --------
TR_LOG_EMP                     DISABLED
TR_SAL_CHANGE                  ENABLED

3、激活触发器
激活触发器是指使触发器重新生效
SQL> alter trigger tr_log_emp enable;

触发器已更改

4、禁止或激活表的所有触发器
如果在表上同时存在多个触发器,那么使用ALTER TABLE命令可以一次禁止或激活所有触发器
SQL> ALTER TABLE emp DISABLE ALL TRIGGERS;

表已更改。

SQL> ALTER TABLE emp ENABLE ALL TRIGGERS;

表已更改。

5、重新编译触发器
但是用ALTER TABLE命令修改表结构时,会使得其触发器转变为INVALID状态,在这种情况下,为了使得触发器继续生效,需要重新编译触发器
SQL> ALTER TRIGGER tr_log_emp COMPILE;

触发器已更改

6、删除触发器
使用DROP TRIGGER命令删除触发器。注意,在表上的触发器越多,对于DML操作的性能影响也越大,所以一定要适度使用触发器。
SQL> DROP TRIGGER tr_log_emp;

触发器已删除。

转载于:https://my.oschina.net/xiaomaoandhong/blog/75747

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值