Oracle使用CONNECT BY和LEVEL使用INTERVAL

时间:2014-03-28 12:34:53

标签: sql oracle

Alex Poole在Oracle formatting date intervals发布了另一个用户问题的精彩解决方案,但

我之前没有说清楚,虽然我对亚历克斯的哪些部分有疑问。原始查询do,deisred目标是一个结果集,显示个人在14天间隔内的名单条目。准确理解查询的所有部分的作用是获得良好结果的关键。如此多的重点放在查询的某些部分,而不是我想要到达的地方。 :)

在查询的这一部分中,48的除数与30分钟的间隔有什么关系?开玩笑说,它可能是宇宙的新秘密(Hitchhiker的银河系指南) - 它不再是42,而是48.:)

一位同事和我认为这可能是为期6天的工作周(48小时--6 8小时工作日)。这是Alex'查询,请注意对象tbl_stat位于帖子的顶部,而不是Alex'查询本身,它是海报原始问题的一部分:

with tmp_tab as (
    select start_time + (level - 1)/48 as period_start,
        start_time + level/48 - interval '1' second as period_end
    from (
        select to_date(:start_time, 'DD/MM/YYYY HH24:MI:SS') start_time,
            to_date(:end_time, 'DD/MM/YYYY HH24:MI:SS') end_time
        from dual
    )
    connect by start_time + (level - 1)/48 < end_time
)
select to_char(tt.period_start, 'DD/MM/YYYY HH24:MI') dt,
    count(ts.track_datetime)
from tmp_tab tt
left join tbl_stat ts
on ts.track_datetime between tt.period_start and tt.period_end
group by tt.period_start
order by tt.period_start;

了解整个查询,但是间隔是什么?&#34; / 48&#34;属于设置30分钟间隔等:

select start_time + (level - 1)/48 as period_start,
    start_time + level/48 - interval '1' second as period_end

谢谢,希望这不是一个问题,但我真的不知道它是什么。

2 个答案:

答案 0 :(得分:0)

我需要在加班名单的支付期内获得间隔的查询已完成,我将分享。

这是我的弗兰肯斯坦,现在已经有了生命。一个奇怪的事情,虽然......我从字面上拿了一个帖子,最初把我的WHERE谓词作为:

WHERE TRIM(UPPER(TO_CHAR(TO_DATE(TT.PERIOD_END, 'DD/MM/YYYY' ), 'DAY'))) = 'SATURDAY'
    AND MOD(TT.PAY_PERIOD,2) <> 0

这是一个奇怪的星期一PERIOD_END日期。我必须将=设置为'星期四'以获得星期六的日期。好吧,取出不需要的TO_DATE()就解决了这个问题,但我觉得奇怪的是它以它的方式摆弄了它,抵消了我想要的东西2天。

WITH TMP_TAB AS (
    SELECT 
        START_TIME + (LEVEL - 1) AS PERIOD_START
        ,START_TIME + LEVEL + INTERVAL '13' DAY AS PERIOD_END
        ,LEVEL AS PAY_PERIOD
    FROM (
        SELECT 
            TO_DATE
                (
            TO_CHAR(
                    (
                        SELECT 
                            CASE WHEN MOD(TO_NUMBER(TO_CHAR((SELECT MIN(ENTERED_DT) FROM OVTR_LOG) + DT1,'J')),2) = 0 
                                THEN TRUNC((SELECT MIN(ENTERED_DT) FROM OVTR_LOG) + DT1 + 7) 
                                ELSE TRUNC((SELECT MIN(ENTERED_DT) FROM OVTR_LOG) + DT1) END AS FIRST_PPE_DT
                        FROM (SELECT 7 - TO_NUMBER(TO_CHAR(MIN(ENTERED_DT),'D')) AS DT1 FROM OVTR_LOG)
                    ),'DD/MM/YYYY'
                  ), 'DD/MM/YYYY'
                ) START_TIME
            ,TO_DATE
                (
            TO_CHAR(
                    (
                        SELECT 
                            CASE WHEN MOD(TO_NUMBER(TO_CHAR((SELECT MAX(ENTERED_DT) FROM OVTR_LOG) + DT1,'J')),2) = 0 
                                THEN TRUNC((SELECT MAX(ENTERED_DT) FROM OVTR_LOG) + DT1 + 7) 
                                ELSE TRUNC((SELECT MAX(ENTERED_DT) FROM OVTR_LOG) + DT1) END AS MOST_RECENT_PPE_DT
                        FROM (SELECT 7 - TO_NUMBER(TO_CHAR(MAX(ENTERED_DT),'D')) AS DT1 FROM OVTR_LOG)
                    ),'DD/MM/YYYY'
                  ), 'DD/MM/YYYY'
                ) END_TIME
        FROM DUAL
    )
    CONNECT BY START_TIME + (LEVEL -1) < END_TIME
)
SELECT 
    TO_CHAR(TT.PERIOD_START, 'DD/MM/YYYY') PERIOD_START
    ,TO_CHAR(TT.PERIOD_END, 'DD/MM/YYYY') PERIOD_END 

