SQL соединяет таблицу с собой для получения данных за предыдущий год - PullRequest
0 голосов
/ 19 ноября 2018

SQL. Как я могу присоединить таблицу к себе, чтобы получить желаемые результаты, как показано в таблице ниже. Логика в том, что я хочу иметь Единицы для того же продукта и соответствующего месяца предыдущего года.

Простое левое соединение исходной таблицы с самим собой по ключу a.[year]=b.[year]+1 (и, конечно, месяц к месяцу и продукт к продукту) приведет к потере данных, значения которых у нас были в предыдущем году, а сейчас нет.

enter image description here

Ответы [ 5 ]

0 голосов
/ 19 ноября 2018

Вы можете сгенерировать все возможные комбинации для года, месяца и продукта в ваших данных, используя 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       |
0 голосов
/ 19 ноября 2018

год месяц

Используйте cross join для генерации строк, left join для ввода данных и затем 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;
0 голосов
/ 19 ноября 2018

Я бы пошел с LAG и календарной таблицей .

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

Это немного отгадывает ваш дизайн (предполагается, что у вас есть таблица продуктов).

0 голосов
/ 19 ноября 2018

Если вы хотите, чтобы строки, в которых ничего не было продано как в 2017, так и в 2018 году, соответствовали желаемым результатам за март 2017 года, вам также нужно сгенерировать месяц, год и присоединиться к продукту, чтобы получить нулевые значения.

Этот запрос делает это для месяца и года, надеюсь, вы также сможете добавить Продукт, если потребуется

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);
0 голосов
/ 19 ноября 2018

Полного соединения должно быть достаточно

  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

Если вам нужно отфильтровать такие фьючерсы, вы можете добавить дополнительный предикат where, например:

where coalesce(a.year, b.year+1) <= year(getdate())
...