database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
PostgreSQL
Firebird
ㆍOracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
Oracle Q&A 39319 게시물 읽기
No. 39319
특정일자의 시간대 데이터에서 입력되지 않은 시간대 구하기.
작성자
sysopmin
작성일
2012-02-28 17:39ⓒ
2012-02-28 18:01ⓜ
조회수
4,768

안녕하세요.
database sarang net 에서 검색하니 좋은 쿼리가 있어 참조하여 수정 중 다소 비효율적인 쿼리로
나와서 혹시나 개선할 방법이 있는지 여쭈어 봅니다.

출력을 원하는 것은 아래 with문의 row 데이터에서
특정 일자에서 시작시간(sdt) ~ 종료시간(edt) 이 없는 시간대를 출력하는 것입니다.
아래 최종 쿼리로 결과는 제대로 나옵니다만, 비 효율적인 부분이 보여 좀더 효율적으로
개선할 방안이 있는지 해서 글을 올립니다.

(비효율은 아래와 같아 보입니다)
1. union all 로 마지막 시간대를 하는 부분 말고 한방 쿼리로 가능할까요?
2. 마지막 시간대 부분의 레코드 출력 부분이 중복되는 부분


혹시나 저와 같은 고민으로 해결하신 선배님들 계시면 조언 바라겠습니다.
감사합니다.



쿼리 개발은 아래와 같습니다.


1. 23:59 분 전까지의 시간 대의 로데이터 레코드가 있는 만큼 중복 발생 함.
with t as (
  select to_date('201108011400', 'yyyy-mm-dd hh24:mi:ss') sdt
             ,to_date('201108011700', 'yyyy-mm-dd hh24:mi:ss') edt from dual
  union all
  select to_date('201108012000', 'yyyy-mm-dd hh24:mi:ss') sdt
             ,to_date('201108012259', 'yyyy-mm-dd hh24:mi:ss') edt from dual
)
select decode(from_dt
                          ,null,to_char(trunc(to_dt,'dd') + 1/24/60,'yyyy-mm-dd hh24:mi:ss')
                          ,to_char(from_dt, 'yyyy-mm-dd hh24:mi:ss')
            ) from_dt
           ,to_char(to_dt,'yyyy-mm-dd hh24:mi:ss') to_dt
  from (
     select max(edt)
                   over(order by sdt, edt
                            rows between unbounded preceding and 1 preceding
                    ) + 1/24/60
                as from_dt
               ,sdt - 1/24/60
                as to_dt
       from t
    union all
    select max(edt) 
                  over(order by sdt, edt 
                           rows between 1 preceding and 1 following
                  ) + 1/24/60
               as from_dt 
              ,trunc(edt+1,'dd') - 1/24/60
               as to_dt
      from t
 );
FROM_DT                          TO_DT
==========                     ===========
2011-08-01 00:01:00     2011-08-01 13:59:00
2011-08-01 17:01:00     2011-08-01 19:59:00
2011-08-01 23:00:00     2011-08-01 23:59:00                --- 중복
2011-08-01 23:00:00     2011-08-01 23:59:00                --- 중복 
 

2. rownum = 1 추가.
select decode(from_dt
                          ,null,to_char(trunc(to_dt,'dd') + 1/24/60,'yyyy-mm-dd hh24:mi:ss')
                          ,to_char(from_dt, 'yyyy-mm-dd hh24:mi:ss')
            ) from_dt
           ,to_char(to_dt,'yyyy-mm-dd hh24:mi:ss') to_dt
  from (
     select max(edt)
                   over(order by sdt, edt
                            rows between unbounded preceding and 1 preceding
                    ) + 1/24/60
                as from_dt
               ,sdt - 1/24/60
                as to_dt
       from t
    union all
    select from_dt
               ,to_dt
      from (
        select max(edt) 
                      over(order by sdt, edt
                               rows between 1 preceding and 1 following
                      ) + 1/24/60
                   as from_dt
                  ,trunc(edt+1,'dd') - 1/24/60
                   as to_dt
                  ,rownum rn
          from t
      )
     where rn = 1
  );
