<? include 'head.php' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head>
<link rel="stylesheet" href="css.css" type="text/css">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<meta http-equiv="Content-Language" content="ru">
<title>Глава 21.</title></head>
<body>
<h1>Глава 21. ИЗМЕНЕНИЕ ЗНАЧЕНИЙ С ПОМОЩЬЮ ПРЕДСТАВЛЕНИЙ</h1>
<hr width="50%">
<p>В этой главе рассказано о командах модификации языка DML - ВСТАВИТЬ (INSERT), 
ИЗМЕНИТЬ (UPDATE) и УДАЛИТЬ (DELETE) - и о том, когда они применяются для 
представлений. Как сказано в предыдущей главе, использование команд 
модификации в представлениях это косвенный способ использования их в ссылочных 
таблицах с помощью запросов представлений. Однако не все представления могут 
модифицироваться.</p>
<p>В этой главе мы будем обсуждать правила, определяющие, является ли 
представление модифицируемым. Кроме того, вы научитесь использовать предложение 
WITH CHECK OPTION, управляющее указанными значениями, которые можно вводить в 
таблицу с помощью представления.</p>
<p>Как сказано в <a href="ch18.html">Главе 18</a>, это, в некоторых случаях, 
может быть желательным вариантом непосредственного ограничения таблицы.</p>
<h3><a name="21.1">М</a>ОДИФИЦИРОВАНИЕ ПРЕДСТАВЛЕНИЯ</h3>
<p>Один из наиболее трудных и неоднозначных аспектов представлений - 
непосредственное их использование с командами модификации DML. Как упомянуто в 
предыдущей главе, эти команды фактически воздействуют на значения в базовой 
таблице представления. Это является некоторым противоречием.</p>
<p>Представление 
состоит из результатов запроса, и, когда вы модифицируете представление, вы 
модифицируете набор результатов запроса. Но модификация не должна воздействовать 
на запрос; она должна воздействовать на значения в таблице, к которой был сделан 
запрос, и таким образом изменять вывод запроса. Это не простой вопрос. Следующий 
оператор будет создавать представление, показанное на Рисунке 21.1:</p>
<pre>      CREATE VIEW Citymatch (custcity, salescity)
          AS SELECT DISTINCT a.city, b.city
             FROM Customers a, Salespeople b
             WHERE a.snum = b.snum;</pre>
<p>Это представление показывает все совпадения заказчиков с их продавцами так, 
что имеется по крайней мере один заказчик в городе_заказчика, обслуживаемый 
продавцом в городе_продавца.</p>
<p>Например, одна строка этой таблицы - London London - показывает, что имеется 
по крайней мере один заказчик в Лондоне, обслуживаемый продавцом в Лондоне. Эта 
строка может быть произведена при совпадении Hoffmanа с его продавцом Peel, 
причем если оба они из Лондона.</p>
<pre>            ===============  SQL Execution Log ==============
           |                                                 |
           | SELECT *                                        |
           | FROM  Citymatch;                                |
           | =============================================== |
           |   custcity    salescity                         |
           |  ---------    ---------                         |
           |  Berlin       San Jose                          |
           |  London       London                            |
           |  Rome         London                            |
           |  Rome         New York                          |
           |  San Jose     Barselona                         |
           |  San Jose     San Jose                          |
           |                                                 |
             ================================================

	     Рисунок 21.1 Представление совпадения по городам</pre>
