присоединиться к таблице Gap и Islands SQL Teradata

У меня есть данные, хранящиеся в БД в виде промежутков и островов, и мне нужно соединить их с другой таблицей. таблица зазоров и островов, как показано ниже:

|Subs_ID|ORIGINAL_STATUS|NEW_STATUS|CHANGE_DATE|
|-------+---------------+----------+-----------|
|123456 |1              |2         |12/2/2017  |
|123456 |2              |3         |12/8/2019  |
|123456 |3              |4         |12/18/2019 |
|123456 |4              |8         |12/28/2019 |
|123456 |8              |9         |10/4/2020  |

вторая таблица включает только Subs_ID и упорядоченный Connect_date

|Subs_ID|CONNECT_DATE|
|-------+------------|
|123456 |12/1/2017   |
|123456 |12/3/2017   |
|123456 |11/4/2018   |
|123456 |10/5/2019   |
|123456 |12/30/2019  |
|123456 |10/4/2020   |
|123456 |5/21/2021   |

Мне нужно присоединить текущий STATUS из первой таблицы ко второй, используя subs_id и dates. и результат будет ниже, где, если Connect_date меньше, чем Change_Date, возьмет первое ORIGINAL_STATUS, а если Connect_date больше, чем Change_Date, возьмет last NEW_STATUS

|Subs_ID|CONNECT_DATE|Status|
|-------+------------+------|
|123456 |12/1/2017   |1     |
|123456 |12/3/2017   |2     |
|123456 |11/4/2018   |2     |
|123456 |10/5/2019   |2     |
|123456 |12/30/2019  |8     |
|123456 |10/4/2020   |8     |
|123456 |5/21/2021   |9     |

person Ahmed Abdelkader    schedule 20.05.2021    source источник
comment
Как получить CONNECT_DATE первых двух строк?   -  person dnoeth    schedule 20.05.2021
comment
да, откуда в вашем выводе берутся данные CONNECT_DATE?   -  person eshirvana    schedule 20.05.2021
comment
Извините, это была моя ошибка при написании вопроса, я отредактировал сообщение.   -  person Ahmed Abdelkader    schedule 20.05.2021


Ответы (2)


Я надеюсь, что этот запрос поможет вам. Разбивка кода и объяснение приведены ниже

select a.*,B.ORIGINAL_STATUS
from mytable2 a
join (select *,LAG(CHANGE_DATE) OVER(ORDER BY CHANGE_DATE) as previous_date_value
from mytable)b
on a.Subs_ID =b.Subs_ID and a.CONNECT_DATE >b.previous_date_value and a.CONNECT_DATE<= b.CHANGE_DATE

UNION

select * from (
select a.*,CASE WHEN A.CONNECT_dATE<B.CHANGE_DATE THEN b.ORIGINAL_STATUS ELSE NULL END AS STATUS
from mytable2 a
join (
select Subs_ID,ORIGINAL_STATUS,NEW_STATUS,CHANGE_DATE from
(
select Subs_ID,ORIGINAL_STATUS,NEW_STATUS,CHANGE_DATE,row_number() over(partition by Subs_ID order by CHANGE_DATE)rn
from mytable
)mytable_less
where rn=1)b
on a.Subs_ID=b.Subs_ID)less_than_available_date
where status is not null

UNION

select * from (
select a.*,CASE WHEN A.CONNECT_dATE>B.CHANGE_DATE THEN b.NEW_STATUS ELSE NULL END AS STATUS
from mytable2 a
join (
select Subs_ID,ORIGINAL_STATUS,NEW_STATUS,CHANGE_DATE from
(
select Subs_ID,ORIGINAL_STATUS,NEW_STATUS,CHANGE_DATE,row_number() over(partition by Subs_ID order by CHANGE_DATE desc)rn
from mytable
)mytable_less
where rn=1)b
on a.Subs_ID=b.Subs_ID)greater_than_available_date
where status is not null

Объяснение: Первый блок кода возвращает статус даты подключения, если дата подключения присутствует в диапазоне доступных дат изменения. Я использую функцию задержки для получения предыдущего значения даты изменения и сравнения с датой подключения.

select a.*,B.ORIGINAL_STATUS
from mytable2 a
join (select *,LAG(CHANGE_DATE) OVER(ORDER BY CHANGE_DATE) as previous_date_value
from mytable)b
on a.Subs_ID =b.Subs_ID and a.CONNECT_DATE >b.previous_date_value and a.CONNECT_DATE<= b.CHANGE_DATE

Это дает нам следующий набор результатов

+---------+--------------+-----------------+
| Subs_ID | CONNECT_DATE | ORIGINAL_STATUS |
+---------+--------------+-----------------+
|  123456 | 2017-12-03   |               2 |
|  123456 | 2018-11-04   |               2 |
|  123456 | 2019-10-05   |               2 |
|  123456 | 2019-12-30   |               8 |
|  123456 | 2020-10-04   |               8 |
+---------+--------------+-----------------+

Теперь нам нужно найти статус на 01.12.2017, который меньше доступной даты изменения. таблица 2 объединяется с таблицей 1, которая имеет строку минимальной даты изменения, и ORIGINAL_STATUS берется, если дата подключения меньше даты изменения.

