2009-11-12 18 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