Вставка строк в существующую таблицу, охватывающих диапазон, указанный в 2 столбцах

#sql #oracle

#sql #Oracle

Вопрос:

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

Я хочу вставить номер недели в эту таблицу, чтобы, если продвижение длилось 2 недели, я бы видел идентификатор группы в двух отдельных строках с номерами двух недель в столбце рядом с ним и дублированием всей другой информации.

Я пытался работать с функцией model, но, похоже, не могу понять, как применить ее к моим данным.

Моя текущая таблица выглядит следующим образом:

 GROUP_ID   PROMOTION_DESCRIPTION            START_DATE  END_DATE   WEEKS  START_YEARWEEK   END_YEARWEEK   MEDIA_SUPPORT
40284      Gehwol / Wartner 2e halve prijs  27-06-17    01-07-17   1      201726           201726         Radio
40315      Voordeelzakken En Dozen          26-06-17    09-07-17   2      201726           201727         Online Campagne
  

И я хочу, чтобы это выглядело как:

 GROUP_ID   WEEK     PROMOTION_DESCRIPTION              MEDIA_SUPPORT
40284      201706   Gehwol / Wartner 2e halve prijs    Radio
40315      201726   Voordeelzakken En Dozen            Online Campagne
40315      201727   Voordeelzakken En Dozen            Online Campagne
  

Любая помощь была бы высоко оценена!

Ответ №1:

Это можно сделать, создав календарь и присоединив его к данным акции.

Приведенный ниже пример создает calendar подфактор, который включает по одной записи для каждой недели (включая 53-ю неделю) за 2017, 2018, 2019 годы, затем присоединяется к любой promotion записи, start_yearweek и end_yearweek которой пересекаются с этой календарной неделей.

 WITH CALENDAR AS (
    SELECT (100 * YEAR_OFFSET)   WEEK_OFFSET AS CALENDAR_WEEK
    FROM (
        SELECT LEVEL AS WEEK_OFFSET
        FROM DUAL
        CONNECT BY LEVEL <= 53)
             CROSS JOIN (SELECT 2016   LEVEL AS YEAR_OFFSET
                         FROM DUAL
                         CONNECT BY LEVEL <= 3))
SELECT GROUP_ID,
    CALENDAR_WEEK,
    PROMOTION_DESCRIPTION,
    MEDIA_SUPPORT
FROM CALENDAR
         INNER JOIN PROMOTION
ON CALENDAR_WEEK
    BETWEEN START_YEARWEEK AND END_YEAR_WEEK;
  

Результат:

   GROUP_ID   CALENDAR_WEEK PROMOTION_DESCRIPTION             MEDIA_SUPPORT
     40284          201726 Gehwol / Wartner 2e halve prijs   Radio
     40315          201726 Voordeelzakken En Dozen           Online Campagne
     40315          201727 Voordeelzakken En Dozen           Online Campagne


3 rows selected.
  

РЕДАКТИРОВАТЬ: Для создания календаря недели ISO можно использовать маску формата ‘IW’ в TO_DATE .

В этом примере календаря генерируются недели ISO с 2014 по 2022 год:

 SELECT DISTINCT TO_NUMBER(TO_CHAR((DATE '2014-01-01'   LEVEL),'YYYYIW'))
    AS ISO_CALENDAR_WEEK FROM DUAL 
CONNECT BY (DATE '2014-01-01'   LEVEL) < DATE '2023-01-01';
  

В примере продвижения это может быть использовано как:

 WITH CALENDAR AS (
    SELECT DISTINCT TO_NUMBER(TO_CHAR((DATE '2014-01-01'   LEVEL),'YYYYIW'))
        AS ISO_CALENDAR_WEEK FROM DUAL
    CONNECT BY (DATE '2014-01-01'   LEVEL) < DATE '2023-01-01' )
SELECT GROUP_ID,
    ISO_CALENDAR_WEEK,
    PROMOTION_DESCRIPTION,
    MEDIA_SUPPORT
FROM CALENDAR
         INNER JOIN PROMOTION
ON ISO_CALENDAR_WEEK
    BETWEEN START_YEARWEEK AND END_YEAR_WEEK;
  