<p>Однако то же самое значение будет произведено при совпадении Clemens из 
Лондона с его продавцом, который также оказался с именем Peel. Пока отличающиеся 
комбинации городов выбирались конкретно, только одна строка из этих значений была произведена.</p>
<p>Даже если вы не получите выбора, используя отличия, вы всё ещё будете в том 
же самом положении, потому что вы будете тогда иметь две строки в представлении 
с идентичными значениями, то есть с обоими столбцами, равными &quot; Lоndon London &quot;. 
Эти две строки представления будут отличаться друг от друга, так что вы пока не 
сможете сообщить, какая строка представления исходила из каких значений базовых 
таблиц (имейте в виду, что запросы, не использующие предложение ORDER BY, 
производят вывод в произвольном порядке).<br>
Это относится также и к запросам, 
используемым внутри представлений, которые не могут использовать ORDER BY. Таким 
образом, заказ из двух строк не может быть использован для их отличий. Это 
означает, что мы будем снова обращаться к выводу строк, которые не могут быть 
точно связаны с указанными строками запрашиваемой таблицы.<br>
Что, если вы пробуете 
удалить строку &quot; London London &quot; из представления? Означало бы это удаление 
Hoffman из таблицы Заказчиков, удаление Clemens из той же таблицы, или удаление 
их обоих? Должен ли SQL также удалить Peel из таблицы Продавцов? На эти вопросы 
невозможно ответить точно, поэтому удаления не разрешены в представлениях такого 
типа. Представление Citymatch это пример представления только_для_чтения: оно 
может быть только запрошено, но не изменено.</p>
<h3><a name="21.2">О</a>ПРЕДЕЛЕНИЕ МОДИФИЦИРУЕМОСТИ ПРЕДСТАВЛЕНИЯ</h3>
<p>Если команды модификации могут выполняться в представлении, представление, 
как уже говорилось, будет модифицируемым; в противном случае оно предназначено 
только для чтения при запросе. Не противореча этой терминологии, мы будем 
использовать выражение &quot;модифицировать представление&quot; (updating a view), что 
означает возможность выполнения в представление любой из трёх команд модификации DML (Вставить, Изменить и Удалить), которые могут изменять значения.</p>
<p>Как вы 
определите, является ли представление модифицируемым? В теории базы данных, это 
- пока обсуждаемая тема. Основной  принцип таков: модифицируемое представление 
это представление, в котором команда модификации может выполниться, чтобы 
изменить одну, и только одну, строку основной таблицы в каждый момент времени, 
не воздействуя на любые другие строки любой таблицы. Использование этого 
принципа на практике, однако, затруднено.</p>
<p>Кроме того, некоторые представления, 
которые теоретически являются модифицируемыми, на самом деле не являются 
модифицируемыми в SQL.</p>
<p>Вот критерии, по которым определяют, является ли в SQL-представление 
модифицируемым, или нет:</p>
<ul>
<li>Оно должно выводиться из одной, и только из одной, базовой таблицы.</li>
<li>Оно должно содержать первичный ключ этой таблицы (это технически не 
предписывается стандартом ANSI, но было бы неплохо придерживаться этого).</li>
<li>Оно не должно иметь никаких полей, которые являлись бы агрегатными функциями.</li>
<li>Оно не должно содержать DISTINCT в своем определении.</li>
<li>Оно не должно использовать GROUP BY или HAVING в своем определении.</li>
<li>Оно не должно использовать подзапросы (это ANSI-ограничение, которое не 
предписано для некоторых реализаций).</li>
<li>Оно может быть использовано в другом представлении, но это представление 
должно также быть модифицируемым.</li>
<li>Оно не должно использовать константы, строки или выражения для значений 
(например: comm * 100) среди выбранных полей вывода.</li>
<li>Для INSERT оно должно содержать любые поля основной таблицы, которые имеют 
ограничение NOT NULL, если другое ограничение по умолчанию не определено.</li></ul>
<h3><a name="21.3">М</a>ОДИФИЦИРУЕМЫЕ ПРЕДСТАВЛЕНИЯ<br>
И ПРЕДСТАВЛЕНИЯ ТОЛЬКО_ДЛЯ_ЧТЕНИЯ.</h3>
<p>Одно из этих ограничений - то, что модифицируемые представления фактически 
подобны окнам в базовых таблицах. Они показывают кое-что, но не обязательно всё, 
из содержимого таблицы. Они могут ограничивать определенные строки 
(использованием предикатов) или специально именованные столбцы (с исключениями), 
но они представляют значения непосредственно и не выводят  информацию с 
использованием составных функций и выражений.</p>
<p>Они также не сравнивают строки таблиц друг с другом (как в объединениях и 
подзапросах или как с DISTINCT).</p>
<p>Различия между модифицируемыми представлениями и представлениями 
только_чтение не случайны.</p>
<p>Цели для которых вы их используете, часто различны. Модифицируемые 
представления в основном используются точно так же, как и базовые таблицы. 
Фактически пользователи не могут даже осознать, является ли объект, который они 
запрашивают, базовой таблицей или представлением. Это превосходный механизм 
защиты для скрытия частей таблицы, которые являются конфиденциальными или не 
относятся к потребностям данного пользователя. (В <a href="ch22.html">Главе 22</a> 
мы покажем вам, как разрешить пользователям обращаться к представлению, а не к 
базовой таблице).</p>
<p>Представления только_чтение, с другой стороны, позволяют вам получать и 
переформатировать данные более рационально. Они дают вам библиотеку сложных 
запросов, которые вы можете выполнить и повторить снова, сохраняя полученную 
вами информацию до последней минуты.<br>
Кроме того, результаты этих запросов в 
таблицах, которые могут затем использоваться в запросах самостоятельно 
(например, в объединениях), имеют преимущество над просто выполнением запросов.</p>
<p>Представления только_чтение могут также иметь прикладные программы защиты. 
Например, вы можете захотеть, чтобы некоторые пользователи видели агрегатные 
данные, такие как усредненное значение комиссионных продавца, а не представление 
индивидуальных значений комиссионных.</p>
<h3><a name="21.4">КАКОЕ</a> ПРЕДСТАВЛЕНИЕ ЯВЛЯЕТСЯ МОДИФИЦИРУЕМЫМИ?</h3>
<p>Вот некоторые примеры модифицируемых представлений и представлений только_чтение.</p>
<pre>           CREATE VIEW Dateorders (odate, ocount)
              AS SELECT odate, COUNT (*)
                 FROM Orders
                 GROUP BY odate;</pre>
