Как бы мне условно вставить строку на основе предыдущей строки?

#sql #google-bigquery #sql-insert #where-clause #window-functions

#sql #google-bigquery #sql-вставить #where-предложение #окно-функции

Вопрос:

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

Набор данных, с которым я работаю, будет выглядеть следующим образом

Исходная таблица

И я бы хотел, чтобы это выглядело следующим образом:

После изменения

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

Я пытаюсь выполнить это в консоли Google BigQuery.

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

1. вы действительно используете BigQuery? Вы приняли ответ, который не имеет шансов на выполнение в BigQuery и, таким образом, может ввести в заблуждение других пользователей, которые будут искать аналогичный вариант использования!

2. Я действительно использую BigQuery. И я должен спросить, что, по вашему мнению, я мог бы получить, задав вопрос с необходимостью, а затем приняв ответ, который на самом деле не удовлетворяет моей потребности? Нет, это не сработало, когда я использовал dateadd, но это сработало, когда я использовал date_add. В остальном, это абсолютно сработало для того, что мне было нужно, и я использую это прямо сейчас. Я пришел сюда, чтобы задать другой вопрос, который построен поверх моего использования этого. Именно так я обнаружил ваш комментарий.

Ответ №1:

Вы можете использовать union all и lead() :

 select item, store, start, end, sold
from t
union all
select item, store, dateadd(end, interval 1 day), dateadd(next_start, interval -1 day)
from (select item, store, end, lead(start) over (partition item, store start) as next_start
      from t
     ) t
where next_start  dateadd(end, interval 1 day);
  

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

1. Спасибо за это. Обычно мне не нужно выполнять функции такого типа, но это напомнило мне об использовании UNION ALL для такого типа вещей. Я ценю это. Мне нужно подумать о получении вашей книги!

Ответ №2:

Вы можете использовать оконные функции и insert ... select синтаксис для создания «отсутствующих» строк:

 insert into mytable (item, store, start, end, sold)
select 
    item,
    store, 
    dateadd(end, interval 1 day),
    dateadd(lead_start, interval -1 day),
     0
from (
    select 
        t.*,
        lead(start) over(partition by item, store order by start) lead_start
    from mytable t
) t
where lead_start > dateadd(end, interval 1 day)
  

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

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

Ответ №3:

Ниже приведен стандартный SQL BigQuery

 #standardSQL
SELECT * FROM `project.dataset.table` UNION ALL
SELECT * FROM (
  SELECT item, store,
    DATE_ADD(`end`, INTERVAL 1 DAY) new_start,
    DATE_SUB(LEAD(start) OVER(PARTITION BY item, store ORDER BY start), INTERVAL 1 DAY) new_end, 0
  FROM `project.dataset.table` 
)
WHERE new_start <= new_end
  

если применить к образцу данных из вашего вопроса — результат будет

 Row item    store   start       end         sold     
1   5       1       2020-01-01  2020-01-15  22   
2   5       1       2020-01-16  2020-01-31  0    
3   5       1       2020-02-01  2020-02-20  14      
  

В случае, если вы хотите вставить эти «отсутствующие» строки в свою таблицу — вы должны использовать только second select — который

 INSERT INTO `project.dataset.table`
SELECT * FROM (
  SELECT item, store,
    DATE_ADD(`end`, INTERVAL 1 DAY) new_start,
    DATE_SUB(LEAD(start) OVER(PARTITION BY item, store ORDER BY start), INTERVAL 1 DAY) new_end, 0
  FROM `project.dataset.table` 
)
WHERE new_start <= new_end 
  

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

1. То же, что и мой комментарий к следующему ответу; Я тоже вижу, как это сработало бы, но я выбрал ответ в качестве того, который я выбрал для первой попытки (который был первым ответом). Спасибо, что тоже оставили ответ!

2. дело не в том, кто первый! важнее, какой ответ правильный и из тех, что лучше других! но, по крайней мере, исправьте! очевидно, что вам решать, какой ответ принять, но вы вводите в заблуждение других пользователей, которые будут искать похожие варианты использования

3. Ну, я полагаю, я не квалифицирован, чтобы ответить, что лучше. Что я имел в виду своим комментарием, так это то, что я вижу, насколько ваш ответ правильный, но первый ответ также был правильным. Они оба решили бы мою проблему. Итак, я приму участие в том, как мне, возможно, следует решить, что лучше. Учитывая мой уровень новичка в SQL, я не понимаю, как я мог бы сказать, какой из приведенных здесь ответов лучше другого.

4. конечно. не волнуйтесь : o) вы все сделали правильно : o) вам действительно решать, что принять, за что проголосовать и т.д.