使用“相关”子查询进行高效连接
Posted
技术标签:
【中文标题】使用“相关”子查询进行高效连接【英文标题】:Efficient join with a "correlated" subquery 【发布时间】:2009-01-25 22:13:38 【问题描述】:Oracle 中给定三个表 Dates(date aDate, doUse boolean), Days(rangeId int, day int, qty int) 和 Range(rangeId int, startDate date)
我想加入这些,以便 Range 与来自 aDate = startDate 的 Dates 加入其中,其中 doUse = 1 whith 每天以 Days 为单位。
给定一个范围,它可能会做这样的事情
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,这给我制定查询带来了一些麻烦。
请记住,Dates 表非常大,因此我想避免从表中的第一个日期开始计算天值,而每个 Range Days 不应超过 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
【问题讨论】:
您能否填写一些示例数据,您希望得到什么结果? 【参考方案1】:试试这个:
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
是假期,哪些不是。
【讨论】:
为什么?为什么一遍又一遍地生成一些东西。日期表非常有用...您可以为假期设置列,我们有数百个假期日历,您可以为周末、工作日、第一天、最后一天、促销活动设置列,您可以将它们全部位图,这样您就可以拥有临时 q 的 最初设计日期表的动机是因为当时不知道如何在 oracle 中生成序列。但现在我认为它使管理更容易,我们使用的实际数据模型比我上面的示例更复杂,不同的媒体和部门有不同的“假期”。【参考方案2】:好吧,也许我找到了办法。像这样的:
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 值...
【讨论】:
以上是关于使用“相关”子查询进行高效连接的主要内容,如果未能解决你的问题,请参考以下文章