Как мне выполнить обновление Oracle SQL из select в определенном порядке?

#sql #oracle #sql-update

#sql #Oracle #sql-обновление

Вопрос:

У меня есть таблица со старыми значениями (некоторые null) и новыми значениями для различных атрибутов, все вставленные в разное время добавления в течение месяцев. Я пытаюсь обновить вторую таблицу записями с датами окончания рабочего месяца. Прямо сейчас эти записи содержат только самые последние новые значения для всех дат окончания месяца. Цель состоит в том, чтобы создать исторические данные, обновив значения на конец предыдущего месяца старыми значениями из первой таблицы. Я новичок и смог придумать запрос для обновления одного объекта, где была одна запись из первой таблицы. Теперь я пытаюсь расширить запрос, чтобы включить несколько объектов с возможными несколькими старыми значениями в течение одного месяца. Я попытался использовать «порядок по» (поскольку мне нужно делать обновления в течение месяца в порядке возрастания, чтобы получить последнее значение), но прочитал, что это не работает с инструкциями обновления без подзапроса. Итак, я попробовал свои силы в создании более сложного запроса, но безуспешно. Я получаю следующую ошибку: однорядный подзапрос возвращает более одной строки. Спасибо!

Таблица:

 | ID | TYPE | OLD_VALUE | NEW_VALUE | ADD_TIME|
-----------------------------------------------
| 1 | A     | 2 | 3 | 1/11/2019 8:00:00am |
| 1 | B     | 3 | 4 | 12/10/2018 8:00:00am|
| 1 | B     | 4 | 5 | 12/11/2018 8:00:00am|
| 2 | A     | 5 | 1 | 12/5/2018 08:00:00am|
| 2 | A     | 1 | 2 | 12/5/2019 09:00:00am|
| 2 | A     | 2 | 3 | 12/5/2019 10:00:00am|
| 2 | B     | 1 | 2 | 12/5/2019 10:00:00am|
  

TableB

 | ID | MONTH_END | TYPE_A | TYPE_B | 
-----------------------------------
| 1  | 1/31/19  | 3  | 5 |
| 1  | 12/31/18 | 3  | 5 |
| 1  | 11/30/18 | 3  | 5 |
| 2  | 12/31/18 | 3  | 2 |
| 2  | 11/30/18 | 3  | 2 |
  

Желаемый результат для TableB

 | ID | MONTH_END | TYPE_A | TYPE_B | 
-----------------------------------
| 1  | 1/31/19  | 3  | 5 |
| 1  | 12/31/18 | 2  | 5 |
| 1  | 11/30/18 | 2  | 3 |
| 2  | 12/31/18 | 3  | 2 |
| 2  | 11/30/18 | 5  | 2 |
  

Мой запрос для типа A (который я планирую адаптировать для типа B и выполнить также для желаемого результата)

 update TableB B
set b.type_a =
(
    with aa as
    (
    select id, nvl(old_value, new_value) typea, add_time
    from TableA 
    where type = 'A'
    order by id, add_time ascending
    )
select typea
from aa
where aa.id = b.id
and b.month_end <= aa.add_tm
)
where exists
(
    with aa as
    (
    select id, nvl(old_value, new_value) typea, add_time
    from TableA 
    where type = 'A'
    order by id, add_time ascending
    )
select typea
from aa
where aa.id = b.id
and b.month_end <= aa.add_tm
)
  

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

1. SQL — это декларативный язык, а не процедурный, поэтому порядок, в котором все происходит, не имеет значения. На самом деле важно, чтобы он этого не делал. Хорошо, что вы включили данные, но я не могу понять, как вы переходите от существующего вывода к желаемому выводу. Можете ли вы переформулировать соответствующие правила?

Ответ №1:

Kudo для предоставления примера входных данных и желаемого результата. Я нашел ваш вопрос немного запутанным, поэтому позвольте мне перефразировать: «Укажите значение последнего типа a из таблицы a, которое относится к тому же месяцу, что и конец месяца.

Сопоставив тип и дату ввода, мы можем получить ваш ответ. Значение «ROWNUM = 1» предназначено для ограничения набора результатов одной записью в случае, если имеется более одной строки с одинаковым add_time. Этот SQL все еще в беспорядке, возможно, кто-то другой может придумать что-то получше.

 UPDATE tableb b
   SET b.typea   =
           (SELECT old_value
              FROM tablea a
             WHERE     LAST_DAY( TRUNC( a.add_time ) ) = b.month_end
                   AND TYPE = 'A'
                   AND add_time =
                       (SELECT MAX( add_time )
                          FROM tablea
                         WHERE TYPE = 'A' AND LAST_DAY( TRUNC( a.add_time ) ) = b.month_end)
                   AND ROWNUM = 1)
 WHERE EXISTS
           (SELECT old_value
              FROM tablea a
             WHERE LAST_DAY( TRUNC( a.add_time ) ) = b.month_end AND TYPE = 'A');