none
Query Sort 비용이 너무 높게 나옵니다. RRS feed

  • 질문

  • 안녕하세요.

    실시간 사용내역 + 월누적금액을 합산하는 쿼리를 만들었는데 

    퍼포먼스 확인을 위해 테스트 데이터 약 350만건을 넣어두고 해보니 시간도 너무 오래걸리고 쿼리실행계획을 보니 Sort 관련 비용이 너무 높아질문 올립니다.


    쿼리는 아래와 같습니다.

    declare @money float
    set @money = 20

    select
    b.순서,
    b.서비스,
    b.건이용량 '건사용초',
    (b.사용시+':'+b.사용분+':'+b.사용초) as 건사용량,
    b.누적사용량 '누적사용초',
    (b.누적사용시+':'+b.누적사용분+':'+b.누적사용초) as 누적사용량,
    round(b.누적사용량 / convert(float,60),0) as 분변환,
    (round(b.누적사용량 / convert(float,60),0)) * @money as 월누적금액,
    convert(varchar(10),update_date,11) as 사용일자
    from
    (SELECT 
    *,
    case when len(((convert(int,건이용량) % 86400) / 3600)) = 1 
    then '0'+convert(varchar(2),((convert(int,건이용량) % 86400) / 3600))
    else convert(varchar(2),((convert(int,건이용량) % 86400) / 3600))
    end as '사용시',
    case when len((((convert(int,건이용량) % 86400) % 3600) /60)) = 1 
    then '0'+convert(varchar(2),(((convert(int,건이용량) % 86400) % 3600) /60))
    else convert(varchar(2),(((convert(int,건이용량) % 86400) % 3600) /60))
    end as '사용분',
    case when len((((convert(int,건이용량) % 8640) % 3600) % 60)) = 1 
    then '0'+convert(varchar(2),(((convert(int,건이용량) % 8640) % 3600) % 60))
    else convert(varchar(2),(((convert(int,건이용량) % 8640) % 3600) % 60))
    end as '사용초',
    case when len(((누적사용량 % 86400) / 3600)) = 1 
    then '0'+convert(varchar(2),((convert(int,누적사용량) % 86400) / 3600))
    else convert(varchar(2),((convert(int,누적사용량) % 86400) / 3600))
    end as '누적사용시',
    case when len((((convert(int,누적사용량) % 86400) % 3600) /60)) = 1 
    then '0'+convert(varchar(2),(((convert(int,누적사용량) % 86400) % 3600) /60))
    else convert(varchar(2),(((convert(int,누적사용량) % 86400) % 3600) /60))
    end as '누적사용분',
    case when len((((convert(int,누적사용량) % 8640) % 3600) % 60)) = 1 
    then '0'+convert(varchar(2),(((convert(int,누적사용량) % 8640) % 3600) % 60))
    else convert(varchar(2),(((convert(int,누적사용량) % 8640) % 3600) % 60))
    end as '누적사용초'
    from
    (

    select 
    row_number() over(order by update_date desc) as '순서',
    'Test' as '서비스',
    convert(int,round(((stt_duration/convert(float,1000000))/convert(float,2)),0)) as '건이용량',
    convert(int,sum(round(((stt_duration/convert(float,1000000))/convert(float,2)),0)) over (partition by convert(varchar(7),update_date,11) order by update_date)) as '누적사용량',
    update_date

    from app_api_sttusage_test 
    where client_id = 'Test'
    ) as a
    ) as b
    order by update_date desc

    위와 같이 sort 에서 각각 41%씩 비용을 잡아먹는데 하나는 update_date asc 다른 하나는 update_date desc 라서 어떻게 접근해야할지 모르겠습니다.

    해당 테이블 인덱스는 update_date desc로 적용해놨습니다.

    이런 쿼리는 어떻게 튜닝하면 좋을지요..?

    2019년 4월 23일 화요일 오전 3:11

답변

  • 안녕하세요

    ROW_NUMBER () function 칼럼에 이미 적용하였기에 "update_date" 칼럼에 쿼리 리코드를 다시 sort 필요가 없어보이며, 그냥 "row_number" 칼럼을 ORDER BY clause에 추가하시면 될거같습니다.

    퀴리를 아래와 같이 튜닝하였는데 퍼포먼스가 어떤지 테스트하시기 바랍니다.

    필요하시다면 “client_id” 칼럼에 non clustered index 를 만들어 차이가 있는지 확인하시기 바랍니다.

    declare @money float
    set @money = 20
    
    ;WITH a AS
    (
    select 
          row_number() over(order by update_date desc) as '순서',
          'Test' as '서비스',
          convert(int,round(((stt_duration/convert(float,1000000))/convert(float,2)),0)) as '건이용량',
          convert(int,sum(round(((stt_duration/convert(float,1000000))/convert(float,2)),0)) over (partition by convert(varchar(7),update_date,11) order by update_date)) as '누적사용량',
          update_date
          from app_api_sttusage_test 
          where client_id = 'Test'
    )
    select
    순서,
    서비스,
    건이용량 '건사용초',
    
    case when len(((convert(int,건이용량) % 86400) / 3600)) = 1 
          then '0'+convert(varchar(2),((convert(int,건이용량) % 86400) / 3600))
          else convert(varchar(2),((convert(int,건이용량) % 86400) / 3600))
      end+':'+case when len((((convert(int,건이용량) % 86400) % 3600) /60)) = 1 
           then '0'+convert(varchar(2),(((convert(int,건이용량) % 86400) % 3600) /60))
           else convert(varchar(2),(((convert(int,건이용량) % 86400) % 3600) /60))
           end+':'+case when len((((convert(int,건이용량) % 8640) % 3600) % 60)) = 1 
           then '0'+convert(varchar(2),(((convert(int,건이용량) % 8640) % 3600) % 60))
           else convert(varchar(2),(((convert(int,건이용량) % 8640) % 3600) % 60))
           end as 건사용량,
    누적사용량 '누적사용초',
    
    case when len(((누적사용량 % 86400) / 3600)) = 1 
           then '0'+convert(varchar(2),((convert(int,누적사용량) % 86400) / 3600))
           else convert(varchar(2),((convert(int,누적사용량) % 86400) / 3600))
           end+':'+ case when len((((convert(int,누적사용량) % 86400) % 3600) /60)) = 1 
           then '0'+convert(varchar(2),(((convert(int,누적사용량) % 86400) % 3600) /60))
           else convert(varchar(2),(((convert(int,누적사용량) % 86400) % 3600) /60))
           end+':'+ case when len((((convert(int,누적사용량) % 8640) % 3600) % 60)) = 1 
           then '0'+convert(varchar(2),(((convert(int,누적사용량) % 8640) % 3600) % 60))
           else convert(varchar(2),(((convert(int,누적사용량) % 8640) % 3600) % 60))
           end as 누적사용량,
    
    round(누적사용량 / convert(float,60),0) as 분변환,
    (round(누적사용량 / convert(float,60),0)) * @money as 월누적금액,
    convert(varchar(10),update_date,11) as 사용일자
    from a
    order by 순서
    /*
    (
    
    SELECT *,
      case when len(((convert(int,건이용량) % 86400) / 3600)) = 1 
          then '0'+convert(varchar(2),((convert(int,건이용량) % 86400) / 3600))
          else convert(varchar(2),((convert(int,건이용량) % 86400) / 3600))
      end as '사용시',
      case when len((((convert(int,건이용량) % 86400) % 3600) /60)) = 1 
           then '0'+convert(varchar(2),(((convert(int,건이용량) % 86400) % 3600) /60))
           else convert(varchar(2),(((convert(int,건이용량) % 86400) % 3600) /60))
           end as '사용분',
      case when len((((convert(int,건이용량) % 8640) % 3600) % 60)) = 1 
           then '0'+convert(varchar(2),(((convert(int,건이용량) % 8640) % 3600) % 60))
           else convert(varchar(2),(((convert(int,건이용량) % 8640) % 3600) % 60))
           end as '사용초',
      case when len(((누적사용량 % 86400) / 3600)) = 1 
           then '0'+convert(varchar(2),((convert(int,누적사용량) % 86400) / 3600))
           else convert(varchar(2),((convert(int,누적사용량) % 86400) / 3600))
           end as '누적사용시',
      case when len((((convert(int,누적사용량) % 86400) % 3600) /60)) = 1 
           then '0'+convert(varchar(2),(((convert(int,누적사용량) % 86400) % 3600) /60))
           else convert(varchar(2),(((convert(int,누적사용량) % 86400) % 3600) /60))
           end as '누적사용분',
      case when len((((convert(int,누적사용량) % 8640) % 3600) % 60)) = 1 
           then '0'+convert(varchar(2),(((convert(int,누적사용량) % 8640) % 3600) % 60))
           else convert(varchar(2),(((convert(int,누적사용량) % 8640) % 3600) % 60))
           end as '누적사용초'
      from a
    ) as b
    */
    --order by update_date desc

    MSDN Community Support Ricky

    커뮤니티 멤버에게 유익 할 수 있 문제를 해결 한 답변을 '답변으로 표시'를 클릭하고 그렇지 않은 경우 '답변으로 표시 취소'를 클릭하시기 바랍니다. MSDN 서포트에 대한 의견이나 불만이 있 경우 MSDNFSF@microsoft.com 으로 연락하시기 바랍니다.





    2019년 4월 25일 목요일 오전 7:46
    중재자