FROM TMP_TAB TT
--LEFT JOIN TO THE OTR
--ON WHATEVER

WHERE TRIM(UPPER(TO_CHAR(TT.PERIOD_END, 'DAY'))) = 'SATURDAY'
    AND MOD(TT.PAY_PERIOD,2) <> 0

GROUP BY 
    TT.PERIOD_START
    ,TT.PERIOD_END

ORDER BY TT.PERIOD_START

这给出了以下结果集。我很抱歉没有“完整的形式” - 正如亚历克斯所建议的那样,应该对帮助中心进行审核。

最初的问题包括我曾经/ 48做过什么?它根据LEVEL使用的上下文来划分LEVEL,直接和简单。

这是我最初想要的结果集,并且在Alex的查询的帮助下(感谢Peter的原始问题)我到了那里。这应该解释我试图到达的地方。

PERIOD_START    PERIOD_END
23/04/2011  07/05/2011
07/05/2011  21/05/2011
21/05/2011  04/06/2011
04/06/2011  18/06/2011
18/06/2011  02/07/2011
02/07/2011  16/07/2011
16/07/2011  30/07/2011
30/07/2011  13/08/2011
13/08/2011  27/08/2011
27/08/2011  10/09/2011
10/09/2011  24/09/2011
24/09/2011  08/10/2011
08/10/2011  22/10/2011
22/10/2011  05/11/2011
05/11/2011  19/11/2011
19/11/2011  03/12/2011
03/12/2011  17/12/2011
17/12/2011  31/12/2011
31/12/2011  14/01/2012
14/01/2012  28/01/2012
28/01/2012  11/02/2012
11/02/2012  25/02/2012
25/02/2012  10/03/2012
10/03/2012  24/03/2012
24/03/2012  07/04/2012
07/04/2012  21/04/2012
21/04/2012  05/05/2012
05/05/2012  19/05/2012
19/05/2012  02/06/2012
02/06/2012  16/06/2012

答案 1 :(得分:0)

datetime/interval arithmetic上的文档说明:

  

您可以在日期和时间戳值的算术运算中使用NUMBER常量,但不能使用区间值。 Oracle在内部将时间戳值转换为日期值,并将算术日期时间和间隔表达式中的NUMBER常量解释为天数。例如,SYSDATE + 1是明天。 SYSDATE - 7是一周前的。 SYSDATE + (10/1440)从现在开始是十分钟。

(这可能有点误导; SYSDATE + 1明天同时 ......)

通常看到1/24用于表示计算中的一小时,1 /(24 * 60)表示一分钟,1 /(24 * 60 * 60)表示秒。有些人更喜欢这种格式 - 或1/86400 - 而有些人更喜欢interval '1' secondnumtodsinterval(1, 'SECOND'),但最后它们的意思相同。

正如Peter Lang所说,1/48代表半小时,相当于一天的一小部分。 1/24是一小时,所以1 /(2 * 24)是半小时;如果有帮助,它与(1/2)*(1/24)相同。

您可以比较两个日期以查看小数差异:

select 1/48, to_date('12:30', 'HH24:MI') - to_date('12:00', 'HH24:MI') as diff
from dual

      1/48       DIFF
---------- ----------
.020833333 .020833333