<p>Это представление только_чтение из-за присутствия в нём агрегатной функции и GROUP BY.<br>
&nbsp;</p>
<pre>              CREATE VIEW Londoncust
                 AS SELECT *
                    FROM Customers
                    WHERE city = 'London';</pre>
<p>А это - модифицируемое представление.<br>
&nbsp;</p>
<pre>             CREATE VIEW SJsales (name, number, percentage)
                 AS SELECT sname, snum, comm * 100
                    FROM Salespeople
                    WHERE city = 'SanJose';</pre>
<p>Это - представление только_чтение из-за выражения &quot;comm * 100&quot;. При этом, 
однако, возможны переупорядочивание и переименование полей. Некоторые программы 
будут позволять удаление в этом представлении или в заказах столбцов snum и 
sname.<br>
&nbsp;</p>
<pre>             CREATE VIEW Salesonthird
                AS SELECT *
                  FROM Salespeople
                  WHERE snum IN
                    (SELECT snum
                       FROM Orders
                       WHERE odate = 10/03/1990);</pre>
<p>Это - представление только_чтение в ANSI из-за присутствия в нём подзапроса. 
В некоторых программах, это может быть приемлемо.<br>
&nbsp;</p>
<pre>            CREATE VIEW Someorders
               AS SELECT snum, onum, cnum
                  FROM Orders
                  WHERE odate IN (10/03/1990,10/05/1990);</pre>
<p>Это - модифицируемое представление.</p>
<h3><a name="21.5">П</a>РОВЕРКА ЗНАЧЕНИЙ, ПОМЕЩАЕМЫХ В ПРЕДСТАВЛЕНИЕ</h3>
<p>Другой вывод о модифицируемости представления -  вы можете вводить 
значения, которые &quot;проглатываются&quot; (swallowed) в базовой таблице. Рассмотрим 
такое представление:</p>
<pre>          CREATE VIEW Highratings
             AS SELECT cnum, rating
                FROM Customers
                WHERE rating = 300;</pre>
