就像@Jason Quinones 所说,Transact-SQL 不支持触发器FOR EACH ROW
。它也不支持BEFORE
那些,这就是为什么他和我的回答都提供了一个AFTER
触发器。尽管事实上,INSTEAD OF
T-SQL 中有触发器,有时可以用作BEFORE
触发器的替代品,但两者还是完全不同的(正如名称所暗示的那样)。
无论如何,AFTER
触发器应该可以完美地解决这个问题,所以,这里有另一个 Transact-SQL 版本供您使用:
CREATE TRIGGER locations_format_phone
ON locations
AFTER INSERT, UPDATE
AS
BEGIN
UPDATE locations
SET phone = '(' + STUFF(STUFF(RIGHT('907' + RTRIM(i.phone), 10), 7, 0, '-'), 4, 0, ') ')
FROM inserted AS i
WHERE i.location_id = locations.location_id
AND LEN(i.phone) IN (7, 10)
END
如您所见,仅当 的长度为phone
7 或 10 个字符时,触发器才会执行更新。(其他长度的值因此保持不变。)
如果该值是指定长度之一,则首先确保其长度为 10:907
在开头添加默认前缀,然后取结果的最后 10 个字符。因此,为了说明该方法的工作原理,如果原始长度为 7,您将得到:
'907' + '1234567' -> '9071234567' -> '9071234567'
^^^^^^^^^^ (10 chars) (same as before)
如果是 10,则会发生以下情况:
'907' + '1234567890' -> '9071234567890' -> '1234567890'
^^^^^^^^^^ (10 chars) (original value)
接下来,STUFF
调用该函数两次以在结果数字的中间插入 a-
和 a )
。因为插入位置与未更改的 10 位数字相关,所以-
先插入 ,然后插入)
(否则需要考虑位置的移动):
#4 #7
| |
v v
0) 'xxxxxxxxxx'
1) 'xxxxxx-xxxx'
2) 'xxx) xxx-xxxx'
最后,(
在开头简单地连接了 a。
与此问题相关的另一件值得牢记的事情是,AFTER UPDATE
更新同一个表的触发器可能会导致自身再次触发,这是一种称为触发器递归的现象。默认情况下禁用触发递归,但如果您不确定相应选项是否从未更改,您可能需要验证其当前状态。查询sys.databases
是一种方法:
SELECT is_recursive_triggers_on
FROM sys.databases
WHERE name = 'your database name'
作为is_recursive_triggers_on
一bit
列,0 代表off
,1 代表on
。
上述触发器的设计方式不会阻止其无限递归:即使嵌套调用会在某个时候停止更新任何行(因为LEN(i.phone) IN (7, 10)
条件false
最终会变为),触发器仍将继续被调用。要解决此问题,您只需在开头添加此检查:
IF NOT EXISTS (SELECT * FROM inserted)
RETURN
;
在此处阅读有关递归触发器的更多信息: