Учитывая эту таблицу:
CREATE TABLE DeptPeopleHistory (
DEPT_ID INTEGER,
PERSON_ID INTEGER,
START_DATE INTEGER,
END_DATE INTEGER,
UNIQUE(DEPT_ID, START_DATE, PERSON_ID), -- works as sorted index.
UNIQUE(PERSON_ID, START_DATE),
UNIQUE(PERSON_ID, END_DATE),
CONSTRAINT (START_DATE < END_DATE)
);
У меня две потребности. Во-первых, это собрать всех людей, которые работают в определенном отделе на определенную дату. В настоящее время я использую этот (семантически правильный) запрос:
SELECT PERSON_ID FROM DeptPeopleHistory
WHERE
DEPT_IT = :given_dept AND
START_DATE <= :given_date AND :given_date < END_DATE
Это быстро для небольших таблиц истории или запросов недавних данных, но медленно для больших таблиц истории и старых данных, потому что оптимизатор использует только первый индекс, и нет хорошего способа справиться с END_DATE. Я пытался добавить END_DATE к первому индексу, но производительность запросов такая же. Я предполагаю, что это потому, что подфильтр (DEPT_IT =: Given_dept AND START_DATE <=: данное_дата) при применении к отсортированному индексу (DEPT_ID, START_DATE, END_DATE, PERSON_ID) приводит к данным с несортированным END_DATE, поэтому (данный момент: <дата_ДАТЫ) требуется последовательное сканирование результата. </p>
Мое другое требование - ввести следующее ограничение: человек не может работать в двух отделах одновременно или дважды в одном и том же отделе. Это означает следующее:
-- This must work for previously empty data:
INSERT INTO DeptPeopleHistory(DEPT_ID, PERSON_ID, START_DATE, END_DATE)
VALUES (1, 1, 20100501, 20100520);
-- This should cause constraint violation because the person already
-- works at dept 1 on days from 20100517 to 20100519:
INSERT INTO DeptPeopleHistory(DEPT_ID, PERSON_ID, START_DATE, END_DATE)
VALUES (:any_dept, 1, 20100517, 20100523);
Другой способ указать это ограничение заключается в том, что для данного PERSON_ID значение START_DATE должно быть минимальным или равным END_DATE из другой записи.
Глядя на эти две потребности, нам на самом деле нужен эффективный способ работы с непересекающимися диапазонами. Знаете ли вы какую-либо функцию или конструкцию в универсальном SQL или какую-то конкретную базу данных, которая может удовлетворить эти потребности? Возможно, какая-то особенность «пространственной базы данных»?
Примеры приведены в MySQL, но мне нужны решения, которые работают на Oracle, SQL Server и FireBird. Решения не должны быть переносимыми по всем таким базам данных.