|
Программирование >> Проектирование баз данных
поиск самой последней цены (которая в большинстве случаев является текушей): SELECT price FROM prices WHERE product code = :p code AND date to = TO DATE(0l-Dec-4712,DD-Mon-YYYY) ; Помните наш совет по поводу использования единого для всего проекта значения большой даты? Сейчас вы оцените его важность. Если в качестве большой даты применяется несколько значений, то данный запрос не всегда будет работать правильно. Чтобы избежать этой проблемы, необходимо создать (в Oracle версии 7.1 и выше) общую для проекта функцию, возвращающую большую дату, и использовать эту дату вместо литерального значения: CREATE OR REPLACE FUNCTION high date RETURN DATE AS BEGIN RETURN TO DATE(Ol-DEC-4 712,DD-MON-YYYY); END high date; SELECT price FROM prices WHERE product code = :p code AND date to = high date; Мы не должны забывать еще об одной проблеме. При вводе новой цены товара необходимо сделать следующее: закрыть старую цену (установить DATE TO = SYSDATE); создать новую цену (с помощью DATE FROM = SYSDATE+дельта, DATETO = больщая дата). Первый шаг предполагает обновление столбца DATE TO, который является частью первичного ключа. Обновление столбцов первичного ключа в Oracle допускается, но специалисты по реляционным базам данных, как правило, относятся к этому неодобрительно. Все изменения, внесенные в первичный ключ, нужно каскадно распространить до внешнего ключа всех подчиненных таблиц. В противном случае нарушается ссылочная целостность. (Позже мы объясним, почему эта проблема может оказаться не такой серьезной.) Итак, мы будем придерживаться нашей первой рекомендации: лучший вариант первичного ключа - это внешний ключ к родительской таблице плюс столбец DATETO. Комбинация внешнего ключа и столбца DATE FROM редко оказывается оптимальной, но тем не менее именно эту схему чаще всего используют на практике! Однако вне зависимости от того, какой столбец является вторым компонентом первичного ключа, могут возникнуть проблемы, если во временном ряду имеется много строк с одинаковым значением внешнего ключа. В таких случаях по возможности следует переходить на процедурный метод. Допускать ли перекрытия и разрывы по дате? Вопрос о том, следует ли допускать перекрытия и разрывы по дате, связан с непрерывностью временного ряда данных. Ответ зависит от бизнес-правил, ограничений и требований системы. Вновь обратившись к нашему примеру с ценами, зададим себе два вопроса: Может ли товар иметь более одной цены (единицы) в данный момент времени? Может ли товар сначала иметь цену, затем не иметь цены, а затем вновь иметь ее? Для нашей таблицы PRICES ответ на оба вопроса, вероятно, будет отрицательным. Конечно, могут существовать товары, снятые с продажи, у которых последнее значение DATE TO находится в прошлом, и (что менее вероятно) планируемые товары, у которых наименьшее значение DA-TE FROM находится в будущем, но ни один из этих случаев не представляет собой разрыв по дате. Предположим, что для таблицы PRICES нужно создать механизм, препятствующий появлению разрывов и перекрытий по дате, так как они могут вызвать довольно серьезные проблемы при оформлении счетов-фактур. Если имеется временной ряд, в котором не допускаются ни перекрытия, ни разрывы, то что нужно сделать, чтобы это обеспечить? Во-первых, необходимо создать механизм для их обнаружения. В скрипте на SQL*Pliis из примера 7.2 сначала создается простая таблица только с действительными по дате столбцами, а затем - триггеры, которые препятствуют появлению перекрытий и разрывов по дате. Пример 7.2. Скрипт па SQL*Plus, создающий триггеры, предотвращающие появление перекрытий и разрывов REM Тестовый сценарий, демонстрирующий проверку наличия REM перекрывающихся действительных дат и разрывов во временном ряде REM Предполагается, что степень детализации - день REM CREATE TABLE contig ( clate froni DATE NOT NOLL CONSTRAINT contig date frotn time CHECK (date frotn = TR0NC (date frotn) ) , date to DATE NOT NOLL CONSTRAINT contig date to time CHECK (date to = TR0NC(date to)) , CONSTRAINT contig range CHECK (date to >= date frotn) , CONSTRAINT contig pk PRIMARY KEY (date to) ) ; CREATE OR REPLACE TRIGGER contig biur BEFORE INSERT OR UPDATE ON contig FOR EACH ROW BEGIN IF :new.clate to IS NULL THEN :new.date to := TO DATE(31-DEC-4712,DD-MON-YYYY) ; END IF; END; / CREATE OR REPLACE TRIGGER contig aiud AFTER INSERT OR DELETE OR UPDATE ON contig DECLARE l dummy VARCHAR2(1) ; l prev date to DATE; CURSOR c contig overlap IS SELECT x FROM contig contigl ,contig contig2 WHERE contigl.date from <= contig2.date to AND contigl.date to >= contig2.date from AND contigl.date to <> contig2.date to; CURSOR c contig gaps IS SELECT TRUNC(contig.date from) date from ,TRUNC(contig.date to) date to FROM contig ORDER BY contig.date to; BEGIN IF INSERTING OR UPDATING THEN - Проверяем, нет ли перекрытия OPEN c contig overlap; FETCH c contig overlap INTO l duramy; IF c contig overlap%FOUND THEN CLOSE c contig overlap; RAISE APPLICATION ERROR(-20001,Overlap detected); END IF; CLOSE c contig overlap; END IF; IF DELETING OR INSERTING THEN - Проверяем, нет ли разрывов FOR c gaps IN c contig gaps LOOP IF c gaps.date from <> NVL(l prev date to + 1, c gaps.date from) THEN RAISE APPLICATION ERROR(-20002, Gap detected) ; END IF; l prev date to := c gaps.date to; END LOOP; END IF; END; REM Допустимые действия с таблицами.
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |