0

電話番号がデフォルトの形式でデータベースに挿入されるようにするトリガーを作成しました。Oracle データベース用に作成されました。現在、SQL Server 2008 R2 で作業していますが、このトリガーの変換に問題があります。次のトリガーを T-SQL に変換するのを手伝ってくれませんか?

create or replace trigger before_insert_update_locations  
BEFORE insert or update  
ON locations  
for each row  
BEGIN  
    if length(:new.phone) = 10 then  
       :new.phone := '('||substr(:new.phone,1,3)||') '||substr(:new.phone,4,3)||'-'||substr(:new.phone,7,4);  
    elsif length(:new.phone) = 7 then  
       :new.phone := '(907) '||substr(:new.phone,1,3)+'-'||substr(:new.phone,4,4);  
    end if;  
END;  
4

2 に答える 2

2

これでうまくいくはずです。T-SQL トリガーはステートメントごとに起動することに注意してください。「FOR EACH ROW」に相当するものはありません。したがって、以下のトリガーは、更新と挿入の後に起動され、新しく更新/挿入された行の値を調整します。「IF EXISTS」ビットは、電話番号が更新されていない場合、何もしないようにします。

CREATE TRIGGER UPDPH
ON locations
AFTER UPDATE, INSERT
AS
BEGIN
    IF EXISTS
        (
            SELECT NULL
            FROM
                inserted i
            LEFT JOIN
                locations l
                ON i.locationsID = l.locationsID
                AND 
                    (
                        i.phone <> l.phone
                        OR 
                        (   l.phone IS NULL
                            AND 
                            i.phone IS NOT NULL
                        )
                        OR
                        (   i.phone IS NULL
                            AND 
                            l.phone IS NOT NULL
                        )
                    )                   
        )
    UPDATE locations
    SET phone = 
        CASE 
            WHEN LEN(i.phone) = 10
                THEN '(' + left(i.phone, 3)
                    + ') '
                    + SUBSTRING(i.phone, 4, 3)
                    + '-'
                    + RIGHT(i.phone, 4)
            WHEN LEN(i.phone) = 7
                THEN '(907) '
                    + LEFT(i.phone, 3)
                    + '-'
                    + RIGHT(i.phone, 4)
            ELSE    
                i.phone
        END
    FROM
        inserted i
    JOIN
        locations 
        ON locations.locationsID = i.locationsID;
END
于 2013-03-11T16:52:38.460 に答える
1

@Jason Quinonesが言ったように、Transact-SQLはFOR EACH ROWトリガーをサポートしていません。どちらもサポートしていません。BEFOREそのため、彼と私の答えの両方がAFTERトリガーを提供します。実際のところ、INSTEAD OFT-SQLにはトリガーがあり、トリガーの代わりに使用されることもありBEFOREますが、それでも2つはまったく異なります(名前が示すように)。

とにかく、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

ご覧のとおり、トリガーは、の長さがphone7文字または10文字の場合にのみ更新を実行します。(したがって、他の長さの値は変更されません。)

値が指定された長さの1つである場合、最初に長さが10であることを確認します。デフォルトの907プレフィックスが最初に追加され、結果の最後の10文字が取得されます。したがって、メソッドがどのように機能するかを説明するために、元の長さが7の場合、次のようになります。

'907' + '1234567'  ->  '9071234567'            ->  '9071234567'
                        ^^^^^^^^^^ (10 chars)      (same as before)

そしてそれが10の場合、これは何が起こるかです:

'907' + '1234567890'  ->  '9071234567890'            ->  '1234567890'
                              ^^^^^^^^^^ (10 chars)      (original value)

次に、STUFF関数が2回呼び出され、結果の数値の中央にa-とaが挿入されます。挿入位置は変更されていない10桁の数字に関連しているため、-最初にが挿入され、次に(位置のシフトを考慮する必要があります):

       #4  #7
        |  |
        v  v
0)  'xxxxxxxxxx'

1)  'xxxxxx-xxxx'

2)  'xxx) xxx-xxxx'

最後に、a(は最初に単純に連結されます。


この問題に関連して覚えておく価値のあるもう1つのことは、同じテーブルを更新するトリガーによって、それ自体が再度起動する可能性があることです。これは、トリガーの再帰AFTER UPDATEと呼ばれる現象です。トリガー再帰はデフォルトで無効になっていますが、対応するオプションが変更されていないかどうかわからない場合は、現在の状態を確認することをお勧めします。クエリは1つの方法です。sys.databases

SELECT is_recursive_triggers_on
FROM sys.databases
WHERE name = 'your database name'

列と同様is_recursive_triggers_onに、0はを表し、1はを表します。bitoffon

上記のトリガーは、無制限の再帰を妨げないように設計されています。ネストされた呼び出しは、ある時点で行の更新を停止しますが(LEN(i.phone) IN (7, 10)条件がfalse最終的になるため)、トリガーは引き続き呼び出されます。これを修正するには、最初にこのチェックを追加するだけです。

IF NOT EXISTS (SELECT * FROM inserted)
  RETURN
;

再帰トリガーの詳細については、こちらをご覧ください。

于 2013-03-13T08:48:13.947 に答える