SQL 加入 table 自身以获取上一年的数据
SQL join table to itself to get data for previous year
SQL。我怎样才能将 table 连接到自身以获得所需的结果,如下面的 table 所示。逻辑是我想要相同产品和上一年相应月份的单位。
源 table 上键 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 Fiddle : https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=d01dc5bd626854b083be0864f2d5b0e4
结果:
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
如果您需要像那样过滤掉 futures,那么您可以添加一个额外的 where 谓词,例如:
where coalesce(a.year, b.year+1) <= year(getdate())
我会选择 LAG
和 calendar table。
SELECT C.[Year],
C.[Month],
YPT.product,
YST.units,
YST.LAG(YST.units) OVER (PARTITION BY YTP.[product],C.[month] ORDER BY C.[year]) AS UnitsPrev
FROM CalendarTable C
CROSS JOIN YourProductTable YPT
LEFT JOIN YourSourceTable YST ON C.[Year] YST.[Year]
AND C.[Month] = YST.[Month]
AND YPT.Product = YST.Product
WHERE C.[day] = 1
AND C.[date] BETWEEN {SomeStartDate} AND {SomeEndDate];
这对您的设计进行了一些猜测(假设您有一个产品 table)。
如果您想要 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);
年月
使用cross join
生成行,left join
引入数据,然后lag()
获取"previous"值:
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;
您可以使用 CROSS JOIN
在您的数据中生成年、月和产品的所有可能组合。如果存在特定组合的数据,一个简单的 LEFT JOIN
将为您提供值或 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 |
SQL。我怎样才能将 table 连接到自身以获得所需的结果,如下面的 table 所示。逻辑是我想要相同产品和上一年相应月份的单位。
源 table 上键 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 Fiddle : https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=d01dc5bd626854b083be0864f2d5b0e4
结果:
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
如果您需要像那样过滤掉 futures,那么您可以添加一个额外的 where 谓词,例如:
where coalesce(a.year, b.year+1) <= year(getdate())
我会选择 LAG
和 calendar table。
SELECT C.[Year],
C.[Month],
YPT.product,
YST.units,
YST.LAG(YST.units) OVER (PARTITION BY YTP.[product],C.[month] ORDER BY C.[year]) AS UnitsPrev
FROM CalendarTable C
CROSS JOIN YourProductTable YPT
LEFT JOIN YourSourceTable YST ON C.[Year] YST.[Year]
AND C.[Month] = YST.[Month]
AND YPT.Product = YST.Product
WHERE C.[day] = 1
AND C.[date] BETWEEN {SomeStartDate} AND {SomeEndDate];
这对您的设计进行了一些猜测(假设您有一个产品 table)。
如果您想要 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);
年月
使用cross join
生成行,left join
引入数据,然后lag()
获取"previous"值:
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;
您可以使用 CROSS JOIN
在您的数据中生成年、月和产品的所有可能组合。如果存在特定组合的数据,一个简单的 LEFT JOIN
将为您提供值或 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 |