FULL OUTER使用COALESCE连接复制行

Tim*_*ter 4 t-sql sql-server join sql-server-2005

这是漫长的一天,也许这是一个简单的问题,但无论如何我都被卡住了.

基本上我有两个相似的表SalesForecasts.我正在尝试创建一个视图,从两个表中选择行,并选择给定模型+月份+国家/地区的任何内容.如果两个表都包含数据,则Sales具有优先级,这意味着Forecast应省略行.

为了简化查询我正在使用CTE.实际上两个表的模式是不同的,并且连接了许多表,还Forecasts包含仅显示最后一个的历史行.

我创建了一个简化的架构和数据来向您展示我正在尝试做的事情:

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
Run Code Online (Sandbox Code Playgroud)

这是一个带有示例数据的sql-fiddle:http://sqlfiddle.com/#!3/9081b/9/2

结果:

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
Run Code Online (Sandbox Code Playgroud)

此查询根据ID和源(最后一列)返回重复项.

3   ABC February, 01 2014 00:00:00+0000 Germany 900 Sales
3   ABC February, 01 2014 00:00:00+0000 Germany 900 Sales
Run Code Online (Sandbox Code Playgroud)

显然,对于该模型+月份+国家/地区组合,Sales行会被多个Forecast箭头复制.Sales如果没有重复项可用Sales+ Forecast行,Forecast如果没有Sales行,我如何只获得行?

Lam*_*mak 6

您的查询的问题不是使用COALESCE,而只是使用JOIN.Forecast表中有2行具有相同的组合Model, Month, Country,行为ID2和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 ?
??????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

它们都与表中的第ID3 行连接Sales:

??????????????????????????????????????????????????????????
? ID ? Model ?          Month          ? Country ? Count ?
??????????????????????????????????????????????????????????
?  3 ? ABC   ? 2014-02-01 00:00:00.000 ? Germany ?   900 ?
??????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

并且由于您的查询正在使用COALESCE(s.ID, fc.ID),因此您ID在结果中获得2行,其中包含3 行


ype*_*eᵀᴹ 5

Lamak的答案提供了结果中重复行的原因.这是一个解决方案:

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 ;
Run Code Online (Sandbox Code Playgroud)

测试时间:SQL-Fiddle