Like @Jason Quinones said, Transact-SQL doesn't support FOR EACH ROW
triggers. Neither does it support BEFORE
ones, which is why both his and my answers offer an AFTER
trigger. Though, as a matter of fact, there are INSTEAD OF
triggers in T-SQL, which sometimes might be used as a replacement for BEFORE
triggers, the two are nevertheless quite different (as the names may suggest).
Anyway, an AFTER
trigger should work perfectly for this problem, so, here's another Transact-SQL version for you:
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
As you can see, the trigger performs an update only if the length of phone
is either 7 or 10 characters. (Values of other lengths thus stay unmodified.)
If the value is one of the specified lengths, it is first made sure to be the length of 10: the default 907
prefix is added to it at the beginning and then the last 10 characters of the result are taken. So, just to illustrate how the method works, if the original length is 7, you get this:
'907' + '1234567' -> '9071234567' -> '9071234567'
^^^^^^^^^^ (10 chars) (same as before)
And if it is 10, this is what happens:
'907' + '1234567890' -> '9071234567890' -> '1234567890'
^^^^^^^^^^ (10 chars) (original value)
Next, the STUFF
function is called twice to insert a -
and a )
in the middle of the resulting number. Because the insert positions are related to the unchanged 10-digit number, the -
is inserted first, then the )
(otherwise the shift of positions would need to be taken into account):
#4 #7
| |
v v
0) 'xxxxxxxxxx'
1) 'xxxxxx-xxxx'
2) 'xxx) xxx-xxxx'
Finally, a (
is simply concatenated at the beginning.
One other thing worth keeping in mind in connection with this problem is that an AFTER UPDATE
trigger updating the same table may cause itself to fire again, which is a phenomenon called trigger recursion. Trigger recursion is disabled by default, but if you are not sure if the corresponding option was never changed, you might want to verify its present state. Querying against sys.databases
is one way:
SELECT is_recursive_triggers_on
FROM sys.databases
WHERE name = 'your database name'
As is_recursive_triggers_on
is a bit
column, 0 would stand for off
and 1 for on
.
The above trigger is designed in a way that does not prevent its unlimited recursion: even though nested calls would stop updating any rows at some point (because the LEN(i.phone) IN (7, 10)
condition would become false
eventually), the trigger would still keep being invoked. To fix that, you could simply add this check at the beginning:
IF NOT EXISTS (SELECT * FROM inserted)
RETURN
;
Read more about recursive triggers here: