我有3个表格(tblPreference
,tblCustomer
,tblCustomerPreference
),如下所示:
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
我正在创建此数据的一个数据透视,因此它使用以下存储过程将它们全部放在一行中,这样它将始终回退所有首选项,如果找到Customer特定值,它将返回,否则返回默认值值:
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,它将为任何为其他客户填充的PreferenceID返回NULL
。如果我在没有PIVOT
表达式的情况下运行查询,它将返回按预期填充的所有首选项。只有当我PIVOT数据时,NULL
才会进入。我希望我错过了一些简单的事情,但我没有看到错误。
答案 0 :(得分:1)
您甚至在CustomerID的1& 2中看到preference3默认值的唯一原因是因为preference3的 NO tblCustomerPreference记录,而不是因为没有针对CustomerID组合的tblCustomerPreference记录= 1 / Preference3和CustomerID = 2 / Preference3。
在您的RIGHT JOIN条件中,您指定仅在首选项值上仅在tblCustomerPreference和tblPreference之间进行连接 - 这将仅从具有 NO 匹配的记录的tblPreference中实现记录tblCustomerPreference中的任何 customerID。如果你在customerID = @innerCustomerID上为该子句添加了一个额外的连接条件,那么你现在正在做你想要的事情:即给我所有偏好记录和 ANY 匹配Customer ID = @ innerCustomerID的 通过简单地添加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