2016-03-10 2 views
0

Так у меня есть 2 таблицы, которые выглядят как этотэффективно объединять таблицы интервалами (сложные)

___A___  _____B____ 
id | a  id | s | e 
1 | 5  1 | 4 | 6 
2 | 4  2 | 2 | 7 
3 | 3  3 | 3 | 4 
       4 | 1 | 5 

таблицы А и имеет приблизительно 1500000 и 200000 строк соответственно. Я хочу, чтобы соединить таблицы наименьшим интервалом, который A.a находится в.

Это мой запрос, но это очень медленно

select A.a, 
     B.s, 
     B.e 
    from A 
    join B 
    on A.a > B.s 
    and A.a < B.e 
    and (B.e - B.s) = (
     select min(B.e - B.s) 
     from B 
     where A.a > B.s 
      and A.a < B.e 
    ) 

подзапрос используется, чтобы убедиться, что мы используем наименьший интервал. Есть ли способ ускорить этот запуск?

Благодаря

+1

1. Сколько интервалов будет один «a'typically поместиться внутри? 2. Какие индексирования у вас есть? 3. Вы решили не использовать тип диапазона, и если да, почему бы и нет? –

+0

'NOT EXISTS()' variant * может * быть более эффективным (но он все равно нуждается в правильных индексах, очевидно) – wildplasser

+0

Отправьте план выполнения вашего текущего запроса, пожалуйста. – Andreas

ответ

0

Я не эксперт PostgreSQL, но вы можете попробовать использовать КТР:

WITH A AS (
SELECT MIN(B.e - B.s) AS MinInterval 
FROM #A AS A 
    INNER JOIN #B AS B ON A.a > B.s AND A.a < B.e) , B AS 

(SELECT A.a 
    , B.s 
    , B.e 
FROM #A AS A 
    JOIN #B AS B ON A.a > B.s AND A.a < B.e 
        AND (B.e - B.s) = (SELECT MinInterval FROM A)) 
       SELECT * FROM B; 

РЕЗУЛЬТАТ:

enter image description here

0

версия NOT EXISTS() может иногда избежать агрегатного подзапроса :

SELECT a.a, 
     b.s, 
     b.e 
    FROM AAAA a 
    JOIN BBBB b 
    ON a.a > b.s 
    AND a.a < b.e 
    AND NOT EXISTS (SELECT * 
     FROM BBBB nx 
     WHERE a.a > nx.s 
     AND a.a < nx.e 
     AND (nx.e - nx.s) < (b.e - b.s) 
    ); 
0

Использование RANK() window function делает это относительно просто:

SELECT ranked.id, ranked.val, ranked.start, ranked.end 
FROM 
(
    SELECT 
     a.id, 
     a.val, 
     b.start, 
     b.end, 
     RANK() OVER (PARTITION BY a.id ORDER BY (b.end - b.start) ASC, b.id ASC) AS match_rank 
    FROM a 
    JOIN b 
     ON a.val BETWEEN b.start AND b.end 
) ranked 
WHERE ranked.match_rank = 1 

Вы найдете все матчи, то для каждого матча вы присвоить ему значение ранга основано на том, как небольшой диапазон b «s это. Чем меньше диапазон, тем лучше (используя b.id в качестве тай-брейка для предотвращения дублирования). Тогда мы сохраняем наилучшее соответствие для каждого a.id.

SQL Fiddle demo

0

Попробуйте группу по версии:

select A.a 
     , B.s 
     , B.e 
from A 
join B on A.a > B.s and  A.a < B.e 
group by A.a 
     , B.s 
     , B.e 
     , B.e - B.s 
having (B.e - B.s) = min(B.e - B.s) 
+0

Просто голова, это может привести к дублированию, если есть два диапазона одного размера, которые соответствуют значению 'a'. Пример: 'a.a = 5',' b.s = 3, b.e = 6' и 'b.s = 4, b.e = 7' –

+1

@ Mr.Llama: то же, что и в исходном запросе (и моем) – wildplasser

Смежные вопросы