select * from (
select a.*,CASE WHEN A.CONNECT_dATE<B.CHANGE_DATE THEN b.ORIGINAL_STATUS ELSE NULL END AS STATUS
from mytable2 a
join (
select Subs_ID,ORIGINAL_STATUS,NEW_STATUS,CHANGE_DATE from
(
select Subs_ID,ORIGINAL_STATUS,NEW_STATUS,CHANGE_DATE,row_number() over(partition by Subs_ID order by CHANGE_DATE)rn
from mytable
)mytable_less
where rn=1)b
on a.Subs_ID=b.Subs_ID)less_than_available_date
where status is not null

+---------+--------------+--------+
| Subs_ID | CONNECT_DATE | STATUS |
+---------+--------------+--------+
|  123456 | 2017-12-01   |      1 |
+---------+--------------+--------+

Остаются записи, у которых дата подключения больше доступной даты изменения. Это достигается с помощью приведенного ниже кода, и таблица 2 объединяется с таблицей 1, имеющей строку с максимальной датой изменения, и новый статус принимается, если дата подключения больше даты изменения.

select * from (
select a.*,CASE WHEN A.CONNECT_dATE>B.CHANGE_DATE THEN b.NEW_STATUS ELSE NULL 
END AS STATUS
from mytable2 a
join (
select Subs_ID,ORIGINAL_STATUS,NEW_STATUS,CHANGE_DATE from
(
select Subs_ID,ORIGINAL_STATUS,NEW_STATUS,CHANGE_DATE,row_number() 
over(partition by Subs_ID order by CHANGE_DATE desc)rn
from mytable
)mytable_less
where rn=1)b
on a.Subs_ID=b.Subs_ID)greater_than_available_date
where status is not null

    +---------+--------------+--------+
    | Subs_ID | CONNECT_DATE | STATUS |
    +---------+--------------+--------+
    |  123456 | 2021-05-21   |      9 |
    +---------+--------------+--------+

Наконец, после применения union мы получаем требуемый набор результатов.

+---------+--------------+-----------------+
| Subs_ID | CONNECT_DATE | ORIGINAL_STATUS |
+---------+--------------+-----------------+
|  123456 | 2017-12-01   |               1 |
|  123456 | 2017-12-03   |               2 |
|  123456 | 2018-11-04   |               2 |
|  123456 | 2019-10-05   |               2 |
|  123456 | 2019-12-30   |               8 |
|  123456 | 2020-10-04   |               8 |
|  123456 | 2021-05-21   |               9 |
+---------+--------------+-----------------+
person PrakaasH M    schedule 21.05.2021
comment
@PrakaaH M большое спасибо за вашу помощь, я попробую данный код и проверю его производительность, поскольку я работал с огромным объемом данных. - person Ahmed Abdelkader; 23.05.2021

Обычно я решаю такие проблемы, избегая неэквивалентного соединения:

SELECT
   Subs_ID
  ,dt AS CONNECT_DATE
 -- fill the NULLs
 -- if the Connect_date more than the Change_Date will take the last NEW_STATUS  
  ,Coalesce(Lag(NEW_STATUS IGNORE NULLS)
            Over (PARTITION BY Subs_ID
                  ORDER BY dt)
 -- if the Connect_date less than the Change_Date will take the first ORIGINAL_STATUS 
           ,Lead(ORIGINAL_STATUS IGNORE NULLS)
            Over (PARTITION BY Subs_ID
                  ORDER BY dt)
           )
FROM 
 ( -- combine both tables
   SELECT
      1 AS x -- flag indicating the source tables
     ,Subs_ID
     ,ORIGINAL_STATUS
     ,NEW_STATUS
     ,CHANGE_DATE AS dt
   FROM t1
   UNION ALL
   SELECT
      2 AS x
     ,Subs_ID
     ,NULL -- to get the same number of columns
     ,NULL -- to get the same number of columns
     ,CONNECT_DATE 
   FROM t2
 ) AS t
QUALIFY x = 2 -- return only rows from t2
ORDER BY CONNECT_DATE
;

Чтобы увидеть, как это работает, прокомментируйте QUALIFY.

Если ваши данные позволяют удалить IGNORE NULLS в LEAD, это будет более эффективно (только один шаг STAT вместо двух).

person dnoeth    schedule 20.05.2021
comment
@dnoath запрос занимает много времени, так как в обеих таблицах миллионы или миллиарды строк, а во второй таблице есть другие столбцы, которые мне нужны для их использования, но они не включены в образец, чтобы упростить вопрос. - person Ahmed Abdelkader; 23.05.2021
comment
Какое количество строк в таблице и среднее/максимальное на Subs_ID? - person dnoeth; 23.05.2021
comment
первая таблица имеет 845 миллионов строк с подписками AVG 315 миллионов строк, а вторая таблица имеет миллиарды или строки и 13 миллионов строк в качестве подписок AVG. - person Ahmed Abdelkader; 23.05.2021
comment
315 миллионов строк на Subs_ID? Или 315 миллионов Subs_ID, то есть однозначные строки на значение? - person dnoeth; 23.05.2021
comment
Я имею в виду 315 миллионов различных Sub_ID. - person Ahmed Abdelkader; 24.05.2021
comment
Что ж, вы можете использовать неэквивалентное соединение на основе BETWEEN, но вам все равно нужно создать 6 строк с диапазонами дат из ваших 5 примеров строк в таблице 1 перед присоединиться. Если это распространенный запрос, вам следует изменить модель данных на медленно меняющееся измерение valid_from/valid_to. - person dnoeth; 25.05.2021