|
Программирование >> Полное сканирование таблицы
тимого, не равного null значения Address ID, исходный запрос возвращает null для Shipment Date, несмотря на то, что соединение с Shipments само по себе допустимо. Скорее всего, такое поведение - это совсем не то, что требовалось разработчику, и в нем нет функциональной необходимости, поэтому новая форма будет, скорее всего, работать прекрасно, даже лучше, чем исходная, в этом редком случае. Однако любое изменение функциональности, сделанное для улучшения производительности, опасно. Следовательно, перед тем как вносить изменения, например, проводить описанное слияние запроса, определяющего представление, с главным оператором SQL, удостоверьтесь, что новое поведение запроса будет правильным для редких случаев, и предупредите разработчиков, что изменение может привести к тому, что тестирование вернет другие результаты. Маловероятно, что вам действительно понадобится именно исходное поведение запроса. Но если вы просто хотите проявить осторожность, не выясняя, было ли исходное поведение правильным для редкого случая, то можете прекрасно эмулировать функциональность исходного запроса таким кодом: SELECT OV.Customer Main Phone. C.Honorific, OV.Customer First Name. OV.Custonier Last Name. C.Suffix. DV.Custonier Adclress ID. A.Address ID Shipment Address ID. A.Street Addr Li nel Shi pment Street Address Li nel. A.Street Addr Li ne2 Shi pment Street Address Li ne2. A.CityName Shipment City Name. A.State Abbreviation Shipment State, A.ZIP Code Shipment ZIP. DD.Deferred Ship Date. OD.Item Count. DDT.Text. P.Prod Description. DECODE(A.Address ID. NULL. TO DATE(NULL). S.Shipnient Date) Shipment Date FROM Recent Drder V OV. Drder Details DD. Products P. Shipments S. Addresses A. Codejranslations ODT, Customers С WHERE UPPER(DV.Customer Last Name) LIKE :last name S; AND UPPER(OV.Customer First Name) LIKE :first nameг AND DD.Order ID = DV.Order ID AND OV.CustomerJD = C.CustomerJD AND OD.Product ID = P.ProductJD(+) AND OD.Shipment ID = S.Shipment ID(+) AND S.AddressJD = A.Address ID(+) AND OD.StatusJode = ODT.Code AND ODT. Code Jype = ORDER DETAIL STATUS ORDER BY OV.CustomerJD. OV.Order ID Desc. DECODE(A.Address ID. NULL. TO NUMBER(NULL). S.ShipmentJD). OD.Order DetailJD В этом запросе есть два изменения, которые заставляют запрос возвращать результаты, как если бы соединение с Shipments выдавало внешний случай независимо от того, возникает ли внешний случай в соединении с Addresses. Без представления запрос будет обрабатывать соединение с Shi pments независимо от соединения с Addresses. Однако выражение DECODE в конце списка SELECT и в середине списка ORDER BY заставляет внутренний случай первого соединения эмулировать внешний случай соединения (создавая null вместо Shipment Date и Shipment ID), независимо от того, будет ли при соединении с Addresses найден внешний случай. Иногда у вас будут действительно хорошие причины использовать представление вместо простых таблиц. Наиболее распространенная причина - необходимость обойти ограничения в автоматически сгенерированном SQL-коде. Функционально вам может понадобиться какой-то сложный синтаксис SQL, который генератор SQL обработать не может. Распространенный обход - скрыть эту сложность в оп- ределяющем представление запросе, который вы создадите вручную, и заставить генератор SQL считать представление простой таблицей, скрывая от него все сложности. В таких случаях у вас может не получиться избежать представлений, как, например, я предполагал в предыдущих решениях. Тогда альтернативный подход - расширить использование представления, скрывая больше SQL в определении представления. Например, так как предыдущая задача включала внешнее соединение с представлением, то вы могли бы решить ее, заключив внешнее соединение в запрос, определяющий представление. Тогда вместо Shipment V вы бы использовали OrderDetan V и следующий определяющий представление запрос: CREATE VIEW Order Detail V AS SELECT A.AddressJD Shipment Address ID. A.Street Addr Linel Shipment Street Address Linel. A. Street Addr Li ne2 Shi pinent Street Address Li ne2. A.City Name Shipment City Name, A.State Abbrev1ation Shipment State. A.ZIP Code Shipment ZIP. S.Shipment Date. S.Shipment ID. OD.Deferred Ship Date, OD.Item Count. DD.Order ID. OD.Order Detail ID. OD.Product ID. OD.Status Code FROM Shipments S. Addresses A. Order Detai1s OD WHERE OD.ShipiTient ID = S.Shipment ID(+) AND S.AddressJD = A.Address ID(+) Тогда запрос, использующий расширенное представление, становится таким: SELECT OV.Customer Main Phone. С.Honorific. DV.Customer First Name. DV.Customer Last Name. С.Suffix. OV.CustomerAddressJD. ODV.Shipment Address ID. ODV.Shipment Street Address Linel. ODV. Shi pment Street Address Li ne2. ODV. Shi pment Ci tyjame. ODV.Shipment State. ODV.Shiprient Zip. ODV.Deferred Ship Date. ODV.Item Count. ODT.Text. P.Prod Description. ODV.Shipment Date FROM Recent Order V OV. Order Oetail V ODV. Products P. Codejranslations ODT. Customers С WHERE UPPERCOV.Customer Last Name) LIKE :last name AND UPPER(DV.CustomerJirst Name) LIKE :first name S; AND ODV.OrderJD = OV.OrderJD AND DV.Customer ID = C.CustomerJD AND ODV. Product JD = P.Product ID(+) AND ODV. Status Jode = ODT. Code AND ODT.Codejype = ORDERJETAILJTATUS ORDER BY OV. Customer JD. OV.OrderJD Desc. ODV.ShipmentJD. ODV.OrderJetailJD Излишние считывания в запросах, использующих представления Теперь рассмотрим случай соединений с узлами, помеченными С* и С на рис. 7.35. Эти узлы представляют одну и ту же таблицу с одинаковыми операторами соединения, поэтому любой план исполнения, включающий оба узла, избыточен, он считывает одни и те же строки таблицы и, вероятно, записи индекса, дважды. Второе, ненужное считывание в любом случае не должно требовать физического ввода-вывода, так как первое считывание, выполненное менее чем миллисекунду назад, должно поместить блок таблицы или индекса в голову совместно используемого кэша. Если план исполнения сильно фильтруется перед тем, как обратиться ко второму, избыточному узлу, то дополнительными операциями логического ввода-вывода можно пренебречь. Но для больщих запросов или запросов, в которых большинство строк фильтруются только после подобных ненужных считываний, стоимость излишних операций логического ввода-вывода существенна. Если разработчик первоначально создал запрос к простым таблицам, вероятность появления ошибки такого типа весьма мала. Чтобы включить ненужное соединение, ему пришлось бы сойти со своего пути создания запроса, и избыточность была бы очевидна при просмотре кода. С представлениями, однако, эти ошибки легко допустить, причем они будут хорошо спрятаны. Как избавиться от лишнего соединения с таблицей Customers? У вас есть три варианта. Добавить новые требуемые столбцы в список SELECT запроса, определяющего представление, и использовать их вместо ссылок на столбцы лишней таблицы в запросе, использующем представления. Это безопасно для других запросов, которым нужно то же представление, так как изменения включают только добавление столбцов, но не модификацию диаграммы определяющего представление запроса. Исключить лишнее соединение из запроса, определяющего представление, и использовать только столбцы из узла простой таблицы в запросе, использующем представления. Однако это опасно, если существуют другие использующие представления запросы, которым могут понадобиться исключенные столбцы представления. Устранить представление из запроса, использующего представления, заменив его эквивалентными не избыточными соединениями с простыми таблицами. Ненужные узлы и соединения Рассмотрим соединение с узлом ОТ в последнем запросе, использующем представления. Кажется, что исходный определяющий представление запрос В1слючает это соединение в целях поддержки запросов, относящихся к состоянию заказов. Но на самом деле этот запрос даже не упоминает состояние заказа, поэтому у вас может возникнуть вопрос, нужен ли этот узел. Если вы не заметили этот бесполезный узел, то могли бы распознать его, заметив в плане исполнения соединение с индексом по первичному ключу этой таблицы, но без считываний из самой таблицы. Такие только индексные считывания индексов по первичным ключам обычно указывают на ненужные соединения. Безопасное исключение таких ненужных соединений будет достаточно непростым делом, потому что иногда у них есть функциональные побочные эффекты. Так как это внутреннее соединение, то, по крайней мере, вероятно, что, даже без фильтра для этого узла, само соединение отбрасывает строки, которые запрос не должен возвращать. Это может быть сделано за счет отбрасывания строк, где Orders .Status Code IS NULL или где Status Code указывает не недопустимые коды состояния, для которых не существует соответствия в таблице Code Trans1ations. Последнее маловероятно или должно быть исправлено во время восстановления ссылочной целостности. Однако равные null внешние ключи встречаются часто, и если значение столбца может быть null, вам следует задуматься о добавлении явного условия Status Code IS NOT NULL перед тем, как убирать соединение, чтобы эмулировать неявную фильтрующую функцию внутреннего соединения. Более вероятно, что разработчик, применяющий представление, даже не подумал о неявной фильтрующей функции представления, и неявный фильтр - это нежелательная случайность. Таким образом, перед тем, как имитировать прежнее поведение
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |