FULL OUTER JOIN dupliquer des lignes à l'aide de COALESCE

Ce fut une longue journée, peut-être que c'est une question simple mais je suis coincé quand même.

Fondamentalement, j'ai deux tables similaires Sales et Forecasts . J'essaie de créer une vue qui sélectionne les lignes des deux tables et sélectionne tout ce qui existe pour un model donné + mois + pays. Si les deux tables contiennent des données, Sales a la priorité, ce qui signifie que les lignes Forecast doivent être omises.

Pour simplifier la requête j'utilise CTE. En fait, le schéma des deux tables est différent et de nombreuses tables sont jointes. Forecasts contient également des lignes d'historique où seul le dernier doit être affiché.

J'ai créé un schéma simplifié et des données pour vous montrer ce que j'essaie de faire:

 WITH Sales AS ( SELECT ID, Model, Month, Country, Amount = Count, [Forecast / Sales] = 'Sales' FROM dbo.Sales ) , Forecasts AS ( SELECT ID, Model, Month, Country, Amount = Count, [Forecast / Sales] = 'Forecast' FROM dbo.Forecast ) SELECT ID = COALESCE(s.ID, fc.ID), Model = COALESCE(s.Model, fc.Model), Month = COALESCE(s.Month, fc.Month), Country = COALESCE(s.Country, fc.Country), Amount = COALESCE(s.Amount, fc.Amount), [Forecast / Sales] = COALESCE(s.[Forecast / Sales], fc.[Forecast / Sales]) FROM Sales s FULL OUTER JOIN Forecasts fc ON s.Model = fc.Model AND s.Month = fc.Month AND s.Country = fc.Country ORDER BY ID,Month,Country,Model 

Voici un sql-violon avec des données d'exemple: http://sqlfiddle.com/#!3/9081b/9/2

Résultat:

 ID MODEL MONTH COUNTRY AMOUNT FORECAST / SALES 1 ABC December, 01 2013 00:00:00+0000 Germany 777 Sales 2 ABC January, 01 2014 00:00:00+0000 Germany 999 Sales 3 ABC February, 01 2014 00:00:00+0000 Germany 900 Sales 3 ABC February, 01 2014 00:00:00+0000 Germany 900 Sales 4 ABC January, 01 2014 00:00:00+0000 UK 600 Forecast 4 ABC February, 01 2014 00:00:00+0000 UK 444 Sales 5 ABC March, 01 2014 00:00:00+0000 UK 500 Forecast 

Cette requête renvoie des duplicates en fonction de l' ID et de la source (dernière colonne).

 3 ABC February, 01 2014 00:00:00+0000 Germany 900 Sales 3 ABC February, 01 2014 00:00:00+0000 Germany 900 Sales 

Apparemment, les lignes Sales sont en cours de duplication à plusieurs niveaux de Forecast pour cette combinaison model + mois + pays. Comment puis-je get uniquement des lignes Sales si les lignes Sales + Forecast sont disponibles sans duplicates et Forecast s'il n'y a pas de lignes Sales ?

La réponse de Lamak fournit la raison des lignes dupliquées dans le résultat. Voici une solution:

 WITH Sales AS ( ... ) , Forecasts AS ( ...) , Combos AS -- get all distinct ( -- model + month + country SELECT Model, Month, Country -- combinations FROM Sales -- from Sales UNION -- this is UNION DISTINCT SELECT Model, Month, Country FROM Forecasts -- and Forecasts ) SELECT ID = COALESCE(s.ID, f.ID), c.Model, c.Month, c.Country, Amount = COALESCE(s.Amount, f.Amount), [Forecast / Sales] = COALESCE(s.[Forecast / Sales], f.[Forecast / Sales]) FROM Combos c LEFT JOIN Sales s ON s.Model = c.Model AND s.Month = c.Month AND s.Country = c.Country LEFT JOIN Forecasts f ON s.Model IS NULL -- join Forecasts only if there is no Sales AND f.Model = c.Model AND f.Month = c.Month AND f.Country = c.Country ORDER BY ID, Month, Country, Model ; 

Test chez: SQL-Fiddle

Le problème avec votre requête n'est pas l'utilisation de COALESCE , mais simplement avec le JOIN . Il y a 2 lignes dans le tableau Forecast qui ont la même combinaison de Model, Month, Country , lignes avec ID 2 et 3:

 ╔════╦═══════╦═════════════════════════╦═════════╦═══════╗ ║ ID ║ Model ║ Month ║ Country ║ Count ║ ╠════╬═══════╬═════════════════════════╬═════════╬═══════╣ ║ 2 ║ ABC ║ 2014-02-01 00:00:00.000 ║ Germany ║ 1100 ║ ║ 3 ║ ABC ║ 2014-02-01 00:00:00.000 ║ Germany ║ 900 ║ ╚════╩═══════╩═════════════════════════╩═════════╩═══════╝ 

Les deux rejoignent l' ID ligne 3 de la table Sales :

 ╔════╦═══════╦═════════════════════════╦═════════╦═══════╗ ║ ID ║ Model ║ Month ║ Country ║ Count ║ ╠════╬═══════╬═════════════════════════╬═════════╬═══════╣ ║ 3 ║ ABC ║ 2014-02-01 00:00:00.000 ║ Germany ║ 900 ║ ╚════╩═══════╩═════════════════════════╩═════════╩═══════╝ 

Et puisque votre requête utilise COALESCE(s.ID, fc.ID) , alors vous obtenez 2 lignes avec ID 3 dans les résultats

Il semble que vous souhaitiez simplement renvoyer l'intégralité de l'set de Sales et le compléter avec des inputs de Forecasts qui ne figurent pas dans Sales . Pour cela, j'utiliserais probablement UNION ALL comme ceci :

 WITH Sales AS ( ... ) , Forecasts AS ( ... ) SELECT ID, Model, Month, Country, Amount, [Forecast / Sales] FROM Sales UNION ALL SELECT ID, Model, Month, Country, Amount, [Forecast / Sales] FROM Forecasts WHERE NOT EXISTS ( SELECT Model, Month, Country INTERSECT SELECT Model, Month, Country FROM Sales );