Операции 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, . Сохраните их данные, удалите таблицы и выполните пример заново, указав вместо 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.
Логически операция UPDATE вторична, так как сводима к последовательности DELETE и INSERT, но в системах SQL технически не отрабатывается. (Строго говоря, это не совсем так. Технически Oracle способна в некоторых случаях именно удалить запись в БД, представляющую строку таблицы, и добавить вместо нее новую, но это не единственный способ осуществления операции.) Она используется ради удобства применения. Любопытно, что если изменяется поле индексированного столбца и заодно с данными таблицы изменяется индекс, его изменение осуществляется ровно последовательным удалением из индекса старого значения и добавлением нового.
Примеры употребления:
UPDATE proj SET pname = 'GAMMA' WHERE projno = 15; UPDATE proj SET pname = 'GAMMA' , budget = budget * 1.05 WHERE projno = 15 ;
Поскольку речь идет об изменении значений полей уже существующих строк, в предложении UPDATE присутствует фраза WHERE, уточняющая множество строк для внесения изменения. Правила записи фразы WHERE те же, что и для предложения SELECT. Как и в SELECT, если в предложении UPDATE фраза WHERE не указана, изменение коснется всех строк источника данных.
Следующая форма UPDATE предполагает указание списка проставляемых в таблицу значений только однострочным подзапросом:
UPDATE proj SET ( pname, budget ) = ( SELECT pname || '1', budget * 0.95 FROM proj WHERE projno <= 10 ) ;
Упражнение. Проверьте, каков будет результат, если:
SELECT вернет более одной строки;SELECT не вернет не одной строки;BUDGET будет не заполнен (NULL);BUDGET будет частично заполнен.Еще пример формулирования предложения UPDATE:
UPDATE proj
SET budget =
CASE
WHEN pname = 'GAMMA' THEN budget
WHEN budget IS NULL THEN 0
ELSE NULL
END
;
На деле это всего лишь пример использования оператора CASE в построении выражения.
Операция UPDATE изменения существующих значений — множественная в силу своей формулировки.
Операции INSERT и UPDATE роднит то, что обе по сути выполняют присвоение значений. Далее говорится о связанных с этим общими их свойствами.
Выражение для значения поля добавляемой или изменяемой строки можно заменить словом DEFAULT (разрешено в SQL:1999). В случае, когда в определении столбца присутствует выражение для вычисления умолчательного значения, именно оно и будет вычислено, и результат занесен в поле. Если умолчательное значение столбца явно не задавалось, указание слова DEFAULT в качестве значения равносильно указанию NULL (можно полагать, что если в определении столбца конструкция DEFAULT явно не указана, молчаливо предполагается DEFAULT NULL).
Пример:
CREATE TABLE t ( r NUMBER, a NUMBER, b NUMBER DEFAULT 123 )
;
INSERT INTO t ( r, a, b ) VALUES ( 1, 1, 2 );
INSERT INTO t ( r, a, b ) VALUES ( 2, NULL, NULL );
INSERT INTO t ( r, a, b ) VALUES ( 3, DEFAULT, DEFAULT );
INSERT INTO t ( r ) VALUES ( 4 );
INSERT INTO t VALUES ( 5, DEFAULT, DEFAULT );
Проверка:
SQL> SELECT * FROM t;
R A B
---------- ---------- ----------
1 1 2
2
3 123
4 123
5 123
Необычное поведение традиционных операций сравнения (=, <> и др.) с NULL влечет непривычный эффект проверки добавленного в БД командами INSERT и UPDATE значения. Обратимся снова к таблице T из предыдущего примера:
SQL> VARIABLE n NUMBER
SQL> INSERT INTO t ( r, a ) VALUES ( 6, :n );
1 row created.
SQL> SELECT * FROM t WHERE r = 6 AND a = :n;
no rows selected
SQL> SELECT COUNT ( * ) FROM t WHERE r = 6;
COUNT(*)
----------
1
Такое поведение особенно неприятно в программе, и для привлечения внимания именно к этому контексту употребления вместо явного упоминания NULL в примере была применена переменная SQL*Plus. При простом объявлении переменной N она не получила никакого значения, так что появление NULL в запросах оказалось скрытым с ее помощью.
Можно вспомнить, что корни такого поведения Oracle уходят в стандарт SQL.
Основной оператор для удаления строк из таблицы — DELETE.
Примеры:
DELETE FROM proj WHERE projno = 16; DELETE FROM proj WHERE pname IS NULL; DELETE FROM dept_copy;
Поскольку удаляться могут только существующие строки, ради их указания в предложении DELETE присутствует в общем случае фраза WHERE.
Операция удаления строк DELETE — множественная в силу своей формулировки.
Вместо полного удаления строк командой DELETE (в отсутствии фразы WHERE), например, вместо
DELETE FROM dept_copy;
можно употреблять более быструю команду TRUNCATE TABLE:
TRUNCATE TABLE dept_copy;
Особенности TRUNCATE TABLE:
DELETE.Если очищенную от строк таблицу предполагается впоследствии снова заполнять, время последующего заполнения сократится, если выдать:
TRUNCATE TABLE dept_copy REUSE STORAGE;
В этом случае строки будут полагаться удаленными, а структура хранения данных таблицы в БД останется внешне неизменной.
В версии СУБД 9 появилась команда MERGE, позволяющая либо изменить существующие строки какой-нибудь таблицы, либо добавить — в зависимости от сформулированного условия. Условие формулируется на основе соединения целевой таблицы с другой (в общем случае — с источником данных, в качестве которого, однако, помимо обычной таблицы могут выступать еще представление данных, таблицы с внешним хранением с временным хранением данных, а также подзапрос).
Заполним таблицу BONUS данными о сотрудниках, положим, имеющих комиссионные:
INSERT INTO bonus SELECT ename, job, sal, comm FROM emp WHERE comm IS NOT NULL ;
Теперь обновим BONUS данными, "поступившими" из таблицы EMP. Если сотрудник из EMP уже есть в BONUS, повысим ему зарплату, а если нет — добавим к списку BONUS:
MERGE INTO bonus b USING emp e ON ( b.ename = e.ename ) WHEN MATCHED THEN UPDATE SET sal = sal * 10 WHEN NOT MATCHED THEN INSERT VALUES ( e.ename, e.job, e.sal, e.comm ) ;
Одна из двух проверок формально может и отсутствовать, что позволяет всего одной командой SQL просто добавить записи, отсутствующие в однотипной таблице.
В версии 10 фраза во фразе WHEN MATCHED можно дополнительно указать DELETE, например:
MERGE
INTO bonus b
USING emp e
ON ( b.ename = e.ename )
WHEN MATCHED THEN
UPDATE SET sal = sal / 10
DELETE WHERE sal < 1000
;
(Вернули BONUS в состояние до первого оператора MERGE).
Назначение операции MERGE — ускорить обновление больших таблиц. Обратите внимание, что обновление выполняется применительно к одному состоянию БД и поэтому логически несводимо к последовательному выполнению команд INSERT, UPDATE и, возможно, DELETE.
В процессе выполнения множественных операторов обновления данных таблиц изменение отдельной строки может вызвать ошибку — например, вылиться в попытку нарушить ограничение целостности. В таких случаях операция обновления прекращается, а изменения, успевшие произойти до возникновения проблемы, отменяются. Иными словами, операции DML обновления данных либо выполняются целиком, либо, в конечном счете, не выполняются вовсе.
Традиционная реакция на ошибки в процессе выполнения изменяющего данные оператора ("все или ничего") логически оправдана, но не всегда практична в случае больших объемов данных. В версии 10.2 введена возможность не отказываться от исполнения огульно, а вместо этого запоминать возникающие на отдельных строках ошибки в специально подготовленной таблице с целью последующего разбирательства. Специальную таблицу можно завести с помощью особой системной процедуры. Пример ее создания для таблицы EMP и дальнейшего употребления приводится ниже.
SQL> EXECUTE DBMS_ERRLOG.CREATE_ERROR_LOG ( 'EMP', 'ERR_EMP' )
PL/SQL procedure successfully completed.
SQL> INSERT INTO emp ( empno ) VALUES ( 1111 );
1 row created.
SQL> /
INSERT INTO emp ( empno ) VALUES ( 1111 )
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.PK_EMP) violated
SQL> INSERT INTO emp ( empno ) VALUES ( 1111 )
2 LOG ERRORS INTO err_emp ( 'today error' ) REJECT LIMIT 10;
0 rows created.
SQL> COLUMN ora_err_mesg$ FORMAT A45 WORD
SQL> COLUMN ora_err_tag$ FORMAT A12
SQL> COLUMN err# FORMAT 99999
SQL> COLUMN empno FORMAT A6
SQL> SELECT ora_err_number$ err#, ora_err_mesg$, ora_err_tag$, empno
SQL> FROM err_emp;
ERR# ORA_ERR_MESG$ ORA_ERR_TAG$ EMPNO
------ --------------------------------------------- ------------ ------
1 ORA-00001: unique constraint (SCOTT.PK_EMP) today error 1111
violated
Обратите внимание, что второй оператор INSERT строку не добавляет (правило первичного ключа нарушить нельзя), но и ошибку в программу не возвращает.
С версии 11 изменение данных в таблице можно запретить, переведя таблицу в состояние READ ONLY:
ALTER TABLE emp READ ONLY;
До этой версии запретить изменения можно было только одновременно во всех объектах, хранящих свои данные в конкретном
Для сеанса, выдающего команды DML на изменения данных, СУБД создает видимость, что они выполняются сразу в БД. Выполнив тут же запрос, пользователь увидит, будто данные изменились. На деле же они попадут в базу только после выдачи сеансом специальной команды фиксации изменений. Только после этого они станут видны прочим сеансам.
Все изменения со стороны индивидуальных команд DML заносятся Oracle в БД только группами, в рамках транзакции, по завершению транзакции. Команды завершения текущей транзакции:
COMMIT [WORK]; ROLLBACK [WORK];
Завершение транзакции с фиксацией изменений, внесенных операторами DML, происходит только по выдаче (а) команды COMMIT или (б) оператора DDL (скрытым образом завершающего свои действия по изменению таблиц словаря-справочника той же командой COMMIT).
Завершение транзакции с отменой изменений, внесенных операторами DML, происходит только по выдаче команды ROLLBACK, которая или явно выдается программистом, или, в некоторых случаях, неявно порождается самой СУБД (например, по результату аварийного останова работы программы из-за неперехваченной исключительной ситуации).
Упражнение. Вставьте запись в имеющуюся таблицу — откатите изменения. Вставьте запись — зафиксируйте. Создайте таблицу, вставьте запись — откатите изменения. Сохранилась ли таблица и ее данные? Создайте заполненную таблицу предложением CREATE TABLE … AS SELECT …. Откатите изменения.
Oracle нумерует все транзакции сквозным образом постоянно растущими номерами, называемыми COMMIT переводит БД в новое состояние, получающее номер зафиксированной транзакции. Данные БД в более ранних состояниях становятся после этого доступны только средствами (а) восстановления по резервным копиям и (б) "быстрого" восстановления (flashback).
В версии 11 Oracle разрешила не только аннулировать командой ROLLBACK изменения, совершавшиеся в рамках завершаемой транзакции, но также и отменять изменения, выполнявшиеся по очереди несколькими последними транзакциями (завершенными ранее командой COMMIT). Но делается это уже не операцией SQL, а программно, средствами системного пакета DBMS_FLASHBACK.
В версии 10 стало возможным с помощью системной переменной ("псевдостолбца") ORA_ROWSCN узнать
Пример:
SQL> CREATE TABLE dscn AS SELECT * FROM dept; Table created. SQL> UPDATE dscn SET dname = LOWER ( dname ) WHERE ROWNUM <= 2; 2 rows updated. SQL> SELECT dname, ora_rowscn FROM dscn; DNAME ORA_ROWSCN -------------- ---------- accounting 3812962 research 3812962 SALES 3812962 OPERATIONS 3812962 SQL> COMMIT; Commit complete. SQL> SELECT dname, ora_rowscn FROM dscn; DNAME ORA_ROWSCN -------------- ---------- accounting 3812999 research 3812999 SALES 3812999 OPERATIONS 3812999
Однако если создать таблицу с особым указанием, в ней появится скрытый столбец (длиною 6 байт), рассчитанный на хранение номера
SQL> DROP TABLE dscn; Table dropped. SQL> CREATE TABLE dscn ROWDEPENDENCIES AS SELECT * FROM dept; Table created. SQL> UPDATE dscn SET dname = LOWER ( dname ) WHERE ROWNUM <= 2; 2 rows updated. SQL> SELECT dname, ora_rowscn FROM dscn; DNAME ORA_ROWSCN -------------- ---------- accounting research SALES 3814027 OPERATIONS 3814027 SQL> COMMIT; Commit complete. SQL> SELECT dname, ora_rowscn FROM dscn; DNAME ORA_ROWSCN -------------- ---------- accounting 3814035 research 3814035 SALES 3814027 OPERATIONS 3814027
Эти данные можно использовать в программе, чтобы определить, изменялась ли строка с какого-то времени. Перевести
SELECT dname, SCN_TO_TIMESTAMP ( ora_rowscn ) FROM dscn;
Традиционно все существующие СУБД создавались как средства моделирования текущего COMMIT переводит БД в новое состояние, и предыдущие данные оказываются потерянными. Такое поведение проще программировать разработчикам СУБД, но оно не всегда удобно пользователям. В частности:
COMMIT, пусть даже совсем недавно, приходилось довольствоваться восстановлением по резервной копии БД, что долго и требует наличия собственно резервной копии;В версии 9 в Oracle открылись возможности получать от СУБД значения данных таблицы по состоянию на прошлый момент, невзирая на осуществлявшиеся за это время операции UNDO; использование свободного места в
Для (быстрого) запроса к прежним данным ссылку на таблицу во фразе FROM предложения SELECT следует сопроводить указанием конструкции AS OF.
Пример (выполнить в качестве упражнения последовательно):
DELETE FROM emp; COMMIT; SELECT * FROM emp; INSERT INTO emp SELECT * FROM emp AS OF TIMESTAMP ( SYSTIMESTAMP - INTERVAL '1' MINUTE ) ; SELECT * FROM emp;
(Синтаксис допускает употребление скобок в запросе выше, но не требует этого).
Момент для восстановления можно еще указать в терминах номера изменений данных в БД (System Change Number,
Подобное извлечение старых значений возможно только за период, когда к таблице не применялись команды DDL.
Давность воспроизводимых данных ограничивается размером свободного места в UNDO, которое определяется (а) интенсивностью изменений БД и (б) полным размером пространства.
В версии 10 появилась возможность вместо AS OF уточнять имя таблицы во фразе FROM конструкцией VERSIONS BETWEEN, позволяющей извлекать из БД историю изменения строк определенной давности. Следующим примером можно продолжить приводившийся только что код:
COLUMN versions_endtime FORMAT A22 COLUMN versions_starttime FORMAT A22 SELECT empno , sal , versions_starttime , versions_endtime , versions_xid , versions_operation FROM emp VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE ;
Прочие поля строк таблицы EMP в этом запросе не выданы из экономии места.
Упражнение: Измените несколько раз зарплаты разным сотрудникам и просмотрите историю изменений.
С версии 10 открылась возможность одной командой восстановить строки таблицы на момент в прошлом. Это позволяет вместо вышеуказанного INSERT … SELECT … AS OF записать
FLASHBACK TABLE emp TO TIMESTAMP ( SYSTIMESTAMP - INTERVAL '1' MINUTE ) ;
Тем не менее:
FLASHBACK было возможным, мы должны разрешить системе изменять физические адреса строк таблицы. Например, выдать: ALTER TABLE emp ENABLE ROW MOVEMENT;Для полного восстановления таблицы, ранее удалявшейся, команда FLASHBACK выглядит иначе. Вариант простого восстановления (из мусорной корзины):
FLASHBACK TABLE emp TO BEFORE DROP;
Вариант с переименованием (если с момента удаления таблица с именем EMP создавалась повторно):
FLASHBACK TABLE emp TO BEFORE DROP RENAME TO emp_old;
Если таблица переименовывалась несколько раз, вместо имени таблицы можно подставить внутреннее название для одной из старых версий таблицы, полученное из системной таблицы USER_RECYCLEBIN.
Технически такая возможность достигается сохранением старой структуры хранения ("сегмента") таблицы в DROP TABLE, что определяет границы применимости такого подхода.
Если же база данных работает в специальном режиме flashback, команда FLASHBACK позволяет восстановливать ее целиком:
FLASHBACK DATABASE TO TIMESTAMP SYSTIMESTAMP - INTERVAL '1' HOUR;
Операции 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, . Сохраните их данные, удалите таблицы и выполните пример заново, указав вместо 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.
Логически операция UPDATE вторична, так как сводима к последовательности DELETE и INSERT, но в системах SQL технически не отрабатывается. (Строго говоря, это не совсем так. Технически Oracle способна в некоторых случаях именно удалить запись в БД, представляющую строку таблицы, и добавить вместо нее новую, но это не единственный способ осуществления операции.) Она используется ради удобства применения. Любопытно, что если изменяется поле индексированного столбца и заодно с данными таблицы изменяется индекс, его изменение осуществляется ровно последовательным удалением из индекса старого значения и добавлением нового.
Примеры употребления:
UPDATE proj SET pname = 'GAMMA' WHERE projno = 15; UPDATE proj SET pname = 'GAMMA' , budget = budget * 1.05 WHERE projno = 15 ;
Поскольку речь идет об изменении значений полей уже существующих строк, в предложении UPDATE присутствует фраза WHERE, уточняющая множество строк для внесения изменения. Правила записи фразы WHERE те же, что и для предложения SELECT. Как и в SELECT, если в предложении UPDATE фраза WHERE не указана, изменение коснется всех строк источника данных.
Следующая форма UPDATE предполагает указание списка проставляемых в таблицу значений только однострочным подзапросом:
UPDATE proj SET ( pname, budget ) = ( SELECT pname || '1', budget * 0.95 FROM proj WHERE projno <= 10 ) ;
Упражнение. Проверьте, каков будет результат, если:
SELECT вернет более одной строки;SELECT не вернет не одной строки;BUDGET будет не заполнен (NULL);BUDGET будет частично заполнен.Еще пример формулирования предложения UPDATE:
UPDATE proj
SET budget =
CASE
WHEN pname = 'GAMMA' THEN budget
WHEN budget IS NULL THEN 0
ELSE NULL
END
;
На деле это всего лишь пример использования оператора CASE в построении выражения.
Операция UPDATE изменения существующих значений — множественная в силу своей формулировки.
Операции INSERT и UPDATE роднит то, что обе по сути выполняют присвоение значений. Далее говорится о связанных с этим общими их свойствами.
Выражение для значения поля добавляемой или изменяемой строки можно заменить словом DEFAULT (разрешено в SQL:1999). В случае, когда в определении столбца присутствует выражение для вычисления умолчательного значения, именно оно и будет вычислено, и результат занесен в поле. Если умолчательное значение столбца явно не задавалось, указание слова DEFAULT в качестве значения равносильно указанию NULL (можно полагать, что если в определении столбца конструкция DEFAULT явно не указана, молчаливо предполагается DEFAULT NULL).
Пример:
CREATE TABLE t ( r NUMBER, a NUMBER, b NUMBER DEFAULT 123 )
;
INSERT INTO t ( r, a, b ) VALUES ( 1, 1, 2 );
INSERT INTO t ( r, a, b ) VALUES ( 2, NULL, NULL );
INSERT INTO t ( r, a, b ) VALUES ( 3, DEFAULT, DEFAULT );
INSERT INTO t ( r ) VALUES ( 4 );
INSERT INTO t VALUES ( 5, DEFAULT, DEFAULT );
Проверка:
SQL> SELECT * FROM t;
R A B
---------- ---------- ----------
1 1 2
2
3 123
4 123
5 123
Необычное поведение традиционных операций сравнения (=, <> и др.) с NULL влечет непривычный эффект проверки добавленного в БД командами INSERT и UPDATE значения. Обратимся снова к таблице T из предыдущего примера:
SQL> VARIABLE n NUMBER
SQL> INSERT INTO t ( r, a ) VALUES ( 6, :n );
1 row created.
SQL> SELECT * FROM t WHERE r = 6 AND a = :n;
no rows selected
SQL> SELECT COUNT ( * ) FROM t WHERE r = 6;
COUNT(*)
----------
1
Такое поведение особенно неприятно в программе, и для привлечения внимания именно к этому контексту употребления вместо явного упоминания NULL в примере была применена переменная SQL*Plus. При простом объявлении переменной N она не получила никакого значения, так что появление NULL в запросах оказалось скрытым с ее помощью.
Можно вспомнить, что корни такого поведения Oracle уходят в стандарт SQL.
Основной оператор для удаления строк из таблицы — DELETE.
Примеры:
DELETE FROM proj WHERE projno = 16; DELETE FROM proj WHERE pname IS NULL; DELETE FROM dept_copy;
Поскольку удаляться могут только существующие строки, ради их указания в предложении DELETE присутствует в общем случае фраза WHERE.
Операция удаления строк DELETE — множественная в силу своей формулировки.
Вместо полного удаления строк командой DELETE (в отсутствии фразы WHERE), например, вместо
DELETE FROM dept_copy;
можно употреблять более быструю команду TRUNCATE TABLE:
TRUNCATE TABLE dept_copy;
Особенности TRUNCATE TABLE:
DELETE.Если очищенную от строк таблицу предполагается впоследствии снова заполнять, время последующего заполнения сократится, если выдать:
TRUNCATE TABLE dept_copy REUSE STORAGE;
В этом случае строки будут полагаться удаленными, а структура хранения данных таблицы в БД останется внешне неизменной.
В версии СУБД 9 появилась команда MERGE, позволяющая либо изменить существующие строки какой-нибудь таблицы, либо добавить — в зависимости от сформулированного условия. Условие формулируется на основе соединения целевой таблицы с другой (в общем случае — с источником данных, в качестве которого, однако, помимо обычной таблицы могут выступать еще представление данных, таблицы с внешним хранением с временным хранением данных, а также подзапрос).
Заполним таблицу BONUS данными о сотрудниках, положим, имеющих комиссионные:
INSERT INTO bonus SELECT ename, job, sal, comm FROM emp WHERE comm IS NOT NULL ;
Теперь обновим BONUS данными, "поступившими" из таблицы EMP. Если сотрудник из EMP уже есть в BONUS, повысим ему зарплату, а если нет — добавим к списку BONUS:
MERGE INTO bonus b USING emp e ON ( b.ename = e.ename ) WHEN MATCHED THEN UPDATE SET sal = sal * 10 WHEN NOT MATCHED THEN INSERT VALUES ( e.ename, e.job, e.sal, e.comm ) ;
Одна из двух проверок формально может и отсутствовать, что позволяет всего одной командой SQL просто добавить записи, отсутствующие в однотипной таблице.
В версии 10 фраза во фразе WHEN MATCHED можно дополнительно указать DELETE, например:
MERGE
INTO bonus b
USING emp e
ON ( b.ename = e.ename )
WHEN MATCHED THEN
UPDATE SET sal = sal / 10
DELETE WHERE sal < 1000
;
(Вернули BONUS в состояние до первого оператора MERGE).
Назначение операции MERGE — ускорить обновление больших таблиц. Обратите внимание, что обновление выполняется применительно к одному состоянию БД и поэтому логически несводимо к последовательному выполнению команд INSERT, UPDATE и, возможно, DELETE.
В процессе выполнения множественных операторов обновления данных таблиц изменение отдельной строки может вызвать ошибку — например, вылиться в попытку нарушить ограничение целостности. В таких случаях операция обновления прекращается, а изменения, успевшие произойти до возникновения проблемы, отменяются. Иными словами, операции DML обновления данных либо выполняются целиком, либо, в конечном счете, не выполняются вовсе.
Традиционная реакция на ошибки в процессе выполнения изменяющего данные оператора ("все или ничего") логически оправдана, но не всегда практична в случае больших объемов данных. В версии 10.2 введена возможность не отказываться от исполнения огульно, а вместо этого запоминать возникающие на отдельных строках ошибки в специально подготовленной таблице с целью последующего разбирательства. Специальную таблицу можно завести с помощью особой системной процедуры. Пример ее создания для таблицы EMP и дальнейшего употребления приводится ниже.
SQL> EXECUTE DBMS_ERRLOG.CREATE_ERROR_LOG ( 'EMP', 'ERR_EMP' )
PL/SQL procedure successfully completed.
SQL> INSERT INTO emp ( empno ) VALUES ( 1111 );
1 row created.
SQL> /
INSERT INTO emp ( empno ) VALUES ( 1111 )
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.PK_EMP) violated
SQL> INSERT INTO emp ( empno ) VALUES ( 1111 )
2 LOG ERRORS INTO err_emp ( 'today error' ) REJECT LIMIT 10;
0 rows created.
SQL> COLUMN ora_err_mesg$ FORMAT A45 WORD
SQL> COLUMN ora_err_tag$ FORMAT A12
SQL> COLUMN err# FORMAT 99999
SQL> COLUMN empno FORMAT A6
SQL> SELECT ora_err_number$ err#, ora_err_mesg$, ora_err_tag$, empno
SQL> FROM err_emp;
ERR# ORA_ERR_MESG$ ORA_ERR_TAG$ EMPNO
------ --------------------------------------------- ------------ ------
1 ORA-00001: unique constraint (SCOTT.PK_EMP) today error 1111
violated
Обратите внимание, что второй оператор INSERT строку не добавляет (правило первичного ключа нарушить нельзя), но и ошибку в программу не возвращает.
С версии 11 изменение данных в таблице можно запретить, переведя таблицу в состояние READ ONLY:
ALTER TABLE emp READ ONLY;
До этой версии запретить изменения можно было только одновременно во всех объектах, хранящих свои данные в конкретном
Для сеанса, выдающего команды DML на изменения данных, СУБД создает видимость, что они выполняются сразу в БД. Выполнив тут же запрос, пользователь увидит, будто данные изменились. На деле же они попадут в базу только после выдачи сеансом специальной команды фиксации изменений. Только после этого они станут видны прочим сеансам.
Все изменения со стороны индивидуальных команд DML заносятся Oracle в БД только группами, в рамках транзакции, по завершению транзакции. Команды завершения текущей транзакции:
COMMIT [WORK]; ROLLBACK [WORK];
Завершение транзакции с фиксацией изменений, внесенных операторами DML, происходит только по выдаче (а) команды COMMIT или (б) оператора DDL (скрытым образом завершающего свои действия по изменению таблиц словаря-справочника той же командой COMMIT).
Завершение транзакции с отменой изменений, внесенных операторами DML, происходит только по выдаче команды ROLLBACK, которая или явно выдается программистом, или, в некоторых случаях, неявно порождается самой СУБД (например, по результату аварийного останова работы программы из-за неперехваченной исключительной ситуации).
Упражнение. Вставьте запись в имеющуюся таблицу — откатите изменения. Вставьте запись — зафиксируйте. Создайте таблицу, вставьте запись — откатите изменения. Сохранилась ли таблица и ее данные? Создайте заполненную таблицу предложением CREATE TABLE … AS SELECT …. Откатите изменения.
Oracle нумерует все транзакции сквозным образом постоянно растущими номерами, называемыми COMMIT переводит БД в новое состояние, получающее номер зафиксированной транзакции. Данные БД в более ранних состояниях становятся после этого доступны только средствами (а) восстановления по резервным копиям и (б) "быстрого" восстановления (flashback).
В версии 11 Oracle разрешила не только аннулировать командой ROLLBACK изменения, совершавшиеся в рамках завершаемой транзакции, но также и отменять изменения, выполнявшиеся по очереди несколькими последними транзакциями (завершенными ранее командой COMMIT). Но делается это уже не операцией SQL, а программно, средствами системного пакета DBMS_FLASHBACK.
В версии 10 стало возможным с помощью системной переменной ("псевдостолбца") ORA_ROWSCN узнать
Пример:
SQL> CREATE TABLE dscn AS SELECT * FROM dept; Table created. SQL> UPDATE dscn SET dname = LOWER ( dname ) WHERE ROWNUM <= 2; 2 rows updated. SQL> SELECT dname, ora_rowscn FROM dscn; DNAME ORA_ROWSCN -------------- ---------- accounting 3812962 research 3812962 SALES 3812962 OPERATIONS 3812962 SQL> COMMIT; Commit complete. SQL> SELECT dname, ora_rowscn FROM dscn; DNAME ORA_ROWSCN -------------- ---------- accounting 3812999 research 3812999 SALES 3812999 OPERATIONS 3812999
Однако если создать таблицу с особым указанием, в ней появится скрытый столбец (длиною 6 байт), рассчитанный на хранение номера
SQL> DROP TABLE dscn; Table dropped. SQL> CREATE TABLE dscn ROWDEPENDENCIES AS SELECT * FROM dept; Table created. SQL> UPDATE dscn SET dname = LOWER ( dname ) WHERE ROWNUM <= 2; 2 rows updated. SQL> SELECT dname, ora_rowscn FROM dscn; DNAME ORA_ROWSCN -------------- ---------- accounting research SALES 3814027 OPERATIONS 3814027 SQL> COMMIT; Commit complete. SQL> SELECT dname, ora_rowscn FROM dscn; DNAME ORA_ROWSCN -------------- ---------- accounting 3814035 research 3814035 SALES 3814027 OPERATIONS 3814027
Эти данные можно использовать в программе, чтобы определить, изменялась ли строка с какого-то времени. Перевести
SELECT dname, SCN_TO_TIMESTAMP ( ora_rowscn ) FROM dscn;
Традиционно все существующие СУБД создавались как средства моделирования текущего COMMIT переводит БД в новое состояние, и предыдущие данные оказываются потерянными. Такое поведение проще программировать разработчикам СУБД, но оно не всегда удобно пользователям. В частности:
COMMIT, пусть даже совсем недавно, приходилось довольствоваться восстановлением по резервной копии БД, что долго и требует наличия собственно резервной копии;В версии 9 в Oracle открылись возможности получать от СУБД значения данных таблицы по состоянию на прошлый момент, невзирая на осуществлявшиеся за это время операции UNDO; использование свободного места в
Для (быстрого) запроса к прежним данным ссылку на таблицу во фразе FROM предложения SELECT следует сопроводить указанием конструкции AS OF.
Пример (выполнить в качестве упражнения последовательно):
DELETE FROM emp; COMMIT; SELECT * FROM emp; INSERT INTO emp SELECT * FROM emp AS OF TIMESTAMP ( SYSTIMESTAMP - INTERVAL '1' MINUTE ) ; SELECT * FROM emp;
(Синтаксис допускает употребление скобок в запросе выше, но не требует этого).
Момент для восстановления можно еще указать в терминах номера изменений данных в БД (System Change Number,
Подобное извлечение старых значений возможно только за период, когда к таблице не применялись команды DDL.
Давность воспроизводимых данных ограничивается размером свободного места в UNDO, которое определяется (а) интенсивностью изменений БД и (б) полным размером пространства.
В версии 10 появилась возможность вместо AS OF уточнять имя таблицы во фразе FROM конструкцией VERSIONS BETWEEN, позволяющей извлекать из БД историю изменения строк определенной давности. Следующим примером можно продолжить приводившийся только что код:
COLUMN versions_endtime FORMAT A22 COLUMN versions_starttime FORMAT A22 SELECT empno , sal , versions_starttime , versions_endtime , versions_xid , versions_operation FROM emp VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE ;
Прочие поля строк таблицы EMP в этом запросе не выданы из экономии места.
Упражнение: Измените несколько раз зарплаты разным сотрудникам и просмотрите историю изменений.
С версии 10 открылась возможность одной командой восстановить строки таблицы на момент в прошлом. Это позволяет вместо вышеуказанного INSERT … SELECT … AS OF записать
FLASHBACK TABLE emp TO TIMESTAMP ( SYSTIMESTAMP - INTERVAL '1' MINUTE ) ;
Тем не менее:
FLASHBACK было возможным, мы должны разрешить системе изменять физические адреса строк таблицы. Например, выдать: ALTER TABLE emp ENABLE ROW MOVEMENT;Для полного восстановления таблицы, ранее удалявшейся, команда FLASHBACK выглядит иначе. Вариант простого восстановления (из мусорной корзины):
FLASHBACK TABLE emp TO BEFORE DROP;
Вариант с переименованием (если с момента удаления таблица с именем EMP создавалась повторно):
FLASHBACK TABLE emp TO BEFORE DROP RENAME TO emp_old;
Если таблица переименовывалась несколько раз, вместо имени таблицы можно подставить внутреннее название для одной из старых версий таблицы, полученное из системной таблицы USER_RECYCLEBIN.
Технически такая возможность достигается сохранением старой структуры хранения ("сегмента") таблицы в DROP TABLE, что определяет границы применимости такого подхода.
Если же база данных работает в специальном режиме flashback, команда FLASHBACK позволяет восстановливать ее целиком:
FLASHBACK DATABASE TO TIMESTAMP SYSTIMESTAMP - INTERVAL '1' HOUR;
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.