使用“相关”子查询进行高效连接

时间:2009-01-25 22:13:38

标签: sql oracle

在Oracle中给出三个表Dates(date aDate,doUse boolean),Days(rangeId int,day int,qty int)和Range(rangeId int,startDate date)

我想加入这些,以便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 个答案:

答案 0 :(得分:3)

试试这个:

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 :(得分:1)

好的,也许我找到了办法。像这样:

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)

现在我只需要一种填写缺失日期的方法......

编辑:不,这样就意味着我会错过最后日期的doUse vaue ......