2010-06-02 6 views
3

Каков наиболее эффективный способ найти все записи, которые накладываются друг на друга в одной таблице? Каждая запись имеет дату начала и окончания. Например, у меня есть следующие настройки базы данных:Поиск эффективных перекрывающихся записей в таблице SQL

CREATE TABLE DEMO 
(
    DEMO_ID int IDENTITY , 
    START date NOT NULL , 
    END date NOT NULL 
); 

INSERT INTO DEMO (DEMO_ID, START, END) VALUES (1, '20100201', '20100205'); 
INSERT INTO DEMO (DEMO_ID, START, END) VALUES (2, '20100202', '20100204'); 
INSERT INTO DEMO (DEMO_ID, START, END) VALUES (3, '20100204', '20100208'); 
INSERT INTO DEMO (DEMO_ID, START, END) VALUES (4, '20100206', '20100211'); 

Мой запрос выглядит следующим образом:

SELECT DISTINCT * 
FROM DEMO A, DEMO B 
WHERE A.DEMO_ID != B.DEMO_ID 
AND A.START < B.END 
AND B.START < A.END 

Проблема заключается в том, когда мой демонстрационный стол имеет, например 20'000 строк запроса занимает слишком много времени. Моя среда MS SQL Server 2008. Спасибо за любое более эффективным решение

+0

Добавить индексы в начале/конце колонны. – bobince

+1

Я думаю, вы также должны использовать A.DEMO_ID Thierry

ответ

0

Это проще и выполняется в течение примерно 2 секунд для более 20000 записей

select * from demo a 
where not exists(
select 1 from demo b 
where a.demo_id!=b.demo_id 
AND A.S < B.E 
AND B.S < A.E) 
+0

Почему не существует?Я пошел: выбрать * из демо где существует ( выбрать 1 из демо-б где a.demo_id <> b.demo_id И A.ş Laoneo

0

Можно переписать запрос немного:

SELECT A.DEMO_ID, B.DEMO_ID 
FROM DEMO A, DEMO B 
WHERE A.DEMO_ID != B.DEMO_ID 
AND A.START >= B.START 
AND A.START <= B.END 

Избавление от DISTINCT ключевого слова может сделать вещи дешевле, потому что Sql Server будет делать сортировка по возвращенному столбцу (который все они используются при использовании DISTINCT *) для устранения дубликатов.

Вы также должны рассмотреть возможность добавления индекса. С Sql Server 2008 я бы рекомендовал индекс START, END, содержащий DEMO_ID.

+0

Вот ресурс, предоставляющий информацию о настройке индексов в анализаторе запросов : http://msdn.microsoft.com/en-us/library/aa216973%28SQL.80%29.aspx –

+0

Тот факт, что DEMO_ID уже уникален, не будет препятствовать тому, чтобы он возвращался несколько раз, когда он был присоединен к другому table –

+0

True - я отредактировал запрос, чтобы сделать все более ясным: каждая комбинация из DEMO_ID, b.DEMO_ID может отображаться дважды (если A полностью внутри B). Хотя в этом случае идентификаторы будут отображаться в другом порядке, поэтому DISTINCT в любом случае не будет удалять дубликаты. –

0

Используйте функцию или хранимую процедуру:

Во-первых, порядка записи по Start и End

DECLARE @t table (
    Position int identity(1,1), 
    DEMO_ID int, 
    START date NOT NULL , 
    END date NOT NULL 
) 
INSERT INTO @t (DEMO_ID, START, END) 
    SELECT DEMO_ID, START, END 
    FROM DEMO 
    ORDER BY START, END 

Затем проверьте наложения с предыдущим и следующего записью:

SELECT t.DEMO_ID 
FROM @t t INNER JOIN @t u ON t.Position + 1 = u.Position 
WHERE u.Start <= t.End 
UNION 
SELECT t.DEMO_ID 
FROM @t t INNER JOIN @t u ON t.Position - 1 = u.Position 
WHERE t.Start <= u.End 

Вам необходимо меру, чтобы быть уверенным, что это быстрее. В любом случае мы не будем сравнивать поля даты всех записей со всеми другими записями, поэтому это может быть быстрее для больших наборов данных.

+0

Ваше решение действительно интересно, но проблема в том, что строка в u.position не перекрывается с позицией -1, а с позицией -2 .... эта строка не возвращается. Таким образом, результат может быть неправильным. – Laoneo

+0

Я вижу, это не сработает, конечно ... извините за шум. – marapet

0

Позднего ответ, но интересно, если это поможет:

create index IXNCL_Demo_DemoId on Demo(Demo_Id) 

select a.demo_id, b.demo_id as [CrossingDate] 
from demo a 
    cross join demo b 
    where a.[end] between b.start and b.[end] 
    and a.demo_id <> b.demo_id 
Смежные вопросы