成功したログイン試行と失敗したログイン試行の両方を保存するテーブルがdbにあります。 X日以上経過したレコードを削除できるストアドプロシージャを作成しています。これまでのところ良好ですが、私はそれを1つか2つ上げて、[Success]列がtrue、false、またはその両方であるかどうかのレコードを削除するかどうかを指定できるようにしたいと考えています。ただし、実行する必要のあるスクリプトの連結に問題があります。
これが私がこれまでに行ったことです:
-- CREATE PROCEDURE [dbo].[sp_delete_log_attempts]
DECLARE @backDays INT = 1 -- Default to 30 days (one test finishes)
DECLARE @successArg BIT = NULL -- default to both true and false success logins
DECLARE @successAnd VARCHAR(50)
DECLARE @query VARCHAR(MAX)
SET @successAnd = CASE
WHEN @successArg = 'true' THEN
'AND [Success] = ''true'''
WHEN @successArg = 'false' THEN
'AND [Success] = ''false'''
ELSE
'AND [Success] = ''true'' OR [Success] = ''false'''
END
PRINT @successAnd -- just for debugging purposes
SET @query = 'SELECT * FROM [audit].LoginAttempt WHERE [TimeStamp] <= DATEADD(day,-' + @backDays + ', GETDATE())'
EXEC @query
この時点では、@ backDays変数に基づいて行を選択しようとしているだけですが、@ query文字列を変数に連結できません。ここで何が間違っているのかわからない。ただし、動的クエリは初めてです。
ここでは動的SQLも必要ないと思います。クエリでは変数を使用できます。
SELECT *
FROM [audit].[LoginAttempt]
WHERE [TimeStamp] <= dateadd(day, -1 * @backDays, getdate())
AND CASE
WHEN @successArg IS NOT NULL
THEN (SELECT 1
WHERE [Success] = @successArg)
ELSE 1
END = 1;
(サンプルデータが提供されていないため、テストされていません。アイデアを示すためだけです。)
よく見ると、CASE ... END
全体が=
演算の左オペランドであることがわかります。操作の右側は1
です。
残念ながら、SQL ServerはCASE ... END
から直接返すことができるブール型を認識していません。 (その他、たとえばPostgreSQLは行います。)ブール演算に使用できる式を使用して、トリックする必要があります。そのため、=
操作を使用しています。
ここで、実際にテストしたい条件が満たされたときに、この操作をtrueと評価する方法が必要です。つまり、決定に達した場合、それらが満たされると、=
の左側に指定された値を1つ返すという考え方です。反対に、その値をそのまま使用します。その場合、=
はtrueになります。条件が満たされない場合、左側に他の値が返され、=
はtrueになりません。条件が満たされたことを示す値として、1
を選択します。これは、私たちが考えているブール値の表現に近いものです。 (しかし、ほとんど何でも選択できます(NOT NULL
、それ以外の場合は=
操作をIS NULL
に変更する必要がありました))。
では、条件が満たされたときに、左の式が1
を返すようにするにはどうすればよいでしょうか。
さて、CASE ... END
ステートメントを使用して、特定の条件に基づいて値を返すことができます。あなたはすでにCASE ... END
を知っています。これは、Cのような言語(または他の手続き型言語では名前が異なる)のswitch
またはif else
構成要素にいくぶん似ています。
テストする必要があるのは、入力変数@successArg
がnullかどうかです。 nullの場合、ログに記録されたログイン試行が成功したかどうかにかかわらず、呼び出し元は気にしません。それ以外の場合、@successArg
の値は、成功したログインのみ(@successArg = 1
)または失敗したログインのみ(@successArg = 0
)が必要かどうかを示します。これにより、最優先のケースが得られます。成功を無視するか、それを考慮に入れてください。したがって、CASE ... END
に関する@successArg IS NOT NULL
ブランチには2つの(メイン)ブランチがあります。
@successArg IS NULL
の場合の簡単なケースから始めましょう。それが「ELSE」ブランチです。ここでは、ログイン試行が成功したかどうかは関係ありません。成功したかどうかは(ブール式と考えると)、どの行でも常にtrueです。したがって、インジケーターを返します。これは一致でした、1
。
@successArg IS NULL
、@successArg
が値を保持している場合、行は列[Success]
にある必要があります。これは、@successArg = [Success]
の場合にのみ指定されます。すでに述べたように、@successArg = [Success]
だけでは、SQL Serverはそのコンテキストで処理できません。そのため、現在の行の1
が返されたときにtrueを示す値@successArg = [Success]
を返す必要があります。
これを行う方法の1つは、別の内部CASE ... WHEN
か、相関サブクエリを使用することです。外部クエリの現在の行の列の値を使用するため、これは「相関」しています。そして、それは外部クエリの位置から見ると「サブ」なので、「サブ」です。さらに、SQL Serverの機能を使用して、そのクエリのSELECT
がtrueの場合(または何もない場合)、FROM
のないWHERE
は1つのレコードを生成します。それ以外の場合は空のセット。それでは、1
のときに@successArg = [Success]
を保持する1つの列を持つレコードを作成してみましょう。列が1つしかないこのようなレコードは、必要に応じて暗黙的にスカラーにキャストされるため、外部の=
操作に適しています。 @successArg <> [Success]
の場合、そのサブクエリは空のセットを生成します。そのスカラーコンテキストの空のセットは、暗黙的にNULL
にキャストされます。 NULL = 1
は真実ではないので、これでうまくいきます。
これに関する1つの注意点:CASE ... END
を使用せずに、ブール式を使用することもできます。
SELECT *
FROM [audit].[LoginAttempt]
WHERE [TimeStamp] <= dateadd(day, -1 * @backDays, getdate())
AND (@successArg IS NULL
OR [Success] = @successArg);
それは上からの私たちの考えに従います。 @successArg IS NULL
の場合、他に関係なく「true」が必要です。それ以外の場合は、[Success] = @successArg
の場合にのみ「true」が必要です。それ以外の場合は「false」。 (注:前の条件のため、AND
の優先順位をOR
よりも優先するには、括弧が必要です。)
クエリがどのように実行されるか、つまりパフォーマンスに関しては、これが大きな違いになるとは思いません(正直なところ、これは確かにわかりません)。ただし、CASE ... WHEN
を使用したソリューションの方が読みやすく、保守が容易な場合があります。一方、ブール値のみのソリューションは、CASE ... END
に対応していないシステムでも機能します。