我想编写一个触发器,以便在插入或更新decom_date
时,将一年中的一周更新为相应的值。
这是我到目前为止所做的,但在插入日期后,本周仍为空。
create or replace trigger test_trigger
before insert on check_decom
for each row
begin
if inserting then
update check_decom set decom_week= (select to_char(to_date(decom_date,'DD-
MON-YY'),'WW') as week from check_decom) ;
end if;
end;
/
SQL> select * from check_decom;
DECOM_DATE DECOM_WEEK
------------------------------ ----------
23-JUN-17
我做错了什么?
一年中的一周的例子
SQL> select to_char(to_date(sysdate,'DD-MON-YY'),'WW') as week from dual;
WE
--
28
答案 0 :(得分:2)
从日期处理开始,你做了一些错事。您的decom_date
列应定义为DATE
列 - 它看起来可能是您的示例输出中的字符串。但是你对sysdate
的处理也是错误的,因为你隐式转换为一个字符串,以便将它转换回一个日期,这是毫无意义的,容易出错,因为这可能发生在一个不同的会话中NLS设置。如果您的列实际上是DATE
,那么您也不应该针对该列调用to_date()
;如果它是一个字符串,那么该转换是有效的,但 应该是DATE
。
然后您的触发器正在查询并尝试更新触发器所针对的表。没有数据没有错误但没有做任何事情因为没有要更新的现有行 - 您插入的那个尚不存在。如果有数据,那么如果您没有从select
部分获得太多行异常,则会出现变异表错误。
行级触发器可以访问NEW
and OLD
pseudorecords以查看和操纵受影响的行;您不需要(通常不能)使用DML查询来访问您正在操作的行中的数据。
如果您的表定义了日期列和数字列:
create table check_decom(decom_date date, decom_week number);
然后你的触发器可能看起来像:
create or replace trigger test_trigger
before insert on check_decom
for each row
begin
if inserting then
:new.decom_week := to_number(to_char(:new.decom_date, 'WW'));
end if;
end;
/
尽管if inserting
检查有点无意义,因为触发器只会在插入时触发。这本身可能是一个问题;您可能也希望将其设置为更新,但逻辑相同,因此将是:
create or replace trigger test_trigger
before insert or update on check_decom
for each row
begin
:new.decom_week := to_number(to_char(:new.decom_date, 'WW'));
end;
/
做你想做的事:
insert into check_decom (decom_date) values (date '2017-06-23');
1 row inserted.
select * from check_decom;
DECOM_DAT DECOM_WEEK
--------- ----------
23-JUN-17 25
但我根本不会用触发器来做这件事。从Oracle 11g开始,您可以使用a virtual column代替:
create table check_decom (
decom_date date,
decom_week generated always as (to_number(to_char(decom_date, 'WW')))
);
Table CHECK_DECOM created.
insert into check_decom (decom_date) values (date '2017-06-23');
1 row inserted.
select * from check_decom;
DECOM_DAT DECOM_WEEK
--------- ----------
23-JUN-17 25