削除されたデータの発行にトリガー比較挿入

私は前回問題の下に直面していて、なぜそれを疑問に思いました

null <> 'value'

トリガーでは動作しません。 問題を解決するために、私はisnull関数を使わなければなりませんでした

isnull(null,'') <> isnull('value','')

以下にテストするすべてのコード:

-- create main table
CREATE TABLE T_SAMPLE
(
    ID INT,
    NAME NVARCHAR(20)
)
GO

-- populate data in main table
INSERT INTO T_SAMPLE
VALUES (1, 'ONE'),
    (2,'TWO'),
    (3,'THREE')
GO

-- create table to store changes
CREATE TABLE T_SMAPLE_TEST
(
    NAME NVARCHAR(40)
)
GO

-- create trigger on main table
CREATE TRIGGER [dbo].[TRG_SAMPLE]
   ON  [dbo].[T_SAMPLE]
   AFTER UPDATE
AS
BEGIN
    INSERT INTO T_SMAPLE_TEST
    SELECT D.NAME + ',' + I.NAME
    FROM INSERTED I
        INNER JOIN DELETED D
        ON I.ID = D.ID 
    WHERE D.NAME <> I.NAME
END
GO

-- ######### test ######### 
-- below works fine
UPDATE T_SAMPLE
SET NAME = 'ONE2'
WHERE ID = 1
GO

-- test data by running below selects
SELECT * FROM T_SMAPLE_TEST
SELECT * FROM T_SAMPLE

-- but when try to update to null value from not null or vice versa, it doesn't work
UPDATE T_SAMPLE
SET NAME = NULL
WHERE ID = 1
GO

-- test data by running below selects
SELECT * FROM T_SMAPLE_TEST
SELECT * FROM T_SAMPLE

UPDATE T_SAMPLE
SET NAME = 'AGAIN'
WHERE ID = 1
GO

-- test data by running below selects
SELECT * FROM T_SMAPLE_TEST
SELECT * FROM T_SAMPLE

-- solution for this is to alter trigger as below
ALTER TRIGGER [dbo].[TRG_SAMPLE]
   ON  [dbo].[T_SAMPLE]
   AFTER UPDATE
AS
BEGIN
    INSERT INTO T_SMAPLE_TEST
    SELECT D.NAME + ',' + I.NAME
    FROM INSERTED I
        INNER JOIN DELETED D
        ON I.ID = D.ID 
    WHERE ISNULL(D.NAME,'') <> ISNULL(I.NAME,'')
END
GO

/*
DROP TABLE T_SMAPLE_TEST
DROP TABLE T_SAMPLE
DROP TRIGGER TRG_SAMPLE
*/
0
ru de
NULL はやや標準外の値です。 = を使用して確認することはできませんが、 WHERE value IS NULL 、または WHERE I.NAME IS NOT NULL を使用できます。
追加された 著者 SchmitzIT,
ご意見をお寄せいただきありがとうございます。私はあなたの回答を支持するつもりです。どちらの答えを受け入れるべきかは後で決めます。
追加された 著者 PawelCz,
私はこれを知っていますが、私の場合私はメインテーブルのデータが変更されたときはいつでもデータを挿入したいので値がnullでないところには適用できません
追加された 著者 PawelCz,

4 答え

これは、 NULL が未知の値として定義されているため、比較できないためです。
次の文はすべて空のレコードセットになります。

SELECT 1 WHERE NULL = 1
SELECT 1 WHERE NULL <> 1
SELECT 1 WHERE NULL = NULL
SELECT 1 WHERE NULL <> NULL

これらのステートメントは1を返しますが。

SELECT 1 WHERE ISNULL(NULL, 1) = 1
SELECT 1 WHERE ISNULL(NULL, 0) <> 1
SELECT 1 WHERE ISNULL(NULL, 0) = ISNULL(NULL, 0)
SELECT 1 WHERE NULL IS NULL

ところで、文字列とNULLを連結すると、代わりにNULLが返されます。
そのため、 SELECT D.NAME + '、' + I.NAME は、更新前に名前がnullだった場合、またはnullに更新中の場合はnullを返します。
それを避けるために、このテクニックを使うことができます:

SELECT  STUFF(
        ISNULL(','+ D.NAME, '') + 
        ISNULL(',' + I.NAME, '')
        , 1, 1, '')

両方の値がnullの場合のみNULLが返されますが、いずれかがnullでない場合はnullのみが返されます。 STUFF は、値の1つがnullでない場合に最初のコンマを削除するために使用されます。

ISNULL を使用しないようにする方法を探しているのなら、単にこれを実行できます。

WHERE 
(
    I.NAME <> D.NAME
    OR I.NAME IS NULL
    OR D.NAME IS NULL
) 
AND NOT 
(
I.NAME IS NULL 
AND D.NAME IS NULL
)

このように、それらのどれかがnullであるなら、あなたのwhere節はtrueを返すでしょう、そしてそれらのどれもnullではないがそれらは異なる値を保持するなら同様に。

もちろん、 ISNULL を使用すると、コードが短く、保守しやすくなりますが、それを避けたいと書いたのはあなたです...

1
追加された
まあ、私の答えの最初の部分はあなたに正確にその理由を伝えます。未知の値を他の未知の値とさえ比較することはできません。そのため、 =<> 、または <> をnullで使用しようとすると、常にfalseが返されるのはそのためです。
追加された 著者 Zohar Peled,
私の答えの最後の部分を読んでください、それはあなたが他の答えに与えたコメントを指します。
追加された 著者 Zohar Peled,
それからちょうど別の条件を加えなさい…私の編集された答えを見なさい。
追加された 著者 Zohar Peled,
そう、あなたは正しい、thnx
追加された 著者 PawelCz,
あなたの入力に対してThnx、それは望んでいないものをnull値にnull値に更新するならばレコードを作成します。これに対する解決策は、私の質問で指摘したようにISNULL関数を使用することです。
追加された 著者 PawelCz,
THNX、しかし連結はここでは問題ではありません。私はnull + 'value'がnullになることを知っています。 WHERE D.NAME <> I.NAMEに問題がありましたが、高価なWHERE ISNULL(D.NAME、 '')<> ISNULL(I.NAME、 '')に置き換える必要がありました。なぜ最初のバージョンがうまくいかないのか疑問に思いました。
追加された 著者 PawelCz,

nullと通常の比較は使用できません

これらはうまくいきません。

if null <> value
if null = value

nullのテストはこのようにする必要があります。

if value is not null
if value is null

またはこんな感じ

if isnull(value, '') = ''
if isnull(value, '') <> ''

あなたの場合は交換してください

WHERE D.NAME <> I.NAME

これとともに

WHERE isnull(D.NAME, '') <> isnull(I.NAME, '')

それはあなたの問題を解決するはずです

これも変更する

SELECT D.NAME + ',' + I.NAME

これに

SELECT isnull(D.NAME, '') + ',' + isnull(I.NAME, '')

名前の1つがnullの場合、連結文字列全体もnullになるため

1
追加された
私は全然これをしなければなりません。 Btw isnullは高価ではないので問題ありません
追加された 著者 GuidoG,
それがSQLのしくみだからです。 null値で比較することはできません。すべてのデータベースはそのようなものです。正常な動作です
追加された 著者 GuidoG,
I.NAMEは決して= nullになることはできません
追加された 著者 GuidoG,
私は自分の答えを更新します
追加された 著者 GuidoG,
ええ、私はこれを知っています。高価なISNULL機能は避けられないようです。
追加された 著者 PawelCz,
連結は問題ではありません、それはサンプルデータです、どこでisnull(D.NAME、 '')<> isnull(I.NAME、 '')は問題の解決策としてすでに私の答えにあります。私の質問はなぜトリガーがそのように振る舞うのですか?
追加された 著者 PawelCz,
I.NAMEがNOT NULLの場合、問題は解決されません。Nullでない値からNULL値に更新しても機能しません。私はこの問題に対する解決策を持っていますが、私のforstトリガがI.NAME値= nullであってもうまくいかなかったのは不思議
追加された 著者 PawelCz,

null比較以外の動作は ANSI_NULLS オプション このコードを試してください:

set ansi_nulls off
go
if 'jjj' <> null print 'it works with ansi_nulls off'
go
set ansi_nulls on
go
if 'jjj' <> null print 'it works ansi_nulls on'

そのため、ansi_nullsをオフにした場合の動作は予想どおりですが、これは従来のオプションであり、

将来のバージョンのSQL Serverでは、ANSI_NULLSは常にONになり、   このオプションを明示的にOFFに設定したアプリケーションはすべて生成されます。   エラーです。

何らかの理由でこのオプションをoffに設定したほうがいいのですが、あなたのトリガはansi_nullsをオンにして作成されているので、残りのコードがうまく動くとき、トリガが起動したとき作成されました

1
追加された
良いもの、これは私にとって新しいことです、THNX
追加された 著者 PawelCz,

問題は、 = を使用して NULL 値を確認できないことです。 NULL は特別ですが、 'IS NULL'または 'IS NOT NULL'を使用して確認できます。

次のコードを試してください。

ALTER TRIGGER [dbo].[TRG_SAMPLE]
   ON  [dbo].[T_SAMPLE]
   AFTER UPDATE
AS
BEGIN
    INSERT INTO T_SMAPLE_TEST
    SELECT D.NAME + ',' + I.NAME
    FROM INSERTED I
        INNER JOIN DELETED D
        ON I.ID = D.ID 
    WHERE I.NAME IS NOT NULL
END
GO

更新日:私のコメントに対するあなたの返事を見たところです。これでうまくいくでしょうか。

ALTER TRIGGER [dbo].[TRG_SAMPLE]
   ON  [dbo].[T_SAMPLE]
   AFTER UPDATE
AS
BEGIN
    INSERT INTO T_SMAPLE_TEST
    SELECT D.NAME + ',' + I.NAME
    FROM INSERTED I
        INNER JOIN DELETED D
        ON I.ID = D.ID 
    WHERE I.NAME <> D.NAME AND D.NAME IS NOT NULL
END
GO
1
追加された
@PawelCz - アップデートをチェックする
追加された 著者 SchmitzIT,
@PawelCz基本的に、何かを NULL と比較することはできません。 NULLNULL を比較することもできません(試してみてください。 SELECT CASE WHEN NULL = NULL THEN 1 ELSE 0 は0になります)。コードに NULL 比較も行わせたい場合は、明示的にそのように定義する必要があります(私のアップデートのように)。これはSQL全体としての制限です(SQL Serverに固有のものではありません)。 stackoverflow.com/も参照してください。 questions/15929269 /&hellip;
追加された 著者 SchmitzIT,
@PawelCz心配は要りません。しかし、NULLSは本当に苦痛です。私はよくNULLのために机に頭をぶつけました:D
追加された 著者 SchmitzIT,
ありがとう、私はこれを知っています。 ISNULLは方法だけでなく最も単純なものです。
追加された 著者 PawelCz,
うまくいかなかった、私の質問を注意深く読んでください。私はこの問題の解決策を持っています、私の質問はなぜそれがそのように振る舞うのですか、なぜトリガーの最初の部分がうまくいかないのですか?
追加された 著者 PawelCz,
何らかの変更があった場合にレコードを挿入したいと述べたので、データをNULLでない値からNULL値に変更した場合、これは機能しません
追加された 著者 PawelCz,