Единственная причина, по которой вы даже видите значения по умолчанию для preference3 в 1 и 2 CustomerID, заключается в том, что для preference3 существует NO tblCustomerPreference, а не потому, что для комбинации CustomerID = 1 / нет записи tblCustomerPreference. Preference3 и CustomerID = 2 / Preference3.
В вашем условии RIGHT JOIN вы указываете соединение только между tblCustomerPreference и tblPreference только для значения предпочтения - это когда-либо только материализует запись из tblPreference, у которой NO соответствует записи для ANY идентификатор клиента в tblCustomerPreference. Если вы добавите дополнительное условие соединения для customerID = @innerCustomerID для этого предложения, теперь вы будете делать то, что ищете: т.е. дать мне ALL записей предпочтений и ANY соответствия tblCustomerPreference для CustomerID=@innerCustomerID.
Попробуйте, просто добавив запись tblCustomerPreference для CustomerID 1 и Preference3, и вы заметите, что вы начнете видеть не только NULL для значения Prefernce3 для Customer2, но вы больше не будете получать результат для Customer 3.
Похоже, это то, что вы пытались сделать в своем предложении WHERE, но поскольку JOIN обрабатываются до предложения WHERE во время обработки запроса (после правильного логического порядка обработки операторов), вы получаете промежуточный набор результатов, который строго на основе комбинации предпочтений в отличие от комбинации предпочтений клиента и предпочтений.
Итак, пара небольших изменений, и вы должны быть хорошими. По сути, просто добавьте дополнительное условие в ваше предложение RIGHT JOIN, указывающее конкретного клиента, т.е. @innerCustomerID, и удалите все предложение WHERE, и все готово. Обратите внимание, что это также будет иметь побочный эффект фактического возврата всех значений по умолчанию для любого переданного @CustomerID, который даже не существует в качестве клиента - если вы хотите изменить это, чтобы ничего не возвращать для несуществующих клиентов, просто добавьте проверку перед запросом или включите фильтр where there ():
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