Обновление, когда запрос не возвращает ни одной строки, в Oracle SQL - PullRequest
0 голосов
/ 08 июля 2019

Вопрос для начинающих.

Предпосылка : я понял, что здесь ключевой вопрос заключается в том, что в таблице SQL нет внутреннего порядка, поэтому для «добавления» столбцак таблице я должен указать какой-то ключ для соответствия.Тем не менее, я ищу элегантное решение.

Проблема : я хочу заменить значения столбца V1 преобразованием V1, полученным через оконную функцию (котораяЯ использую, чтобы заполнить NULL значения ближайшим известным значением V1, упорядочив по data).

Пример набора данных (обратите внимание, у меня нет ключа для идентификации строк):

create table Tab1(data date, V1 number, val number);
insert into Tab1 values (date '2000-01-01', 1, 100);
insert into Tab1 values (date '2000-02-01', 1, 110);
insert into Tab1 values (date '2000-03-01', 1, 100);
insert into Tab1 values (date '2000-03-01', 1, 130);
insert into Tab1 values (date '2000-05-01', NULL, 100);
insert into Tab1 values (date '2000-06-01', NULL, 100);
insert into Tab1 values (date '2000-03-01', 2, 110);
insert into Tab1 values (date '2000-03-01', 2, 105);
insert into Tab1 values (date '2000-04-01', 2, 190);
insert into Tab1 values (date '2000-05-01', NULL, 200);
insert into Tab1 values (date '2000-06-01', NULL, 150);

select * from Tab1;

DATA       V1   val
2000-01-01  1   100
2000-02-01  1   110
2000-03-01  1   100
2000-03-01  1   130
2000-04-01  1   100
2000-05-01      100
2000-06-01      100
2000-03-01  2   110
2000-03-01  2   105
2000-04-01  2   190
2000-05-01      200
2000-06-01      150

Я хочу избежать создания второй таблицы, как в

  create table Tab2 as
    select A.*, 
    (case when V1 is null 
               then last_value(V1) ignore nulls 
               over (partition by V1 order by data 
               range between unbounded preceding and 1 preceding) 
          else V1 
          end) V2
 from Tab1 A;

На самом деле, оконная функция все еще не выполняет то, что я хочу, но это отдельная проблема (тем не менее, если у вас есть решение, это очень приветствуется).В конце я хочу, чтобы V1 был заменен своей версией с нулями, замененными ближайшими не пропущенными значениями:

DATA       V1   val 
2000-01-01  1   100
2000-02-01  1   110
2000-03-01  1   100
2000-03-01  1   130
2000-04-01  1   100
2000-05-01  1   100
2000-06-01  1   100
2000-03-01  2   110
2000-03-01  2   105
2000-04-01  2   190
2000-05-01  2   200
2000-06-01  2   150

Я не могу использовать update, поскольку функции Windows не допускаютсяи подзапрос с функцией windows извлечет несколько строк.

Аналогично, оператор merge into не будет работать, так как я не могу дать условие on, которое идентифицирует одну строку для сопоставления (data и V1 не достаточно).

Есть ли способ просто "добавить" V2 к Tab1 без необходимости создавать новую таблицу?

Ответы [ 2 ]

2 голосов
/ 08 июля 2019

Сначала хорошие новости, которые являются ответом на ваш вопрос.

Аналогичным образом оператор слияния не будет работать, поскольку я не могу дать условие on, которое идентифицирует одну строку для сопоставления (данных и V1 недостаточно).

Вы можете использовать rowid для этого. Итак:

merge into tab1 t using
( select t1.rowid row_id, 
    (case when t1.V1 is null 
               then last_value(t1.V1) ignore nulls 
               over (partition by null order by data 
               range between unbounded preceding and 1 preceding) 
          else t1.V1 
          end) new_V1
 from Tab1 t1
) u
on ( t.rowid = u.row_id )
when matched then update set t.v1 = u.new_v1;

Теперь плохие новости.

Невозможно написать оконную функцию, чтобы делать то, что вы хотите.

Рассмотрим эти две строки в ваших входных данных:

DATA       V1   val
2000-01-01  1   100
2000-02-01  1   110
2000-03-01  1   100
2000-03-01  1   130
2000-04-01  1   100
2000-05-01      100   <== this one
2000-06-01      100
2000-03-01  2   110
2000-03-01  2   105
2000-04-01  2   190
2000-05-01      200   <== and this one
2000-06-01      150

В этих строках нет ничего, что наша логика могла бы использовать, чтобы знать, что первая должна быть обновлена ​​до V1 = 1, а вторая должна быть обновлена ​​до V1 = 2. Единственное, что у нас есть, - это порядок, в котором вы их перечислили, но эта информация нигде не сохраняется в вашей модели данных.

0 голосов
/ 08 июля 2019

Вам просто нужно поставить его по-другому.

Нет необходимости создавать другую таблицу.В этой ситуации будет работать предложение WITH.

Используйте следующий запрос:

-- your update query
UPDATE TAB1
SET
    V1 = (
        WITH UPDATED_VAL AS (
            SELECT /*+ materialize */
                A.ROWID   RID,
                ( LAST_VALUE(V1) IGNORE NULLS OVER(
                    PARTITION BY DATE1
                    ORDER BY
                        DATE1
                    RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
                ) ) V2
            FROM
                TAB1 A
        )
        SELECT
            V2
        FROM
            UPDATED_VAL
        WHERE
            TAB1.ROWID = UPDATED_VAL.RID
    )
WHERE
    V1 IS NULL

db <> fiddle demo

...