oracle обновление с коррелированным запросом - PullRequest
3 голосов
/ 16 февраля 2020

Какой правильный ответ? Выберите два.

Изучите это SQL утверждение:

UPDATE orders o
SET customer_name = (
    SELECT cust_last_name FROM customers  WHERE customer_id=o.customer_id
);

Какие из двух истинны?

  • A. Подзапрос выполняется до выполнения оператора UPDATE.

  • B. Все существующие строки в таблице ORDERS будут обновлены.

  • C. Подзапрос выполняется для каждой обновленной строки в таблице ORDERS.

  • D. Оператор UPDATE выполняется успешно, даже если подзапрос выбирает несколько строк.

  • E. Подзапрос не является коррелированным подзапросом.

Я знаю, что B верен, но все остальные варианты, которые я считаю неправильными.

  • A. Подзапрос выполняется для каждой строки, которую возвращает внешний запрос, поэтому он должен выполняться после внешнего запроса.

  • C. НЕ для каждой обновленной строки, для каждой строки возвращается внешний запрос.

  • D. Я старался. Это вызывает ошибку ORA-01427: однострочный подзапрос возвращает более одной строки

  • E. Это коррелированный подзапрос.

Ответы [ 2 ]

1 голос
/ 16 февраля 2020

Рассмотрим вариант C:

C. Подзапрос выполняется для каждой обновленной строки в таблице ORDERS.

Вы сказали:

НЕ для каждой обновленной строки, именно для каждой строки возвращается внешний запрос .

Да. Подзапрос действительно выполняется для каждой строки во внешнем запросе (исключая возможные оптимизации, применяемые базой данных). И каждая строка во внешнем запросе обновляется - как вы заметили, так как вы уже и правильно выбрали опцию B: обновляются все существующие строки в таблице ORDERS .

Примечание: ваш аргументы против вариантов A, D и 3 действительны.

0 голосов
/ 16 февраля 2020

Единственный второй верный ответ -

F. это неправильный дизайн, денормализующий CUSTOMER_NAME в таблице orders и противоречащий ему с нормальной формой .

Ответ C может быть правильным где-то во времена Oracle 8 (то есть 20 лет go), но теперь это окончательно неверно! .

Oracle вводит скалярное кеширование подзапроса событие по причине для ограничения количества выполнений подзапросов .

Вот простая демонстрация

Эта настройка в Oracle 19.2 имеет 10 000 заказов и 1 000 клиентов.

create table customers as
select rownum customer_id, 'cust_'||rownum customer_name from dual connect by level <= 1000;

create index customers_idx1 on customers (customer_id);

create table orders as
select rownum order_id, trunc(rownum/10)+1 customer_id, cast (null as varchar2(100)) customer_name
from dual connect by level <= 10000;

Обновление выполняется на 100 000 строк, как и ожидалось

UPDATE /*+ gather_plan_statistics */ orders o
SET customer_name = (
    SELECT customer_name FROM customers  WHERE customer_id=o.customer_id
);

Подсказка gather_plan_statistics собирает статистику выполнения, которую мы рассмотрим.

SQL_ID  8r610vz9fknr6, child number 0
-------------------------------------
UPDATE /*+ gather_plan_statistics */ orders o SET customer_name = (     
SELECT customer_name FROM customers  WHERE customer_id=o.customer_id )

Plan hash value: 3416863305

--------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |
--------------------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                     |                |      1 |        |      0 |00:00:00.18 |   60863 |     21 |
|   1 |  UPDATE                              | ORDERS         |      1 |        |      0 |00:00:00.18 |   60863 |     21 |
|   2 |   TABLE ACCESS FULL                  | ORDERS         |      1 |  10000 |  10000 |00:00:00.01 |      21 |     18 |
|   3 |   TABLE ACCESS BY INDEX ROWID BATCHED| CUSTOMERS      |   1001 |      1 |   1000 |00:00:00.01 |    2020 |      3 |
|*  4 |    INDEX RANGE SCAN                  | CUSTOMERS_IDX1 |   1001 |      1 |   1000 |00:00:00.01 |    1020 |      3 |
--------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("CUSTOMER_ID"=:B1)

Информация об импатенте находится в столбце Start, мы видим, что к таблице customers обращались только 1001 раз, т.е. почти только один раз для клиента и не один раз для заказа,

...