<p>Это представление - модифицируемое. Оно просто ограничивает ваш доступ к 
определенным строкам и столбцам в таблице. Предположим, что вы вставляете 
(INSERT) следующую строку:</p>
<pre>       INSERT INTO Highratings
          VALUES (2018, 200);</pre>
<p>Команда INSERT допустима в данном представлении. Строка будет 
вставлена с помощью представления Highratings в таблицу Заказчиков. Однако, 
когда она появится там, она исчезнет из представления, поскольку значение оценки 
не равно 300. Это - обычная проблема.</p>
<p>Значение 200 может быть просто напечатано, но теперь строка находится уже в 
таблице Заказчиков, где вы не можете даже увидеть её. Пользователь не сможет 
понять, почему введя строку он не может её увидеть и будет неспособен при этом 
удалить её. Вы можете быть гарантированы от модификаций такого типа с помощью 
включения WITH CHECK OPTION (С ОПЦИЕЙ ПРОВЕРКИ) в определение представления. Мы 
можем использовать WITH CHECK OPTION в определении представления Highratmgs.</p>
<pre>          CREATE VIEW Highratings
             AS SELECT cnum, rating
                FROM Customers
                WHERE rating = 300
                WITH CHECK OPTION;</pre>
<p>Вышеупомянутая вставка будет отклонена.</p>
<p>WITH CHECK OPTION производит действие все_или_ничего (all-or-nothing). Вы 
помещаете его в определение представления, а не в команду DML, так что или все 
команды модификации в представлении будут проверяться, или ни одна не будет 
проверена. Обычно вы хотите использовать опцию проверки, применяя её в 
определении представления, что может быть удобно. В общем, вы должны 
использовать эту опцию, если у вас нет причины разрешать представлению помещать 
в таблицу значения, которые оно само не может содержать.</p>
<h3><a name="21.6">П</a>РЕДИКАТЫ И ИСКЛЮЧЁННЫЕ ПОЛЯ</h3>
<p>Похожая проблема, о которой вы должны знать, включает в себя вставку строк в 
представление с предикатом, базирующемся на одном или более исключённых полях. 
Например, может показаться разумным создание Londonstaff, как здесь:</p>
<pre>            CREATE VIEW Londonsta1t
               AS SELECT snum, sname, comm
                  FROM Salespeople
                  WHERE city = 'London';</pre>
<p>В конце концов, зачем включать значение city, если все значения city будут 
одинаковыми?</p>
<p>А как будет выглядеть картинка, получаемая всякий раз, когда мы пробуем 
вставить строку?</p>
<p>Так как мы не можем указать значение city как значение по 
умолчанию, этим значением, вероятно, будет NULL, и оно будет введено в поле city 
(NULL используется, если другое значение по умолчанию не было определено. См. 
подробности в <a href="ch18.html">Главе 18</a>). Так как в этом случае поле city 
не будет равняться значению London, вставляемая строка будет исключена из 
представления.</p>
<p>Это будет верным для любой строки, которую вы попробуете вставить в просмотр 
Londonstaff. Все они должны быть введены с помощью представления Londonstaff в 
таблицу Продавцов, а затем исключены из самого представления (если определением 
по умолчанию был не London, то это особый случай). Пользователь не сможет 
вводить строки в это представление, хотя всё ещё не известно, может ли он 
вводить строки в базовую таблицу. Даже если мы добавим WITH CHECK OPTION в 
определение представления,</p>
<pre>       CREATE VIEW Londonstate
          AS SELECT snum, sname, comm
             FROM Salespeople
             WHERE city = 'London'
             WITH CHECK OPTION;</pre>
<p>проблема не обязательно будет решена. В результате этого мы получим 
представление, которое мы могли бы модифицировать или из которого мы могли бы 
удалять, но не вставлять в него. В некоторых случаях это может быть хорошо; 
хотя, возможно, нет смысла пользователям, имеющим доступ к этому представлению, 
иметь возможность добавлять строки. Но вы должны точно определить, что может 
произойти, прежде чем вы создадите такое представление.</p>
<p>Даже если это не всегда может обеспечить вас нужной информацией, полезно 
включать в ваше представление все поля, на которые имеется ссылка в предикате. 
Если вы не хотите видеть эти поля в вашем выводе, вы всегда сможете исключить их 
из запроса в представлении, в противоположность запросу внутри представления. 
Другими словами, вы могли бы определить представление Londonstaff так:</p>
<pre>           CREATE VIEW Londonstaff
              AS SELECT *
                 FROM Salespeople
                 WHERE city = 'London'
                 WITH CHECK OPTION;</pre>
<p>Эта команда заполнит представление одинаковыми значениями в поле city, 
которые вы можете просто исключить из вывода с помощью запроса, где указаны 
только те поля, которые вы хотите видеть:</p>
<pre>           SELECT snum, sname, comm
              FROM Londonstaff;</pre>
<h3><a name="21.7">П</a>РОВЕРКА ПРЕДСТАВЛЕНИЙ, КОТОРЫЕ БАЗИРУЮТСЯ<br>
НА ДРУГИХ ПРЕДСТАВЛЕНИЯХ</h3>
<p>Еще одно надо упомянуть относительно предложения WITH CHECK OPTION в ANSI: 
оно не делает каскадированного изменения: оно применяется только в 
представлениях, в которых оно определено, но не в представлениях, основанных на 
этом представлении. Например, в предыдущем примере</p>
<pre>          CREATE VIEW Highratings
             AS SELECT cnum, rating
                FROM Customers
                WHERE rating = 300
                WITH CHECK OPTION;</pre>
<p>попытка вставить или модифицировать значение оценки, не равное 300, потерпит 
неудачу. Однако мы можем создать второе представление (с идентичным 
содержанием), основанное на первом:</p>
<pre>           CREATE VIEW Myratings
             AS SELECT *
             FROM Highratings;</pre>
<p>Теперь мы можем модифицировать оценки, не равные 300:</p>
<pre>           UPDATE Myratings
              SET rating = 200
              WHERE cnum = 2004;</pre>
<p>Эта команда, выполняемая так, как если бы она выполнялась как первое 
представление, будет допустима. Предложение WITH CHECK OPTION просто 
гарантирует, что любая модификация в представлении произведет значения, которые 
удовлетворяют предикату этого представления.</p>
<p>Модификация других представлений, 
базирующихся на первом текущем, является всё ещё допустимой, если эти 
представления не защищены предложениями WITH CHECK OPTION внутри этих 
представлений. Даже если такие предложения установлены, они проверяют только те 
предикаты представлений, в которых они содержатся. Так, например, даже если 
представление Myratings создавалось следующим образом,</p>
<pre>             CREATE VIEW Myratings
                AS SELECT *
                   FROM Highratings
                   WITH CHECK OPTION;</pre>