Результат:

   GROUP_ID   ISO_CALENDAR_WEEK PROMOTION_DESCRIPTION             MEDIA_SUPPORT
     40315              201727 Voordeelzakken En Dozen           Online Campagne
     40284              201726 Gehwol / Wartner 2e halve prijs   Radio
     40315              201726 Voordeelzakken En Dozen           Online Campagne


3 rows selected.
  

Комментарии:

1. Привет, Alexgibbs, это отличный ответ, и он работает как шарм! Только я работаю с неделями ISO, поэтому они идут только на 52-ю неделю в 2016-2018 годах, но в 2015 году 53 недели. Если мне нужно объединить эти данные, как бы вы порекомендовали мне это сделать? Я мог бы легко удалить дополнительные недели в годах с 52 неделями, но есть ли какой-либо способ автоматизировать это при создании календаря? Спасибо!

2. Спасибо @J4YJ4Y. Конечно, календарь может быть изменен по мере необходимости. Я обновлю его примером ISO.

3. Ok @J4YJ4Y добавлен пример с неделями ISO

Ответ №2:

Этот рекурсивный запрос может это сделать:

 with c(rn, group_id, week, weeks, promotion_description, media_support) as (
  select 1, group_id, start_yearweek, weeks, promotion_description, media_support from t 
  union all
  select rn   1, group_id, week   1, weeks, promotion_description, media_support 
    from c where rn < weeks)
select group_id, week, weeks, promotion_description, media_support from c 
  

ДЕМОНСТРАЦИЯ

Комментарии:

1. Эй, обдумайте, спасибо за быстрый ответ! Хотя это будет работать для 2-недельных рекламных акций, что бы это сделало, если бы у меня была акция продолжительностью 33 недели?

2. Нет, рекурсивный запрос работает в течение любого количества недель, он выдаст 33 строки для этой записи. Вот dbfiddle , где я добавил строку с 33 неделями. Проблема только в том, что при изменении года мы должны назначить последнюю неделю (как я понял), что требует некоторых преобразований даты. В моей демонстрации вы можете увидеть пример.

Ответ №3:

Вы можете использовать такую комбинацию connect by level стилей ниже, чтобы создать больше строк в зависимости от ваших данных и ограничить их определенным количеством, управляя с помощью distinct ключевого слова.

 with t( group_id, promotion_description, start_yearweek, end_yearweek, media_support ) as
(
 select 40284, 'Gehwol / Wartner 2e halve prijs', 201726, 201726, 'Radio' from dual union all
 select 40315, 'Voordeelzakken En Dozen', 201726, 201727, 'Online Campagne' from dual 
), t1 as
(
  select group_id,  
         sum(end_yearweek - start_yearweek   1 ) as weeks
    from t
   group by group_id    
  )
  select distinct t1.group_id, start_yearweek   level - 1 as week,
         promotion_description, media_support 
   from t1
   join t on t1.group_id = t.group_id   
 connect by level <= weeks
  order by group_id, week;

 GROUP_ID   WEEK    PROMOTION_DESCRIPTION            MEDIA_SUPPORT
 --------   ------  -------------------------------  ---------------
 40284      201726  Gehwol / Wartner 2e halve prijs  Radio
 40315      201726  Voordeelzakken En Dozen          Online Campagne
 40315      201727  Voordeelzakken En Dozen          Online Campagne
  

P.S. Нет необходимости хранить weeks столбец, который может быть вычислен с помощью запроса.

Demo

Комментарии:

1. Привет, Барбарос, спасибо за этот отличный ответ! Кажется, это работает, пока у меня не появится повышение, которое длится 4 недели и начинается на 52 неделе года. Затем внезапно вместо 4 строк у меня получается 52 строки.. Есть ли какой-либо способ исправить это? Нравится работать с датой?

2. @J4YJ4Y добро пожаловать. Количество созданных строк зависит от разницы между start_yearweek и end_yearweek для каждой group_id . Это можно наблюдать, когда мы добавляем больше примеров операторов select в запрос, который создает таблицу t .