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
);