2009-11-12 29 views
1

我有存在返回空值3個表(tblPreferencetblCustomertblCustomerPreference),看起來像下面這樣:SQL Server的動態支點當沒有數據

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 

我創建這個數據的一個支點所以這一切都在使用下面的存儲過程,它會一直拉回來所有的喜好單行,如果找到一個客戶特定值時,返回否則返回默認值:

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爲其他客戶填充的任何偏好ID。如果我在沒有PIVOT表達式的情況下運行查詢,它將返回按預期填充的所有首選項。只有當我穿透數據時,NULL纔會進入。我希望我錯過了一些簡單的事情,但我沒有看到錯誤。

回答

1

唯一的原因,你甚至看到在preference3默認值客戶ID的1 & 2是因爲有NO爲preference3 tblCustomerPreference紀錄,也不是因爲沒有對CustomerID = 1/Preference3的組合tblCustomerPreference記錄和CustomerID = 2/Preference3。

在你的右邊JOIN條件,您指定要上只喜好值tblCustomerPreference和tblPreference之間只能加入 - 這僅會兌現從tblPreference記錄具有用於任何的customerID在tblCustomerPreference NO匹配的記錄。如果您在該子句的customerID = @innerCustomerID上添加了一個額外的聯接條件,那麼您現在正在執行您要查找的內容:即給我全部優先記錄和ANY匹配CustomerID = @ innerCustomerID的tblCustomerPreference。

通過簡單地添加CustomerID 1和Preference3的tblCustomerPreference記錄來嘗試它,您會注意到您不僅會看到Customer2的Prefernce3值不僅爲NULL,而且您也不會再爲客戶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 
+0

太棒了!像我預料的那樣工作。考慮到它在pivoting之前創建臨時表,我認爲WHERE語句將在聚合中考慮。我將來必須記住這一點。 – 2009-11-13 14:25:08