FROM_DT                          TO_DT
==========                     ===========
2011-08-01 00:01:00     2011-08-01 13:59:00
2011-08-01 17:01:00     2011-08-01 19:59:00
2011-08-01 23:00:00     2011-08-01 23:59:00 

 

 

이 글에 대한 댓글이 총 2건 있습니다.

WITH t(sdt, edt) AS
(
SELECT TO_DATE('201108010000', 'yyyymmddhh24mi')
     , TO_DATE('201108010100', 'yyyymmddhh24mi')
  FROM dual
 UNION ALL
SELECT TO_DATE('201108011400', 'yyyymmddhh24mi')
     , TO_DATE('201108011700', 'yyyymmddhh24mi')
  FROM dual
 UNION ALL
SELECT TO_DATE('201108012000', 'yyyymmddhh24mi')
     , TO_DATE('201108012259', 'yyyymmddhh24mi')
  FROM dual
 UNION ALL
SELECT TO_DATE('201108012330', 'yyyymmddhh24mi')
     , TO_DATE('201108012359', 'yyyymmddhh24mi')
  FROM dual
)
SELECT *
  FROM (
        SELECT LAG(edt + 1/24/60, 1, TRUNC(sdt))
               OVER(ORDER BY sdt, edt) sdt
             , sdt - 1/24/60 edt
          FROM t
         UNION ALL
        SELECT MAX(edt) + 1/24/60 sdt
             , TRUNC(MAX(edt)) + 1 - 1/24/60 edt
          FROM t
        )
 WHERE sdt < edt
;

1. 날짜 변환시 포맷을 정확하게 지정해 주세요.
  - 변경전 : TO_DATE('201108011400', 'yyyy-mm-dd hh24:mi:ss')
  - 변경후 : TO_DATE('201108011400', 'yyyymmddhh24mi')
2. 시간범위는 00:00 분분터 하는게 맞을 듯 합니다.
  - 변경전 : 00:01 ~ 23:59
  - 변경후 : 00:00 ~ 23:59
3. 또 다른 제안 : 굳이 1분씩 텀을 둘 필요가 있을까요?
  - 직전 종료값이 22시였다면
    다음 시작값도 22시 1분이 아닌 22시로 시작해도 무방하지 않을런지요?
    이부분은 고민사항으로 남겨두고...
4. 00시00분에 시작하거나 23:59분에 종료되는 것까지 고려하셔야 합니다.
5. 마지막 Union 부분은 분석함수가 아닌 구룹함수 사용하시면 1건만 나오죠.

마농(manon94)님이 2012-02-29 09:10에 작성한 댓글입니다.
이 댓글은 2012-02-29 09:12에 마지막으로 수정되었습니다.

좋은 답변 달아주셔서 감사합니다.

핵심은 이거 였네요. ㅋㅋ
SELECT MAX(edt) + 1/24/60 sdt
     , TRUNC(MAX(edt)) + 1 - 1/24/60 edt
  FROM t

 

참조 잘 하도록 하겠습니다.

sysopmin님이 2012-03-01 17:09에 작성한 댓글입니다. Edit
[Top]
No.
제목
작성자
작성일
조회
39322where절의 in 조건을 파라메터에 따라 부여할수 있을까요?? [2]
곰두리
2012-03-02
4174
39321DB & 소켓통신 질문입니다.(C++ 이용)
나진산
2012-02-29
4216
39320도와주세요 ㅠㅠㅠㅠ [2]
ㅠㅠ
2012-02-29
3678
39319특정일자의 시간대 데이터에서 입력되지 않은 시간대 구하기. [2]
sysopmin
2012-02-28
4768
39318개발서버. OS가 갑자기..깨지는바람에 ㅜㅜ,,,,,
흠흠흠
2012-02-27
3066
39317테이블 생성시 키 값에 대해.. [1]
아폴론
2012-02-27
3262
39316가로로 출력 하는방 법좀 조언 부탁합니다. [3]
강옥석
2012-02-27
5644
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2024 DSN, All rights reserved.
작업시간: 0.018초, 이곳 서비스는
	PostgreSQL v16.2로 자료를 관리합니다