Условие соединения SQL либо A, либо B, но не оба, A и B - PullRequest
0 голосов
/ 08 февраля 2019

У меня есть данные о продажах по годам и кварталам, и за последний год я хочу заполнить недостающие кварталы последней доступной стоимостью.

Скажем, у нас есть исходная таблица:

+------+---------+-------+--------+
| year | quarter | sales | row_no |
+------+---------+-------+--------+
| 2018 |       1 |  4000 |      5 |
| 2018 |       2 |  6000 |      4 |
| 2018 |       3 |  5000 |      3 |
| 2018 |       4 |  3000 |      2 |
| 2019 |       1 |  8000 |      1 |
+------+---------+-------+--------+

Желаемые результаты:

+------+---------+-------+------------------------+
| year | quarter | sales |                        |
+------+---------+-------+------------------------+
| 2018 |       1 |  4000 |                        |
| 2018 |       2 |  6000 |                        |
| 2018 |       3 |  5000 |                        |
| 2018 |       4 |  3000 |                        |
| 2019 |       1 |  8000 |                        |
| 2019 |       2 |  8000 | <repeat the last value |
| 2019 |       3 |  8000 | <repeat the last value |
| 2019 |       4 |  8000 | <repeat the last value |
+------+---------+-------+------------------------+

Таким образом, задача состоит в том, чтобы сделать декартову год и четверть и оставить присоединенной к нейпродажи либо соответствующие, либо последние.

Этот код меня почти достал:

select r.year, k.quarter, t.sales
from (select distinct year        from [MyTable]) r cross join
     (select distinct quarter     from [MyTable]) k left join
     [MyTable] t
     on (r.year = t.year and k.quarter=t.quarter) or row_no=1

Как исправить последнюю строку (условие объединения), чтобы 2018 не удваивался?

Ответы [ 3 ]

0 голосов
/ 08 февраля 2019

Совсем другой подход с использованием CTE и некоторых оконных функций.Для этого не требуется 2 сканирования таблицы или треугольное соединение.

WITH VTE AS(
    SELECT *
    FROM (VALUES (2018,1,4000,5),
                 (2018,2,6000,4),
                 (2018,3,5000,3),
                 (2018,4,3000,2),
                 (2019,1,8000,1)) V([Year],[Quarter],sales, row_no)),
CTE AS(
    SELECT Y.Year,
           Q.Quarter,
           V.sales,
           V.row_no,
           COUNT(CASE WHEN V.sales IS NOT NULL THEN 1 END) OVER (ORDER BY Y.[Year], Q.[Quarter]
                                                                 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Grp
    FROM (VALUES(2018),(2019)) Y([Year])
         CROSS JOIN (VALUES(1),(2),(3),(4)) Q([Quarter])
         LEFT JOIN VTE V ON Y.[Year] = V.[Year] AND Q.[Quarter] = V.[Quarter])
SELECT C.[Year],
       C.[Quarter],
       MAX(C.sales) OVER (PARTITION BY C.Grp) AS Sales
FROM CTE C;

Это будет работать только на SQL Server 2012+ (так как ROWS BETWEEN был представлен в SQL Server 2012), однако, мы надеемся,вы не используете 2008 - который полностью (почти) полностью не поддерживается.

0 голосов
/ 08 февраля 2019

Я бы просто сделал JOIN:

SELECT TT.YEAR, TT.Quarter, COALESCE(T.SALES, MAX(T.SALES) OVER (PARTITION BY TT.YEAR)) AS sales 
FROM (SELECT DISTINCT T.YEAR, TT.Quarter
      FROM [MyTable] T CROSS JOIN
           ( SELECT DISTINCT TT.Quarter FROM [MyTable] TT ) TT
     ) TT LEFT JOIN 
     [MyTable] T 
     ON TT.YEAR = T.YEAR AND TT.Quarter = T.Quarter;

РЕДАКТИРОВАТЬ: Я просто неправильно прочитал вопрос о дополнительных quarter с, поэтому вам нужно APPLY в OUTERПРИСОЕДИНЯЙТЕСЬ:

SELECT TT.YEAR, TT.Quarter, COALESCE(T.SALES, T1.SALES) AS Sales 
FROM (SELECT DISTINCT T.YEAR, TT.Quarter
      FROM [MyTable] T CROSS JOIN
           ( SELECT DISTINCT TT.Quarter FROM [MyTable] TT ) TT
     ) TT LEFT JOIN 
     [MyTable] T 
     ON TT.YEAR = T.YEAR AND TT.Quarter = T.Quarter OUTER APPLY 
     ( SELECT TOP (1) T.*
       FROM [MyTable] T
       WHERE T.YEAR = TT.YEAR
       ORDER BY T.Quarter DESC
     ) T1;
0 голосов
/ 08 февраля 2019

Один метод использует external apply:

select y.year, q.quarter, t.sales
from (select distinct year from [MyTable]) y cross join
     (select distinct quarter from [MyTable]) q outer apply
     (select top (1) t.*
      from [MyTable] t
      where t.year < y.year or
            (t.year = y.year and t.quarter <= q.quarter)
      order by t.year desc, t.quarter desc
     ) t;

Для вашего объема данных это должно подойти.

Более эффективный метод - при условии, что вы назначаете значения только в конце- будет:

select y.year, q.quarter,
       coalesce(t.sales, tdefault.sales)
from (select distinct year from [MyTable]) y cross join
     (select distinct quarter from [MyTable]) q left join
     [MyTable] t
     on t.year = y.year and
        t.quarter = q.quarter cross join
     (select top (1) t.*
      from [MyTable] t
      order by t.year desc, t.quarter desc
     ) tdefault
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...