<p>проблема не будет решена. Предложение WITH CHECK OPTION будет исследовать 
только предикат представления Myratings. Пока у Myratings фактически не имеется 
никакого предиката, WITH CHECK OPTION ничего не будет делать. Если используется 
предикат, то он будет проверяться всякий раз, когда представление Myratings 
будет модифицироваться, но предикат Highratings все равно будет проигнорирован. 
Это - дефект в стандарте ANSI, который у большинства программ исправлен. Вы 
можете попробовать использовать представление наподобие последнего примера и 
посмотреть, избавлена ли ваша система от этого дефекта. (Попытка выяснить это 
самостоятельно может иногда оказаться проще и яснее, чем поиск ответа в 
документации системы.)</p>
<h3><a name="21.8">Р</a>ЕЗЮМЕ</h3>
<p>Вы теперь полностью овладели знаниями о представлениях. Кроме правил, 
определяющих, является ли данное представление модифицируемыми в SQL, вы 
познакомились с основными понятиями, на которых эти правила базируются - т.е., 
что модификации в представлениях допустимы только тогда, когда SQL может 
недвусмысленно определить, какие значения базовой таблицы можно изменять. Это 
означает, что команда модификации при выполнении не должна требовать ни 
изменений для многих строк сразу, ни сравнений между многочисленными строками 
либо базовой таблицы, либо вывода запроса.</p>
<p>Поскольку объединения включают в себя сравнение строк, они также запрещены.</p>
<p>Вы 
также поняли различие между некоторыми способами, которые используют 
модифицируемые представления и представления только_чтение.</p>
<p>Вы научились 
воспринимать модифицируемые представления как окна, отображающие данные 
одиночной таблицы, но не обязательно исключающие или реорганизующие столбцы 
посредством выбора только определенных строк отвечающих условию предиката.</p>
<p>Представления только_чтение, с другой стороны, могут содержать более допустимые 
запросы SQL; они могут, следовательно, стать способом хранения запросов, которые 
вам нужно часто выполнять в неизменной форме. Кроме того, наличие запроса, чей 
вывод обрабатывается как объект данных, дает вам возможность иметь ясность и 
удобство при создании запросов в выводе запросов.</p>
<p>Вы  можете теперь в представлении предотвращать создание командами модификации строк в базовой 
таблице, которые не представлены в самом представлении, с 
помощью предложения WITH CHECK OPTION в определения представления.</p>
<p>Вы можете 
также использовать WITH CHECK OPTION как один из способов ограничения в базовой 
таблице. В автономных запросах, вы обычно используете один или более столбцов в 
предикате, не представленные среди выбранных для вывода, что не вызывает никаких 
проблем. Но если эти запросы используются в модифицируемых представлениях, 
появляются проблемы, так как эти запросы производят представления, которые не 
могут иметь вставляемых в них строк.</p>
<p>Вы видели некоторые подходы к этим 
проблемам. В Главах <a href="ch20.html">20</a> и 21, мы говорили, что представления имеют прикладные 
программы защиты. Вы можете дать пользователям возможность обращаться к 
представлениям, не разрешая в то же время обращаться к таблицам, в которых эти 
представления непосредственно находятся. Глава 22 будет исследовать вопросы 
доступа к объектам данных в SQL.</p>
<h3><a name="21.9">Р</a>АБОТА СО SQL</h3>
<pre>1. Какое из этих представлений - модифицируемое?

       #1 CREATE VIEW Dailyorders
             AS SELECT DISTINCT cnum, snum, onum,
             odate
               FROM Orders;

       #2 CREATE VIEW Custotals
             AS SELECT cname, SUM (amt)
                FROM Orders, Customers
                WHERE Orders.cnum = customer.cnum
                GROUP BY cname;

       #3 CREATE VIEW Thirdorders
             AS SELECT *
                FROM Dailyorders
                WHERE odate = 10/03/1990;

       #4 CREATE VIEW Nullcities
             AS SELECT snum, sname, city
                FROM Salespeople
                WHERE city IS NULL
                   OR sname BETWEEN 'A' AND 'MZ';

2. Создайте представление таблицы Продавцов с именем Commissions
   (Комиссионные). Это представление должно включать только поля
   comm и snum. С помощью этого представления можно будет вводить
   или изменять комиссионные, но только для значений между .10 и .20.

3. Некоторые SQL-реализации имеют встроенную константу, представляющую текущую дату,
   иногда называемую &quot; CURDATE &quot;. 
   Слово CURDATE может, следовательно, использоваться в операторе SQL
   и заменяться текущей датой, когда его значение станет доступным, с помощью таких
   команд как SELECT или INSERT. Мы будем использовать
   представление таблицы Заказов с именем Entryorders для вставки строк
   в таблицу Заказов. Создайте таблицу заказов так, чтобы CURDATE
   автоматически вставлялась в поле odate, если не указано другое значение.
   Затем создайте представление Entryorders так, чтобы значения не могли быть указаны.

(См. ответы в <a href="a.html#21">Приложении A</a>.)</pre></body></html>
<? include 'foot.php' ?>