Sql server 使用合并复制行的完全外部联接

Sql server 使用合并复制行的完全外部联接,sql-server,tsql,sql-server-2005,join,Sql Server,Tsql,Sql Server 2005,Join,这是漫长的一天,也许这是一个简单的问题,但我还是被卡住了 基本上,我有两个类似的表销售和预测。我试图创建一个视图,它从两个表中选择行,并为给定的模型+月份+国家/地区选择任何行。如果两个表都包含数据,Sales具有优先级,这意味着应忽略Forecast行 为了简化查询,我使用了CTE。实际上,这两个表的模式不同,许多表是连接在一起的,而且forecast包含历史记录行,其中只应显示最后一个 我创建了一个简化的模式和数据,以向您展示我正在尝试做的事情: WITH Sales AS ( SE

这是漫长的一天,也许这是一个简单的问题,但我还是被卡住了

基本上,我有两个类似的表
销售
预测
。我试图创建一个视图,它从两个表中选择行,并为给定的模型+月份+国家/地区选择任何行。如果两个表都包含数据,
Sales
具有优先级,这意味着应忽略
Forecast

为了简化查询,我使用了CTE。实际上,这两个表的模式不同,许多表是连接在一起的,而且
forecast
包含历史记录行,其中只应显示最后一个

我创建了一个简化的模式和数据,以向您展示我正在尝试做的事情:

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
下面是一个sql示例数据:

结果:

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
此查询根据
ID
和源(最后一列)返回重复项


显然,
销售
行被该车型+月份+国家/地区组合的多个
预测
-行复制。如果
Sales
+
Forecast
行不存在重复项,如何仅获取
Sales
行?如果没有
Sales
行,如何获取
Forecast
行?

查询的问题不是使用
COALESCE
,而是简单地使用
JOIN
Forecast
表中有两行具有相同的
型号、月份、国家/地区组合,行ID为
2和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 ║
╚════╩═══════╩═════════════════════════╩═════════╩═══════╝
它们都与
Sales
表中的
ID
3行连接:

╔════╦═══════╦═════════════════════════╦═════════╦═══════╗
║ ID ║ Model ║          Month          ║ Country ║ Count ║
╠════╬═══════╬═════════════════════════╬═════════╬═══════╣
║  3 ║ ABC   ║ 2014-02-01 00:00:00.000 ║ Germany ║   900 ║
╚════╩═══════╩═════════════════════════╩═════════╩═══════╝
由于您的查询使用的是
COALESCE(s.ID,fc.ID)
,因此在结果中有两行
ID
3

提供了结果中重复行的原因。这里有一个解决方案:

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 ;

测试地点:

您似乎只想返回整个
销售
集合,并使用
预测
中未在
销售
中找到的条目对其进行补充。为此,我可能只使用UNION ALL:


因此,如果对于“车型+月份+国家/地区”组合,销售中有3行,预测中有5行,那么您只想从销售中获得这3行?问题是在
预测表中有2行具有相同的
车型、月份、国家/地区,行ID为2和3。它们都与
Sales
表中的ID 3行连接,因此在results@ypercube:是的,销售优先。如果已经有销售数据的话,预测是不相关的。@Lamak:很好。但是,即使我手动创建了示例数据,这也是可能的,因为实际上每个国家可能有不同的客户(在这个简化模式中没有显示)。但是我不能使用客户进行连接。@TimSchmelter如果这是可能的,那么您必须澄清在这种情况下您希望得到什么,因为会发生重复。例如,您需要对
Forecast
表进行分组,以在组合重复上述注释时返回最小/最大
ID
,好的捕获。但是我可以做什么来只选择销售行呢?我对预测数据不感兴趣,如果已经有某个国家/地区的销售数据。哦,是的。比我的要简单得多。你是不是在不存在(交集之前)中遗漏了一条FROM语句?@Rubio:我不是。这是一个从较少的选择引用列从外部级别。在这种情况下,它们是
预测
列。子查询获取
预测
的每一行,并将其子集与
销售
的等效投影相交,以查看是否没有匹配项。您也可以将其重写为不存在(从Sales WHERE forecast.Model=Sales.Model AND forecast.Month=Sales.Month AND forecast.Country=Sales.Country中选择*)
,效果相同。@Andriy:Hmm,很有趣。你知道吗?这在Oracle中也有效吗?@Rubio:据我所知,Oracle不支持较少的选择,但这并不意味着这种方法通常无法工作。我认为它应该也适用于Oracle,在
相交之前插入DUAL
中的
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 ;
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
);