1

次のような3 つのテーブル ( tblPreferencetblCustomertblCustomerPreference) があります。

tblPreference:
ID       | Name            | DefaultValue
(int PK) | (nvarchar(100)) | (nvarchar(100))
-------------------------------
1        | Preference1     | 1
2        | Preference2     | Yes
3        | Preference3     | 1

tblCustomer:
CustomerID | ...
(int PK)
--------------------
1          | ...
2          | ...
3          | ...

tblCustomerPreference:
ID       | CustomerID | PreferenceID | Value
(int PK) | (int)      | (int)        | (nvarchar(100))
-------------------------------------------------------
1        | 1          | 1            | 0
2        | 1          | 2            | Yes
3        | 2          | 1            | 0
4        | 2          | 2            | No

このデータのピボットを作成しているので、次のストアド プロシージャを使用してすべてが 1 つの行になり、常にすべての設定が引き戻され、顧客固有の値が見つかった場合はそれが返され、それ以外の場合はデフォルト値が返されます。

CREATE PROCEDURE [dbo].[usp_GetCustomerPreferences] @CustomerID int AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @PivotColumns nvarchar(max)
    DECLARE @PivotColumnsSelectable nvarchar(max)
    SELECT @PivotColumns = COALESCE(@PivotColumns + ',','') + QUOTENAME(Preference.Name),
           @PivotColumnsSelectable = COALESCE(@PivotColumnsSelectable + ',' + Char(10),'') + Preference.Source + '.' + QUOTENAME(Preference.Name) + ' AS ' + QUOTENAME(Preference.Name)
    FROM (SELECT [Name],
                 'PreferencePivot' AS [Source]
          FROM [dbo].[tblPreference]) Preference

    DECLARE @sqlText nvarchar(max)
    SELECT @sqlText = 'SELECT ' + @PivotColumnsSelectable + '
    FROM (SELECT tblPreference.Name AS PreferenceName,
                CASE
                    WHEN tblCustomerPreference.Value IS NOT NULL THEN tblCustomerPreference.Value
                    ELSE tblPreference.DefaultValue
                END AS Value,
                @innerCustomerID AS CustomerID
            FROM tblCustomerPreference
                RIGHT JOIN tblPreference ON tblCustomerPreference.PreferenceID = tblPreference.ID
            WHERE (tblCustomerPreference.CustomerID = @innerCustomerID OR tblCustomerPreference.ID IS NULL)) data
            PIVOT (MAX(Value)
                   FOR PreferenceName IN (' + @PivotColumns + ')) PreferencePivot'

    EXECUTE sp_executesql @sqlText, N'@innerCustomerID int', @CustomerID
END

私が直面している問題は、CustomerID 1 または 2 を照会すると、期待どおりにすべての値が入力されて、すべてが期待どおりに返されることです。しかし、CustomerID 3 を照会するとNULL、他の顧客用に入力された PreferenceID に対して が返されます。式なしでクエリを実行するとPIVOT、期待どおりに入力されたすべての設定が返されます。データが忍び寄るのをピボットしたときだけNULLです。簡単なことを見逃していることを願っていますが、エラーは表示されません。

4

1 に答える 1

1

CustomerID の 1&2 に preference3 の既定値が表示されている唯一の理由は、preference3 のtblCustomerPreferenceレコードがないためであり、CustomerID=1 / Preference3 と CustomerID=2 / Preference3 の組み合わせの tblCustomerPreference レコードがないためではありません。

RIGHT JOIN 条件では、設定値のみで tblCustomerPreference と tblPreference の間でのみ結合するように指定しています。これは、tblCustomerPreference の顧客 ID に一致するレコードがない tblPreference からのレコードのみを実体ます。その句の customerID = @innerCustomerID に追加の結合条件を追加すると、探していることが実行されます。つまり、CustomerID=@innerCustomerID のすべての設定レコードと一致する tblCustomerPreference をすべて教えてください。

CustomerID 1 と Preference3 の tblCustomerPreference レコードを追加するだけで試してみると、Customer2 の Prefernce3 値が NULL になるだけでなく、Customer 3 の結果さえ得られないことがわかります。

これはあなたがWHERE句でやろうとしていたことのように見えますが、JOINはクエリ処理中にWHERE句の前に処理されるため(ステートメント処理の適切な論理順序に従って)、厳密に基づいた中間結果セットを取得しています顧客と好みの組み合わせではなく、好みの組み合わせ。

したがって、いくつかの小さな変更を加えれば、うまくいくはずです。基本的には、RIGHT JOIN 句に特定の顧客 (@innerCustomerID など) を指定する条件を追加し、WHERE 句全体を削除するだけで準備完了です。これには、渡された @CustomerID に対して、顧客として存在すらしていないすべてのデフォルト値が実際に返されるという副作用もあることに注意してください。存在しない顧客に対して何も返さないように変更する場合は、単純にチェックを追加します。クエリの前に、または where exists() フィルターを含めます。

alter PROCEDURE [dbo].[usp_GetCustomerPreferences] @CustomerID int AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @PivotColumns nvarchar(max)
    DECLARE @PivotColumnsSelectable nvarchar(max)
    SELECT @PivotColumns = COALESCE(@PivotColumns + ',','') + QUOTENAME(Preference.Name),
           @PivotColumnsSelectable = COALESCE(@PivotColumnsSelectable + ',' + Char(10),'') + Preference.Source + '.' + QUOTENAME(Preference.Name) + ' AS ' + QUOTENAME(Preference.Name)
    FROM (SELECT [Name],
                 'PreferencePivot' AS [Source]
          FROM [dbo].[tblPreference]) Preference

    DECLARE @sqlText nvarchar(max)
    SELECT @sqlText = 'SELECT ' + @PivotColumnsSelectable + '
    FROM (SELECT tblPreference.Name AS PreferenceName,
                CASE
                    WHEN tblCustomerPreference.Value IS NOT NULL THEN tblCustomerPreference.Value
                    ELSE tblPreference.DefaultValue
                END AS Value,
                @innerCustomerID AS CustomerID
            FROM tblCustomerPreference
                RIGHT JOIN tblPreference 
                ON tblCustomerPreference.PreferenceID = tblPreference.ID
                AND tblCustomerPreference.CustomerID = @innerCustomerID
            ) data
            PIVOT (MAX(Value)
                   FOR PreferenceName IN (' + @PivotColumns + ')) PreferencePivot'

 print @sqlText

    EXECUTE sp_executesql @sqlText, N'@innerCustomerID int', @CustomerID
END
于 2009-11-13T01:00:27.757 に答える