SQL将表连接到自身以获取上一年的数据

SQL将表连接到自身以获取上一年的数据,sql,sql-server,tsql,join,self-join,Sql,Sql Server,Tsql,Join,Self Join,SQL。如何将表连接到自身以获得所需的结果,如下表所示。逻辑是,我想为相同的产品和上一年的相应月份提供单位 源表上的简单左连接在键a.[year]=b.[year]+1上,当然,月到月和产品到产品会导致数据丢失,我们在上一年有值,现在没有值 完全联接就足够了 select distinct coalesce(a.year, b.year+1) as year , coalesce(a.month, b.month) as month , coalesce(a.prod

SQL。如何将表连接到自身以获得所需的结果,如下表所示。逻辑是,我想为相同的产品和上一年的相应月份提供单位

源表上的简单左连接在键a.[year]=b.[year]+1上,当然,月到月和产品到产品会导致数据丢失,我们在上一年有值,现在没有值


完全联接就足够了

  select distinct
    coalesce(a.year, b.year+1) as year
    , coalesce(a.month, b.month) as month
    , coalesce(a.product, b.product) as product
    , a.units as units
    , b.units as units_prev
  from yourtable a
  full join yourtable b on a.[year] = b.[year]+1 and a.[month] = b.[month] and a.product = b.product
但您的预期结果与2018年第2个月产品2的描述略有出入,之前的值为2933

DB小提琴:

结果:

year    month   product units   units_prev
2017    1       1       1721    
2017    2       1       4915    
2017    4       2       2933    
2017    5       1       5230    
2018    1       1               1721
2018    1       2       7672    
2018    2       1       5216    4915
2018    3       1       8911    
2018    4       2               2933
2018    5       1               5230
2019    1       2               7672
2019    2       1               5216
2019    3       1               8911
如果您需要像这样过滤期货,那么您可以添加一个额外的where谓词,类似于:

where coalesce(a.year, b.year+1) <= year(getdate())
我会带着滞后,和一个


对您的设计进行一点猜测,它假设您有一个产品表。

如果您想要2017年和2018年没有销售的行,以及2017年3月的预期结果,您需要生成月份、年份并加入产品以获得空值

这个查询会对月份和年份进行查询,如果需要,希望您也能够添加产品

DECLARE @startMonth INT=1
DECLARE @endMonth INT=12
DECLARE @startYear INT=2017
DECLARE @endYear INT=2018
;
WITH months AS (
    SELECT @startMonth AS m
    UNION ALL
    SELECT m+1 FROM months WHERE m+1<=@endMonth
),
years AS (
    SELECT @startYear AS y
    UNION ALL
    SELECT y+1 FROM years WHERE y+1<=@endYear
),
monthYears AS (
    SELECT m, y
    FROM months, years
)
SELECT  thisYear.[Year], thisYear.[Month], thisYear.[Product], thisYear.[Units], prevYear.[Units] as units_prev
FROM 
    (SELECT [Product], my.y as [Year], my.m as [Month], [Units]
    FROM monthYears my
    LEFT JOIN sales on my.m = [Month] and my.y = [Year]) as thisYear
LEFT OUTER JOIN     
    (SELECT [Product], my.y as [Year], my.m as [Month], my.y + 1 as NextYear, [Units]
    FROM monthYears my
    LEFT JOIN sales on my.m = [Month] and my.y = [Year])  as prevYear 
    on thisYear.Product = prevYear.Product
        and (thisYEAR.[Year]) = prevYear.[NextYear]
        and thisYEAR.[Month] = prevYear.[Month]
ORDER BY thisYear.[Year], thisYear.[Month], thisYear.[Product] 
option (maxrecursion 12);
年-月

使用交叉连接生成行,使用左连接引入数据,然后使用lag获取上一个值:

select y.year, m.month, p.product, t.units,
       lag(t.units) over (partition by p.product, m.month order by y.year) as prev_units
from (select distinct year from t) y cross join
     (select distinct month from t) m cross join
     (select distinct product from t) p left join
     t
     on t.year = y.year and t.month = m.month and t.product = p.producct;

您可以使用交叉联接在数据中生成年、月和产品的所有可能组合。如果存在特定组合的数据,简单的左连接将为您提供值或NULL

DECLARE @t TABLE (year int, month int, product int, unit int);
INSERT INTO @t VALUES
(2017, 1, 1, 1721),
(2017, 2, 1, 4915),
(2017, 5, 1, 5230),
(2018, 2, 1, 5216),
(2018, 3, 1, 8911),
(2017, 4, 2, 2933),
(2018, 1, 2, 7672);

SELECT ally.year, allm.month, allp.product, curr.units, prev.units AS units_prev
FROM (SELECT DISTINCT year FROM @t) AS ally
CROSS JOIN (SELECT DISTINCT product FROM @t) AS allp
CROSS JOIN (SELECT DISTINCT month FROM @t) AS allm
LEFT JOIN @t AS curr ON curr.year = ally.year AND curr.product = allp.product AND curr.month = allm.month
LEFT JOIN @t AS prev ON prev.year = ally.year - 1 AND prev.product = allp.product AND prev.month = allm.month
结果:

| year | month | product | units | units_prev |
|------|-------|---------|-------|------------|
| 2017 | 1     | 1       | 1721  | NULL       |
| 2017 | 2     | 1       | 4915  | NULL       |
| 2017 | 3     | 1       | NULL  | NULL       |
| 2017 | 4     | 1       | NULL  | NULL       |
| 2017 | 5     | 1       | 5230  | NULL       |
| 2017 | 1     | 2       | NULL  | NULL       |
| 2017 | 2     | 2       | NULL  | NULL       |
| 2017 | 3     | 2       | NULL  | NULL       |
| 2017 | 4     | 2       | 2933  | NULL       |
| 2017 | 5     | 2       | NULL  | NULL       |
| 2018 | 1     | 1       | NULL  | 1721       |
| 2018 | 2     | 1       | 5216  | 4915       |
| 2018 | 3     | 1       | 8911  | NULL       |
| 2018 | 4     | 1       | NULL  | NULL       |
| 2018 | 5     | 1       | NULL  | 5230       |
| 2018 | 1     | 2       | 7672  | NULL       |
| 2018 | 2     | 2       | NULL  | NULL       |
| 2018 | 3     | 2       | NULL  | NULL       |
| 2018 | 4     | 2       | NULL  | 2933       |
| 2018 | 5     | 2       | NULL  | NULL       |


令人惊讶的您不在[月]日加入?是这样还是你忘了?好吧,让我吃惊的是今年的合并,应该是b年+1年-技术上说,2019年,产品1的第3个月有8911个单位-但是你没有给出你的预期结果我没有[Day]。@PrzemyslawRemin C.[Day]=1不是YST.[Day]但是如果,你的意思是你的日历表没有日期栏,@PrzemyslawRemin我的问题是为什么?这是日历表中一个非常重要的列,我建议您对它进行更改以包含它。我没有[日期]列。只是因为我处理月度数据。我的[Units]列聚合为完整的月份。那么,@PrzemyslawRemin,在您的日历表中,是否只存储每月的第一天?如果是这样,那么只需从ON子句中删除和C.[Day]=1。如果没有,那么如果您的日历表中有一个月的每一天,为什么不在日历表中呢。然而,基于您的上述陈述,为什么您的日历表中没有每个日历日?这就是为什么它被称为日历表。为什么2017年4月1日有2933页?@SalmanA更正。Thx.您真的需要2017年的行吗?您可以简单地拥有5行输出,如product、month、2018\u units、2017_units@SalmanA是的,我确实需要该结构中每年的数据——比2018年和2017年还要多。我喜欢它的简单性,但它应该改进。它在我的数据的product列中生成三个null。在我的预期结果中,2017年和2018年都没有销售的产品2月份应该没有行。我想你的笛卡尔会为此产生行。请在代码的第二行加上别名好吗?@PrzemyslawRemin。是的,我知道您也希望产品交叉连接。这个查询是固定的。从显示的预期结果来看,我推测他们更喜欢从t y交叉连接选择不同的年份,从t mp选择不同的月份。@GordonLinoff差不多完成了。如果coalesceunits、prev_units不为null,那么“附加”是使查询获得所需结果的最佳方法吗?我不想要今年和上一年没有销售额的行。有些产品仅在一年中的特定月份销售。因为源表有几百万行,所以如果可能的话,我不希望同时删除这些杂草。@PrzemyslawRemin。是的,我想可以了。嗨,萨尔曼,1你的最终成绩中没有[年份]栏。2我不理解year=2018或year=2018-1表示选择*的条款。3[年份]列值在源表中是示例性的。2016年、2015年……为[年度]。。。。请不要参考数值。你能解释一下你的笛卡尔积是如何只产生10行而不是20行的吗。戈登·林诺夫的回答?对不起,我以为我们只谈了两年。它可以很容易地在n年内工作,但是滞后比两个左连接好。为什么你认为滞后比两个左连接好?性能似乎有利于自连接:这取决于。但我还是修改了我的答案。它现在与另一行完全相同,只是它使用左连接查找上一行。这个计划看起来稍微好一点。
| year | month | product | units | units_prev |
|------|-------|---------|-------|------------|
| 2017 | 1     | 1       | 1721  | NULL       |
| 2017 | 2     | 1       | 4915  | NULL       |
| 2017 | 3     | 1       | NULL  | NULL       |
| 2017 | 4     | 1       | NULL  | NULL       |
| 2017 | 5     | 1       | 5230  | NULL       |
| 2017 | 1     | 2       | NULL  | NULL       |
| 2017 | 2     | 2       | NULL  | NULL       |
| 2017 | 3     | 2       | NULL  | NULL       |
| 2017 | 4     | 2       | 2933  | NULL       |
| 2017 | 5     | 2       | NULL  | NULL       |
| 2018 | 1     | 1       | NULL  | 1721       |
| 2018 | 2     | 1       | 5216  | 4915       |
| 2018 | 3     | 1       | 8911  | NULL       |
| 2018 | 4     | 1       | NULL  | NULL       |
| 2018 | 5     | 1       | NULL  | 5230       |
| 2018 | 1     | 2       | 7672  | NULL       |
| 2018 | 2     | 2       | NULL  | NULL       |
| 2018 | 3     | 2       | NULL  | NULL       |
| 2018 | 4     | 2       | NULL  | 2933       |
| 2018 | 5     | 2       | NULL  | NULL       |