Эффективное соединение с «коррелированным» подзапросом - PullRequest
1 голос
/ 26 января 2009

С учетом трех таблиц Dates (date aDate, doUse boolean), Days (rangeId int, day int, qty int) и Range (rangeId int, startDate date) в Oracle

Я хочу присоединиться к ним, чтобы Range объединился с датами из aDate = startDate, где doUse = 1, каждый день в днях.

Для одного диапазона можно сделать что-то вроде этого

SELECT rangeId, aDate, CASE WHEN doUse = 1 THEN qty ELSE 0 END AS qty
FROM (
    SELECT aDate, doUse, SUM(doUse) OVER (ORDER BY aDate) day
    FROM Dates 
    WHERE aDate >= :startDAte
) INNER JOIN (
    SELECT rangeId, day,qty
    FROM Days
    WHERE rangeId = :rangeId
) USING (day)
ORDER BY day ASC

Я хочу сделать запрос для всех диапазонов в Range, а не только для одного.

Проблема в том, что значение соединения "day" зависит от диапазона startDate, который должен быть рассчитан, что создает некоторые проблемы при формулировании запроса.

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

Редактировать: пример данных

Dates                            Days
aDate        doUse               rangeId     day     qty
2008-01-01   1                   1           1       1
2008-01-02   1                   1           2       10
2008-01-03   0                   1           3       8
2008-01-04   1                   2           1       2
2008-01-05   1                   2           2       5

Ranges
rangeId      startDate
1            2008-01-02
2            2008-01-03


Result
rangeId      aDate        qty
1            2008-01-02   1
1            2008-01-03   0
1            2008-01-04   10
1            2008-01-05   8
2            2008-01-03   0
2            2008-01-04   2
2            2008-01-05   5

Ответы [ 2 ]

3 голосов
/ 26 января 2009

Попробуйте это:

SELECT  rt.rangeId, aDate, CASE WHEN doUse = 1 THEN qty ELSE 0 END AS qty
FROM    (
    SELECT  *
    FROM    (
        SELECT  r.*, t.*, SUM(doUse) OVER (PARTITION BY rangeId ORDER BY aDate) AS span
        FROM    (
            SELECT  r.rangeId, startDate, MAX(day) AS dm
            FROM    Range r, Days d
            WHERE   d.rangeid = r.rangeid
            GROUP BY
                r.rangeId, startDate
            ) r, Dates t
        WHERE   t.adate >= startDate
        ORDER BY
            rangeId, t.adate
        )
    WHERE
        span <= dm
    ) rt, Days d
WHERE   d.rangeId = rt.rangeID
    AND d.day = GREATEST(rt.span, 1)

P. S. Мне кажется, что единственное, что нужно хранить в базе данных - Dates, - это получить непрерывный календарь с отмеченными праздниками.

Вы можете создать календарь произвольной длины в Oracle, используя следующую конструкцию:

SELECT :startDate + ROWNUM
FROM   dual
CONNECT BY
       1 = 1
WHERE  rownum < :length

и оставьте только праздники в Dates. Простое соединение покажет вам, какие Dates являются выходными, а какие нет.

1 голос
/ 26 января 2009

Хорошо, возможно, я нашел способ. Примерно так:

SELECT irangeId, aDate + sum(case when doUse = 1 then 0 else 1) over (partionBy rangeId order by aDate) as aDate, qty
FROM Days INNER JOIN (
    select irangeId, startDate + day - 1 as aDate, qty
    from Range inner join Days using (irangeid)
) USING (aDate)

Теперь мне просто нужен способ заполнить пропущенные даты ...

Редактировать: Нет, этот способ означает, что я буду скучать по поводу последних дат ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...