Опубликован: 28.12.2011 | Доступ: свободный | Студентов: 7482 / 955 | Оценка: 3.81 / 3.53 | Длительность: 19:30:00
ISBN: 978-5-9963-0488-2
Лекция 9:

Обновление данных в таблицах

< Лекция 8 || Лекция 9: 1234 || Лекция 10 >
Аннотация: Рассматриваются операции DML по внесению изменений в данные существующих таблиц: INSERT, UPDATE, DELETE и MERGE.

Обновление данных в таблицах

Операции SQL по изменению данных в БД — это принадлежащие категории DML INSERT, UPDATE и DELETE плюс вторичная по отношению к ним MERGE. Все они множественные (вслед за реляционной моделью, где это неслучайно), то есть в общем рассчитаны одним действием изменить сразу множество строк таблицы. Исключение составляет формально однострочная разновидность оператора INSERT.

Операции INSERT, UPDATE, DELETE и MERGE унаследованы в Oracle от стандарта SQL. В реляционной теории операций INSERT, UPDATE и DELETE как таковых нет, но они легко моделируются существующими другими.

Добавление новых строк

Добавление одной строки

Пример:

INSERT INTO proj ( projno ) VALUES ( 20 );

В общем случае имена столбцов (до слова VALUES) и значения (после слова VALUES) приводятся списками с равными количествами элементов.

Имена столбцов со свойством NOT NULL и значения для них должны присутствовать в списках обязательно, если только для них не определены умолчательные значения или если значения не заносятся триггерными процедурами. При несоблюдении этих условий возникает ошибка времени исполнения.

В то время как порядок перечисления выражений должен копировать порядок перечисления имен столбцов перед словом VALUES, сам порядок имен столбцов может быть произвольным. Об этом напоминает синтаксис, и это еще одно наследие в SQL реляционной теории. Следующие предложения равносильны:

INSERT 
INTO proj ( projno, pname, bdate, budget ) 
VALUES
   ( 30, 'BETA', SYSDATE, 20000 )
;
INSERT 
INTO proj ( bdate, budget, projno, pname )
VALUES
   ( SYSDATE, 20000, 30, 'BETA' )
;

Для последней пары запросов SQL допускает и более краткую формулировку:

INSERT INTO proj VALUES ( 30, 'BETA', SYSDATE, 20000 );

Эта краткость может показаться соблазнительной, однако подобная формулировка недопустима в реляционной БД, так как предполагает наличие порядка столбцов в таблице SQL, в то время как в отношениях реляционной модели порядка атрибутов не существует. С практической точки зрения полагаться в запросе на порядок столбцов — ненадежно, ведь Oracle разрешает добавлять и удалять столбцы в таблице; так что со временем порядок может нарушиться и операция окажется некорректной. Кроме того, перечисляя выражения для подстановки значений в поля добавляемой строки, программист, не имея перед глазами списка имен полей, легко может ошибиться.

Пример использования подзапроса в выражении во фразе VALUES:

INSERT INTO emp
   ( empno, deptno ) 
VALUES
   ( 1111, ( SELECT deptno FROM dept WHERE loc = 'CHICAGO' ) )
;

На деле это всего лишь пример использования скалярного подзапроса в построении выражения.

Добавление строк, полученных подзапросом

Множественный вариант INSERT предполагает добавление одним оператором в таблицу сразу группы строк. Добавляемые строки определяются оператором SELECT.

Пример:

DELETE FROM dept_copy;
INSERT INTO dept_copy SELECT * FROM dept WHERE deptno IN ( 10, 20 );
INSERT 
INTO dept_copy ( loc, dname, deptno )
   SELECT loc, INITCAP ( dname ), deptno + 100 FROM dept
;
INSERT INTO dept_copy ( deptno ) SELECT deptno + 200 FROM dept
;

Замечания

  • На формулировку SELECT в предложении INSERT никаких нарочных ограничений не накладывается (в том числе допускаются GROUP BY, агрегатные функции и тому подобное)
  • Типы столбцов должны быть совместимы.

Операция вставки INSERT в общем случае множественная. Ее однострочный вариант можно полагать частным случаем множественного.

Добавление строк одним оператором в несколько таблиц

С версии 9 строки, полученные подзапросом, можно "раскидать" по нескольким таблицам единственным оператором INSERT, например:

CREATE TABLE e1000 AS SELECT ename, sal FROM emp WHERE 1 = 2;
CREATE TABLE e2000 AS SELECT ename, sal FROM emp WHERE 1 = 2;
CREATE TABLE e3000 AS SELECT *          FROM emp WHERE 1 = 2;
INSERT ALL
   WHEN sal > 3000 THEN INTO e3000 
   WHEN sal > 2000 THEN INTO e2000 ( ename ) VALUES ( ename )
   WHEN sal > 1000 THEN INTO e1000           VALUES ( ename, sal )
SELECT * FROM emp WHERE job <> 'SALESMAN'
;

Упражнение. Проверьте содержимое таблиц E1000, E2000 и E3000. Сохраните их данные, удалите таблицы и выполните пример заново, указав вместо INSERT ALL слова INSERT FIRST. Сравните новое содержимое таблиц с предыдущим.

Последовательность проверок WHEN можно завершать проверкой ELSE.

Фразы WHEN … THEN можно опускать, тогда будет выполняться безусловная вставка строк в таблицы, например:

INSERT ALL
   WHEN comm IS NULL THEN INTO e1000 VALUES ( ename, sal )
   INTO e2000 ( ename ) VALUES ( ename )
   INTO e3000
SELECT * FROM emp WHERE job <> 'SALESMAN'
;

Вставка строк оператором INSERT разом в несколько таблиц эффективнее последовательности однотабличных INSERT, так как перемещает процедурную логику внутрь машины SQL СУБД. Это заметно при добавлении данных больших объемов. Кроме того, обновление таблиц выполняется применительно к одному состоянию БД, а потому логически не сводимо к последовательному выполнению команд INSERT.

Вот пример использования формально многотабличного оператора INSERT для занесения данных в БД с попутным выполнением переформатирования (похожее переформатирование, но только в запросе SELECT, а не при добавлении в таблицы БД может с версии 11 выполняться конструкцией SELECT ... FROM … UNPIVOT …).

Пример. Построим таблицу исходных данных:

CREATE TABLE e1 AS SELECT ename, sal, comm FROM emp;

Построим пустую таблицу для результата, где вместо двух полей SAL и COMM каждому сотруднику сопоставим отдельное поле с указанием вида платежа и отдельное поле для величины платежа:

CREATE TABLE e2 ( ename, amount )
  AS 
  SELECT ename, sal FROM emp WHERE 1 = 2
;
ALTER TABLE e2 
  ADD ( 
  payment VARCHAR2 ( 10 ) CHECK ( payment IN ( 'salary', 'commission' ) ) 
);

Теперь заполнение таблицы E2 можно выполнить следующим образом:

INSERT ALL 
WHEN sal  IS NOT NULL THEN INTO e2 VALUES ( ename, sal,  'salary' )
WHEN comm IS NOT NULL THEN INTO e2 VALUES ( ename, comm, 'commission' )
SELECT * FROM e1
;

Результат:

ENAME          AMOUNT PAYMENT
---------- ---------- ----------
SMITH             800 salary
ALLEN            1600 salary
WARD             1250 salary
JONES            2975 salary
MARTIN           1250 salary
BLAKE            2850 salary
CLARK            2450 salary
SCOTT            3000 salary
KING             5000 salary
TURNER           1500 salary
ADAMS            1100 salary
JAMES             950 salary
FORD             3000 salary
MILLER           1300 salary
ALLEN             300 commission
WARD              500 commission
MARTIN           1400 commission
TURNER              0 commission
18 rows selected.
< Лекция 8 || Лекция 9: 1234 || Лекция 10 >
Ярослав Прозоров
Ярослав Прозоров

В лекции № 7 "Введение в Oracle SQL" в подразделе "Несамостоятельность группировки с обобщениями ROLLUP, CUBE и GROUPING SETS"  представленная таблица сравнения содержит ошибки - окончания запросов пропущены. Видимо, ошибки вызваны некорректным переносом материала лекции.

Володимир Миколайчук
Володимир Миколайчук
Помогите разобраться поетапно с логикой запроса
-------TOOLS
NAME PRICE TYPE
drill 155 A
sawzall 192 N
mitre saw 292 M
router 86 I
RAD 145 M
jigsaw 128 I
screwdriver 77 P
------TOOL_TYPES
TYPE USAGE
A Always
I Often
M Sometimes
N Rarely
P Never

Запрос SQL:
SELECT t.type, SUM(t.price)
FROM tools t
GROUP BY t.type
HAVING SUM(t.price) >= (SELECT AVG(price)
FROM tools
WHERE type IN (SELECT type
FROM tool_types
WHERE usage = 'Often'));

И сколько строк он все таки вернет