Browse Source

changed statistics query condition & setting pagination view

junggilpark 4 months ago
parent
commit
d06bb6df08

+ 6 - 3
src/main/java/egovframework/com/its/web/server/controller/TrafficStatsController.java

@@ -35,12 +35,13 @@ public class TrafficStatsController {
     private final AdminService adminService;
 
     @RequestMapping(value = "/getAtrdStat.do", method = RequestMethod.POST, produces = {"application/json; charset=utf8"})
-    public @ResponseBody List<AtrdStatVO> getAtrdStat(String[] atrdIds, String from, String to, String type) {
+    public @ResponseBody Map<String, Object> getAtrdStat(String[] atrdIds, String from, String to, String type) throws IOException {
         return this.trafficStatsService.getAtrdStat(atrdIds, from, to, type);
     }
 
     @RequestMapping(value = "/info.do", method = RequestMethod.GET)
-    public String info(Model model, HttpServletRequest request, HttpServletResponse response,  @Value("${apiKey}") String apiKey) {
+    public String info(Model model, HttpServletRequest request, HttpServletResponse response,  @Value("${apiKey}") String apiKey,
+                       @Value("${statistics.max-records}") String statLimit) {
 
         this.trafficStatsService.setDateParam(model);
 
@@ -48,16 +49,18 @@ public class TrafficStatsController {
         model.addAttribute("subTitle", "소통 통계");
         model.addAttribute("siteSubInfo", "statistics");
         model.addAttribute("key", apiKey);
+        model.addAttribute("statLimit", statLimit);
         return "statistics/info.tiles";
     }
 
     @RequestMapping(value = "/ixrTrafficStats.do", method = RequestMethod.GET)
-    public String trafficStats(Model model,  @Value("${apiKey}") String apiKey) {
+    public String trafficStats(Model model,  @Value("${apiKey}") String apiKey, @Value("${statistics.max-records}") String statLimit) {
         model.addAttribute("subTitle", "교차로 교통량 통계");
         model.addAttribute("siteSubInfo", "statistics");
         this.trafficStatsService.setDateParam(model);
         model.addAttribute("list", this.trafficStatsService.getIxrList());
         model.addAttribute("key", apiKey);
+        model.addAttribute("statLimit", statLimit);
         return "statistics/trafficStats.tiles";
     }
 

+ 5 - 0
src/main/java/egovframework/com/its/web/server/mapper/TrafficStatsMapper.java

@@ -2,6 +2,7 @@ package egovframework.com.its.web.server.mapper;
 
 import egovframework.com.its.web.server.vo.*;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.HashMap;
 import java.util.List;
@@ -23,4 +24,8 @@ public interface TrafficStatsMapper {
     List<ScIxrTrafficVO> findIxrHH(Map<String, Object> paramMap);
 
     List<ScIxrVO> getIxrList();
+
+    int getRoadCount(@Param("atrdIds") String[] atrdIds);
+
+    int getCameraCount(@Param("ixrIds") String[] ixrIds);
 }

+ 173 - 60
src/main/java/egovframework/com/its/web/server/service/TrafficStatsService.java

@@ -5,12 +5,12 @@ import egovframework.com.its.web.server.mapper.TrafficStatsMapper;
 import egovframework.com.its.web.server.vo.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.xssf.streaming.SXSSFSheet;
 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.ui.Model;
 
@@ -19,7 +19,9 @@ import java.io.IOException;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.function.Supplier;
 
 @RequiredArgsConstructor
 @Service
@@ -28,14 +30,60 @@ public class TrafficStatsService {
 
     private final TrafficStatsMapper trafficStatsMapper;
     private final CommonMapper commonMapper;
+    @Value("${statistics.max-records}")
+    String statLimit;
 
-    public List<AtrdStatVO> getAtrdStat(String[] atrdIds, String from, String to, String type) {
+    public Map<String, Object> getAtrdStat(String[] atrdIds, String from, String to, String type) throws IOException {
+        if (type.isEmpty() || atrdIds.length == 0 || from.isEmpty() || to.isEmpty()) {
+            throw new NullPointerException("파라미터 정보를 확인 해주세요.");
+        }
+        Map<String, Object> resultMap = new HashMap<>();
         Map<String, Object> paramMap = new HashMap<>();
         paramMap.put("atrdIds", atrdIds);
         paramMap.put("from", from);
         paramMap.put("to", to);
         paramMap.put("type", type);
-        return this.trafficStatsMapper.getAtrdStat(paramMap);
+
+        if (!type.equals("sYear")) {
+            LocalDateTime startDateTime = getDateValue(from);
+            LocalDateTime endDateTime = getDateValue(to);
+            LocalDateTime limitDate = getDateValue(from);
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+            int limit = Integer.parseInt(this.statLimit);
+            int total = this.trafficStatsMapper.getRoadCount(atrdIds);
+            int dataCount = limit / total;
+            long between = 0;
+            int addTimes =  (int)Math.floor(dataCount);
+
+            switch (type) {
+                case "sTime":
+                    between = ChronoUnit.HOURS.between(startDateTime, endDateTime);
+                    limitDate = limitDate.plusHours(addTimes);
+                    break;
+                case "sDay":
+                    between = ChronoUnit.DAYS.between(startDateTime, endDateTime);
+                    limitDate = limitDate.plusDays(addTimes);
+                    break;
+                case "sMonth":
+                    between = ChronoUnit.MONTHS.between(startDateTime, endDateTime);
+                    limitDate = limitDate.plusMonths(addTimes);
+                    break;
+            }
+
+            if (dataCount < between) {
+                String limitDateStr = limitDate.format(formatter);
+                log.error("조회 하신 기간이 데이터 조회 범위를 초과하였습니다. \n" + limitDateStr + " 이내로 조회해주세요.");
+                throw new IllegalArgumentException("조회 하신 기간이 데이터 조회 범위를 초과하였습니다. "+
+                        "\n조회 기간 : " + startDateTime.format(formatter) + " ~ " + endDateTime.format(formatter) +
+                        "\n가능 데이터 조회 범위 : " + startDateTime.format(formatter) + " ~ " +  limitDateStr);
+            }
+        }
+
+        List<AtrdStatVO> list = this.trafficStatsMapper.getAtrdStat(paramMap);
+        String base64 = getAtrdExcel(list, type);
+        resultMap.put("list", list);
+        resultMap.put("excel", base64);
+        return resultMap;
     }
 
     public List<AtrdVO> getAtrdNm() {
@@ -44,40 +92,77 @@ public class TrafficStatsService {
 
 //    public List<ScIxrTrafficVO> getIxrTrafficStat(String[] ixrIds, String from, String to, String type) throws IOException {
     public Map<String, Object> getIxrTrafficStat(String[] ixrIds, String from, String to, String type) throws IOException {
+//        long start = System.currentTimeMillis();
+        if (from == null || to == null || ixrIds.length == 0 || type == null) {
+            throw new NullPointerException("파라미터 정보를 확인 해주세요");
+        }
+        int total = this.trafficStatsMapper.getCameraCount(ixrIds);
+        if (total == 0) {
+            throw new NullPointerException("설치된 카메라 정보를 조회 할 수 없습니다.");
+        }
+
+        int limit = Integer.parseInt(statLimit);
+
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        long between = 0;
+        int hourPerCount = (type.equals("15M")) ? 4 : 1;
+        LocalDateTime startDateTime = getDateValue(from);
+        LocalDateTime endDateTime = getDateValue(to);
+        LocalDateTime limitDate = getDateValue(from);
+        int dataCount = limit / (total * hourPerCount);
+        int addTimes =  (int)Math.floor(dataCount);
+        if (type.equals("15M") || type.equals("HH")) {
+            between = ChronoUnit.HOURS.between(startDateTime, endDateTime);
+            limitDate = limitDate.plusHours(addTimes);
+        }
+        else if (type.equals("DD")) {
+            between = ChronoUnit.DAYS.between(startDateTime, endDateTime);
+            limitDate = limitDate.plusDays(addTimes);
+        }
+        else if (type.equals("MN")) {
+            between = ChronoUnit.MONTHS.between(startDateTime, endDateTime);
+            limitDate = limitDate.plusMonths(addTimes);
+        }
+
+        if (dataCount < between) {
+            String limitDateStr = limitDate.format(formatter);
+            log.error("조회하신 기간이 데이터 조회 범위를 초과하였습니다. \n" + limitDateStr + " 이내로 조회해주세요.");
+            throw new IllegalArgumentException("조회하신 기간이 데이터 조회 범위를 초과하였습니다."+
+                    "\n조회 기간 : " + startDateTime.format(formatter) + " ~ " + endDateTime.format(formatter) +
+                    "\n가능 데이터 조회 범위 : " + startDateTime.format(formatter) + " ~ " +  limitDateStr);
+        }
+
         Map<String, Object> paramMap = new HashMap<>();
         List<ScIxrTrafficVO> result = null;
         paramMap.put("ixrIds", ixrIds);
         paramMap.put("from", from);
         paramMap.put("to", to);
+        Map<String, Supplier<List<ScIxrTrafficVO>>> methodMap = new HashMap<>();
+        methodMap.put("15M", () -> this.trafficStatsMapper.findIxr15M(paramMap));
+        methodMap.put("HH", () -> this.trafficStatsMapper.findIxrHH(paramMap));
+        methodMap.put("DD", () -> this.trafficStatsMapper.findIxrDD(paramMap));
+        methodMap.put("MN", () -> this.trafficStatsMapper.findIxrMN(paramMap));
 
-        switch (type) {
-            case "15M":
-                result = this.trafficStatsMapper.findIxr15M(paramMap);
-                break;
-            case "HH":
-                result = this.trafficStatsMapper.findIxrHH(paramMap);
-                break;
-            case "DD":
-                result = this.trafficStatsMapper.findIxrDD(paramMap);
-                break;
-            case "MN":
-                result = this.trafficStatsMapper.findIxrMN(paramMap);
-                break;
-            default:
-                return null;
-        }
+        result = methodMap.get(type).get();
         Map<String, Object> resultMap = new HashMap<>();
-        long start = System.currentTimeMillis();
         String base64 = downExcel(result, type);
-        log.info("Down Excel 소요시간 : {}ms", System.currentTimeMillis() - start);
         resultMap.put("list", result);
         resultMap.put("excel", base64);
+//        log.info("소요시간 {}ms", System.currentTimeMillis() - start);
         return resultMap;
     }
+
+    public LocalDateTime getDateValue(String word) {
+        int year       = Integer.parseInt(word.substring(0,4));
+        int month      = Integer.parseInt(word.substring(4,6));
+        int day        = Integer.parseInt(word.substring(6,8));
+        int hour       = Integer.parseInt(word.substring(8,10));
+        int minute     = Integer.parseInt(word.substring(10,12));
+        int seconds    = Integer.parseInt(word.substring(12,14));
+        return LocalDateTime.of(year, month, day, hour, minute, seconds);
+    }
+
     public String downExcel(List<ScIxrTrafficVO> list, String type) throws IOException{
-//        Map<String, String> resultMap = new HashMap<>();
-//        String success = "F";
-//        String message = "교차로 교통량 통계 정보를 확인 할 수 없습니다.";
         if (list != null && list.size() > 0) {
                 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                 Workbook workbook = new SXSSFWorkbook(2000);
@@ -89,28 +174,22 @@ public class TrafficStatsService {
                     headerRow.createCell(i).setCellValue(columns[i]);
                 }
                 int rowNum = 1;
+                DateTimeFormatter inputFormatter  = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+                String pattern = "yyyy-MM-dd HH:mm:ss";
+                if (type.equals("MN")) {
+                    pattern = "yyyy년 MM월";
+                }
+                else if (type.equals("DD")) {
+                    pattern = "yyyy/MM/dd";
+                }
+
+                DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(pattern);
+
                 for (ScIxrTrafficVO vo : list) {
                     Row row = sheet.createRow(rowNum);
                     String clctDt = vo.getClctDt();
-                    if (type.equals("MN")) {
-                        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
-                        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월");
-                        LocalDate dateTime = LocalDate.parse(clctDt, inputFormatter);
-                        clctDt = dateTime.format(outputFormatter);
-                    }
-                    else if (type.equals("DD")) {
-                        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
-                        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
-                        LocalDate dateTime = LocalDate.parse(clctDt, inputFormatter);
-                        clctDt = dateTime.format(outputFormatter);
-                    }
-                    else {
-                        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
-                        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-                        LocalDateTime dateTime = LocalDateTime.parse(clctDt, inputFormatter);
-                        clctDt = dateTime.format(outputFormatter);
-                    }
-
+                    LocalDateTime dateTime = LocalDateTime.parse(clctDt, inputFormatter);
+                    clctDt = dateTime.format(outputFormatter);
                     row.createCell(0).setCellValue(vo.getIxrId());
                     row.createCell(1).setCellValue(vo.getIxrNm());
                     row.createCell(2).setCellValue(vo.getCmraNm());
@@ -140,28 +219,62 @@ public class TrafficStatsService {
                 // Base64 인코딩
                 String base64Encoded = Base64.getEncoder().encodeToString(outputStream.toByteArray());
                 outputStream.close();
-
-//                success = "S";
-//                message = "엑셀 파일 전송 완료.";
-//                resultMap.put("success", success);
-//                resultMap.put("message", message);
-//                resultMap.put("base64", base64Encoded);
-
                 return base64Encoded;
-//            } catch (IOException e) {
-//                return null;
-//                resultMap.put("success", "F");
-//                resultMap.put("message", e.getMessage());
-//            }
         }
-//        else {
-//
-//            resultMap.put("success", success);
-//            resultMap.put("message", message);
-//        }
+        return null;
+    }
+
+    public String getAtrdExcel(List<AtrdStatVO> list, String type) throws IOException{
+        if (list != null && list.size() > 0) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            Workbook workbook = new SXSSFWorkbook(2000);
+            Sheet sheet = workbook.createSheet("교통소통 통계"); // 시트명 지정
+            Row headerRow = sheet.createRow(0);
+            String[] columns = { "도로명", "구간", "통계시각", "속도(km/h)", "통행시간(sec)"};
+            for (int i = 0; i < columns.length; i++) {
+                headerRow.createCell(i).setCellValue(columns[i]);
+            }
+            int rowNum = 1;
 
+            DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+            String pattern = "yyyy-MM-dd HH:mm:ss";
+            if (type.equals("sYear")) {
+                pattern = "yyyy년";
+            }
+            else if (type.equals("sMonth")) {
+                pattern = "yyyy년 MM월";
+            }
+            else if (type.equals("sDay")) {
+                pattern = "yyyy/MM/dd";
+            }
+
+            DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(pattern);
+            for (AtrdStatVO vo : list) {
+                Row row = sheet.createRow(rowNum);
+                String statDt = vo.getSTAT_DT();
+
+                LocalDateTime dateTime = LocalDateTime.parse(statDt, inputFormatter);
+                statDt = dateTime.format(outputFormatter);
+                row.createCell(0).setCellValue(vo.getROAD_NM());
+                row.createCell(1).setCellValue(vo.getSTRT_NM() + " - " + vo.getEND_NM());
+                row.createCell(2).setCellValue(statDt);
+                row.createCell(3).setCellValue(vo.getSPED());
+                row.createCell(4).setCellValue(vo.getTRVL_HH());
+                if (rowNum % 2000 == 0) {  // 500개 행마다 flush
+                    ((SXSSFSheet) sheet).flushRows(2000);
+                }
+                rowNum++;
+            }
+            workbook.write(outputStream);
+            workbook.close();
+            // Base64 인코딩
+            String base64Encoded = Base64.getEncoder().encodeToString(outputStream.toByteArray());
+            outputStream.close();
+            return base64Encoded;
+        }
         return null;
     }
+
     public List<ScIxrVO> getIxrList() {
         return this.trafficStatsMapper.getIxrList();
     }

+ 1 - 0
src/main/java/egovframework/com/its/web/server/vo/AtrdVO.java

@@ -15,5 +15,6 @@ public class AtrdVO  implements Serializable {
 
     private String atrd_id;
     private String atrd_nm;
+    private Integer road_count;
 
 }

+ 1 - 0
src/main/java/egovframework/com/its/web/server/vo/ScIxrVO.java

@@ -15,4 +15,5 @@ public class ScIxrVO implements Serializable {
     private String IXR_NM;
     private Double IXR_X_CRDN;
     private Double IXR_Y_CRDN;
+    private Integer CMRA_COUNT;
 }

+ 3 - 0
src/main/resources/application.yml

@@ -6,6 +6,8 @@ application:
     region-code: 228
     region-name: 평택시
 
+statistics:
+  max-records: 350000
 
 server:
   port: 80
@@ -112,6 +114,7 @@ server:
     key-store-password: 1234
     enabled: true
   port: 443
+
 ---
 #spring:
 #  config:

+ 8 - 5
src/main/resources/egovframework/sqlmap/common.xml

@@ -573,11 +573,14 @@
         Oracle XE 11g 이상에서는 WM_CONCAT() 사용을 못하고,
         대신에 LISTAGG 사용하면 된다.
 -->
-        SELECT ATRD_NM as atrd_nm,
-               LISTAGG(ATRD_ID,',') as atrd_id
-        FROM TB_ATRD
-        WHERE DEL_YN = 'N'
-        GROUP BY ATRD_NM ORDER BY ATRD_NM
+        SELECT A.ATRD_NM as atrd_nm,
+               LISTAGG(DISTINCT(A.ATRD_ID),',') as atrd_id,
+               COUNT(B.ATRD_ID) AS road_count
+          FROM TB_ATRD A
+          JOIN TB_ATRD_RLTN_ROAD B
+            ON A.ATRD_ID = B.ATRD_ID
+         WHERE DEL_YN = 'N'
+         GROUP BY ATRD_NM ORDER BY ATRD_NM
 
 <!-- (티베로) AGGR_CONCAT : 여러행 데이터를 하나의 컬럼으로..
         SELECT ATRD_NM as atrd_nm,

+ 367 - 372
src/main/resources/egovframework/sqlmap/trafficStats.xml

@@ -86,78 +86,24 @@
 			ATRD_NM
 	</select>
 
-<!--	<select id="findIxr15M" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT-->
-<!--		    CLCT_DT AS clctDt,-->
-<!--			IXR_ID AS ixrId,-->
-<!--			IXR_NM AS ixrNm,-->
-<!--			GO_TFVL AS goTfvl,-->
-<!--			LEFT_TFVL AS leftTfvl,-->
-<!--			RGHT_TFVL AS rghtTfvl,-->
-<!--			UTURN_TFVL AS uturnTfvl,-->
-<!--			(GO_TFVL + LEFT_TFVL + UTURN_TFVL + RGHT_TFVL) AS totTfvl-->
-<!--		FROM (SELECT-->
-<!--				  A.CLCT_DT,-->
-<!--				  A.IXR_ID,-->
-<!--				  B.IXR_NM,-->
-<!--				  SUM(NVL(A.LRG_GO_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_GO_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_GO_TFVL, 0)-->
-<!--					  + NVL(A.BUS_DVRS_LANE_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_GO_TFVL, 0)-->
-<!--					  ) GO_TFVL,-->
-<!--				  SUM(NVL(A.LRG_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.BUS_DVRS_LANE_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_LEFT_TFVL, 0)-->
-<!--					  ) LEFT_TFVL,-->
-<!--				  SUM(NVL(A.LRG_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_RGHT_TFVL, 0)-->
-<!--					  ) RGHT_TFVL,-->
-<!--				  SUM(NVL(A.UTURN_TFVL, 0)-->
-<!--					  + NVL(A.LRG_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_UTURN_TFVL, 0)-->
-<!--					  ) UTURN_TFVL-->
-<!--			  FROM TB_SC_ACRD_STAT_15M A, TB_SC_IXR_MNGM B-->
-<!--			  WHERE (1=1)-->
-<!--				AND A.IXR_ID IN (<foreach collection="ixrIds" item="item" separator=",">#{item}</foreach>)-->
-<!--				AND A.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--				AND A.IXR_ID = B.IXR_ID-->
-<!--			  GROUP BY A.CLCT_DT, A.IXR_ID, B.IXR_NM)-->
-<!--			ORDER BY CLCT_DT, TO_NUMBER(IXR_ID), IXR_NM-->
-<!--	</select>-->
 	<select id="findIxr15M" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">
 		SELECT C.CLCT_DT clctDt,
 		       A.IXR_ID AS ixrId,
 			   B.IXR_NM AS ixrNm,
 			   NVL(A.ISTL_LCTN, '-') AS cmraNm,
-			   DECODE(A.DRCT_DVSN_CD, 1, '북', 2, '동', 3, '남', 4, '서', 5, '북동', 6, '남동', 7, '남서', 8, '북서', '-') AS drctDvsnCd,
 			   A.DRCT_DVSN_CD_R AS drctDvsnCdR,
-			   NVL(C.SMAL_LEFT_TFVL,  0) smalLeftTfvl,
-			   NVL(C.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,
-			   NVL(C.LRG_LEFT_TFVL,   0) lrgLeftTfvl,
-			   NVL(C.SMAL_GO_TFVL,    0) smalGoTfvl,
-			   NVL(C.MDDL_GO_TFVL,    0) mddlGoTfvl,
-			   NVL(C.LRG_GO_TFVL,     0) lrgGoTfvl,
-			   NVL(C.SMAL_RGHT_TFVL,  0) smalRghtTfvl,
-			   NVL(C.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,
-			   NVL(C.LRG_RGHT_TFVL,   0) lrgRghtTfvl,
-			   NVL(C.SMAL_UTURN_TFVL, 0) smalUturnTfvl,
-			   NVL(C.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,
-			   NVL(C.LRG_UTURN_TFVL,  0) lrgUturnTfvl,
+			   NVL(C.SMAL_LEFT_TFVL,  0) AS smalLeftTfvl,
+			   NVL(C.MDDL_LEFT_TFVL,  0) AS mddlLeftTfvl,
+			   NVL(C.LRG_LEFT_TFVL,   0) AS lrgLeftTfvl,
+			   NVL(C.SMAL_GO_TFVL,    0) AS smalGoTfvl,
+			   NVL(C.MDDL_GO_TFVL,    0) AS mddlGoTfvl,
+			   NVL(C.LRG_GO_TFVL,     0) AS lrgGoTfvl,
+			   NVL(C.SMAL_RGHT_TFVL,  0) AS smalRghtTfvl,
+			   NVL(C.MDDL_RGHT_TFVL,  0) AS mddlRghtTfvl,
+			   NVL(C.LRG_RGHT_TFVL,   0) AS lrgRghtTfvl,
+			   NVL(C.SMAL_UTURN_TFVL, 0) AS smalUturnTfvl,
+			   NVL(C.MDDL_UTURN_TFVL, 0) AS mddlUturnTfvl,
+			   NVL(C.LRG_UTURN_TFVL,  0) AS lrgUturnTfvl,
 			   (
 					   NVL(C.SMAL_LEFT_TFVL,  0) +
 					   NVL(C.MDDL_LEFT_TFVL,  0) +
@@ -171,7 +117,7 @@
 					   NVL(C.SMAL_UTURN_TFVL, 0) +
 					   NVL(C.MDDL_UTURN_TFVL, 0) +
 					   NVL(C.LRG_UTURN_TFVL,  0)
-				   ) totTfvl
+				   ) AS totTfvl
 		FROM TB_SC_IXR_CMRA_MNGM A
 		JOIN TB_SC_IXR_MNGM B
 		  ON A.IXR_ID = B.IXR_ID
@@ -182,59 +128,79 @@
 		  AND C.CLCT_DT BETWEEN #{from} AND #{to}
 		ORDER BY C.CLCT_DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD
 	</select>
-
-<!--	<select id="findIxrHH" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT-->
-<!--			CLCT_DT AS clctDt,-->
-<!--			IXR_ID AS ixrId,-->
-<!--			IXR_NM AS ixrNm,-->
-<!--			GO_TFVL AS goTfvl,-->
-<!--			LEFT_TFVL AS leftTfvl,-->
-<!--			RGHT_TFVL AS rghtTfvl,-->
-<!--			UTURN_TFVL AS uturnTfvl,-->
-<!--			(GO_TFVL + LEFT_TFVL + UTURN_TFVL + RGHT_TFVL) AS totTfvl-->
-<!--		FROM (SELECT-->
-<!--				  A.CLCT_DT,-->
-<!--				  A.IXR_ID,-->
-<!--				  B.IXR_NM,-->
-<!--				  SUM(NVL(A.LRG_GO_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_GO_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_GO_TFVL, 0)-->
-<!--					  + NVL(A.BUS_DVRS_LANE_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_GO_TFVL, 0)-->
-<!--					  ) GO_TFVL,-->
-<!--				  SUM(NVL(A.LRG_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.BUS_DVRS_LANE_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_LEFT_TFVL, 0)-->
-<!--					  ) LEFT_TFVL,-->
-<!--				  SUM(NVL(A.LRG_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_RGHT_TFVL, 0)-->
-<!--					  ) RGHT_TFVL,-->
-<!--				  SUM(NVL(A.UTURN_TFVL, 0)-->
-<!--					  + NVL(A.LRG_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_UTURN_TFVL, 0)-->
-<!--					  ) UTURN_TFVL-->
-<!--			  FROM TB_SC_ACRD_STAT_HH A, TB_SC_IXR_MNGM B-->
-<!--			  WHERE (1=1)-->
-<!--				AND A.IXR_ID IN (<foreach collection="ixrIds" item="item" separator=",">#{item}</foreach>)-->
-<!--				AND A.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--				AND A.IXR_ID = B.IXR_ID-->
-<!--			  GROUP BY A.CLCT_DT, A.IXR_ID, B.IXR_NM)-->
-<!--		ORDER BY CLCT_DT, TO_NUMBER(IXR_ID), IXR_NM-->
+<!--	<select id="findIxr15M" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
+<!--		WITH DATE_RANGE AS (-->
+<!--			SELECT TO_CHAR(TO_DATE(#{from}, 'YYYYMMDDHH24MISS') + (LEVEL - 1)  * INTERVAL '15' MINUTE, 'YYYYMMDDHH24MISS') AS CLCT_DT-->
+<!--			FROM DUAL-->
+<!--			CONNECT BY LEVEL <![CDATA[<=]]> (TO_DATE(#{to}, 'YYYYMMDDHH24MISS') - TO_DATE(#{from}, 'YYYYMMDDHH24MISS') + 1) * 24 * 4-->
+<!--		),-->
+<!--		IXR_COMBINATIONS AS (-->
+<!--			SELECT DISTINCT A.IXR_ID, A.DRCT_DVSN_CD-->
+<!--			  FROM TB_SC_IXR_CMRA_MNGM A-->
+<!--		),-->
+<!--		BASE_DATA AS (-->
+<!--			SELECT D.CLCT_DT, I.IXR_ID, I.DRCT_DVSN_CD-->
+<!--			  FROM DATE_RANGE D-->
+<!--			 CROSS JOIN IXR_COMBINATIONS I-->
+<!--		),-->
+<!--		TRAFFIC_DATA AS (-->
+<!--			SELECT IXR_ID, DRCT_DVSN_CD, CLCT_DT AS CLCT_DT,-->
+<!--				   SUM(NVL(SMAL_LEFT_TFVL,  0)) AS SMAL_LEFT_TFVL,-->
+<!--				   SUM(NVL(MDDL_LEFT_TFVL,  0)) AS MDDL_LEFT_TFVL,-->
+<!--				   SUM(NVL(LRG_LEFT_TFVL,   0)) AS LRG_LEFT_TFVL,-->
+<!--				   SUM(NVL(SMAL_GO_TFVL,    0)) AS SMAL_GO_TFVL,-->
+<!--				   SUM(NVL(MDDL_GO_TFVL,    0)) AS MDDL_GO_TFVL,-->
+<!--				   SUM(NVL(LRG_GO_TFVL,     0)) AS LRG_GO_TFVL,-->
+<!--				   SUM(NVL(SMAL_RGHT_TFVL,  0)) AS SMAL_RGHT_TFVL,-->
+<!--				   SUM(NVL(MDDL_RGHT_TFVL,  0)) AS MDDL_RGHT_TFVL,-->
+<!--				   SUM(NVL(LRG_RGHT_TFVL,   0)) AS LRG_RGHT_TFVL,-->
+<!--				   SUM(NVL(SMAL_UTURN_TFVL, 0)) AS SMAL_UTURN_TFVL,-->
+<!--				   SUM(NVL(MDDL_UTURN_TFVL, 0)) AS MDDL_UTURN_TFVL,-->
+<!--				   SUM(NVL(LRG_UTURN_TFVL,  0)) AS LRG_UTURN_TFVL-->
+<!--			  FROM TB_SC_ACRD_STAT_15M-->
+<!--		     WHERE IXR_ID IN-->
+<!--			<foreach collection="ixrIds" item="item" open="(" separator="," close=")">-->
+<!--				#{item}-->
+<!--			</foreach>-->
+<!--		       AND CLCT_DT BETWEEN #{from} AND #{to}-->
+<!--		     GROUP BY IXR_ID, DRCT_DVSN_CD, CLCT_DT-->
+<!--		)-->
+<!--		SELECT B.CLCT_DT AS clctDt,-->
+<!--			   A.IXR_ID AS ixrId,-->
+<!--			   X.IXR_NM AS ixrNm,-->
+<!--			   NVL(A.ISTL_LCTN, '-') AS cmraNm,-->
+<!--			   A.DRCT_DVSN_CD_R AS drctDvsnCdR,-->
+<!--			   NVL(T.SMAL_LEFT_TFVL,  0) smalLeftTfvl,-->
+<!--			   NVL(T.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,-->
+<!--			   NVL(T.LRG_LEFT_TFVL,   0) lrgLeftTfvl,-->
+<!--			   NVL(T.SMAL_GO_TFVL,    0) smalGoTfvl,-->
+<!--			   NVL(T.MDDL_GO_TFVL,    0) mddlGoTfvl,-->
+<!--			   NVL(T.LRG_GO_TFVL,     0) lrgGoTfvl,-->
+<!--			   NVL(T.SMAL_RGHT_TFVL,  0) smalRghtTfvl,-->
+<!--			   NVL(T.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,-->
+<!--			   NVL(T.LRG_RGHT_TFVL,   0) lrgRghtTfvl,-->
+<!--			   NVL(T.SMAL_UTURN_TFVL, 0) smalUturnTfvl,-->
+<!--			   NVL(T.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,-->
+<!--			   NVL(T.LRG_UTURN_TFVL,  0) lrgUturnTfvl,-->
+<!--			   (-->
+<!--				   NVL(T.SMAL_LEFT_TFVL,  0) +-->
+<!--				   NVL(T.MDDL_LEFT_TFVL,  0) +-->
+<!--				   NVL(T.LRG_LEFT_TFVL,   0) +-->
+<!--				   NVL(T.SMAL_GO_TFVL,    0) +-->
+<!--				   NVL(T.MDDL_GO_TFVL,    0) +-->
+<!--				   NVL(T.LRG_GO_TFVL,     0) +-->
+<!--				   NVL(T.SMAL_RGHT_TFVL,  0) +-->
+<!--				   NVL(T.MDDL_RGHT_TFVL,  0) +-->
+<!--				   NVL(T.LRG_RGHT_TFVL,   0) +-->
+<!--				   NVL(T.SMAL_UTURN_TFVL, 0) +-->
+<!--				   NVL(T.MDDL_UTURN_TFVL, 0) +-->
+<!--				   NVL(T.LRG_UTURN_TFVL,  0)-->
+<!--			   ) AS totTfvl-->
+<!--		  FROM BASE_DATA B-->
+<!--		  LEFT JOIN TB_SC_IXR_CMRA_MNGM A ON B.IXR_ID = A.IXR_ID AND B.DRCT_DVSN_CD = A.DRCT_DVSN_CD-->
+<!--		  LEFT JOIN TB_SC_IXR_MNGM X ON A.IXR_ID = X.IXR_ID-->
+<!--		  LEFT JOIN TRAFFIC_DATA T ON B.IXR_ID = T.IXR_ID AND B.DRCT_DVSN_CD = T.DRCT_DVSN_CD AND B.CLCT_DT = T.CLCT_DT-->
+<!--		 ORDER BY B.CLCT_DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD-->
 <!--	</select>-->
 
 	<select id="findIxrHH" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">
@@ -242,20 +208,19 @@
 			   A.IXR_ID AS ixrId,
 			   B.IXR_NM AS ixrNm,
 			   NVL(A.ISTL_LCTN, '-') AS cmraNm,
-			   DECODE(A.DRCT_DVSN_CD, 1, '북', 2, '동', 3, '남', 4, '서', 5, '북동', 6, '남동', 7, '남서', 8, '북서', '-') AS drctDvsnCd,
 		       A.DRCT_DVSN_CD_R AS drctDvsnCdR,
-			   NVL(C.SMAL_LEFT_TFVL,  0) smalLeftTfvl,
-			   NVL(C.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,
-			   NVL(C.LRG_LEFT_TFVL,   0) lrgLeftTfvl,
-			   NVL(C.SMAL_GO_TFVL,    0) smalGoTfvl,
-			   NVL(C.MDDL_GO_TFVL,    0) mddlGoTfvl,
-			   NVL(C.LRG_GO_TFVL,     0) lrgGoTfvl,
-			   NVL(C.SMAL_RGHT_TFVL,  0) smalRghtTfvl,
-			   NVL(C.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,
-			   NVL(C.LRG_RGHT_TFVL,   0) lrgRghtTfvl,
-			   NVL(C.SMAL_UTURN_TFVL, 0) smalUturnTfvl,
-			   NVL(C.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,
-			   NVL(C.LRG_UTURN_TFVL,  0) lrgUturnTfvl,
+			   NVL(C.SMAL_LEFT_TFVL,  0) AS smalLeftTfvl,
+			   NVL(C.MDDL_LEFT_TFVL,  0) AS mddlLeftTfvl,
+			   NVL(C.LRG_LEFT_TFVL,   0) AS lrgLeftTfvl,
+			   NVL(C.SMAL_GO_TFVL,    0) AS smalGoTfvl,
+			   NVL(C.MDDL_GO_TFVL,    0) AS mddlGoTfvl,
+			   NVL(C.LRG_GO_TFVL,     0) AS lrgGoTfvl,
+			   NVL(C.SMAL_RGHT_TFVL,  0) AS smalRghtTfvl,
+			   NVL(C.MDDL_RGHT_TFVL,  0) AS mddlRghtTfvl,
+			   NVL(C.LRG_RGHT_TFVL,   0) AS lrgRghtTfvl,
+			   NVL(C.SMAL_UTURN_TFVL, 0) AS smalUturnTfvl,
+			   NVL(C.MDDL_UTURN_TFVL, 0) AS mddlUturnTfvl,
+			   NVL(C.LRG_UTURN_TFVL,  0) AS lrgUturnTfvl,
 			   (
 				   NVL(C.SMAL_LEFT_TFVL,  0) +
 				   NVL(C.MDDL_LEFT_TFVL,  0) +
@@ -269,7 +234,7 @@
 				   NVL(C.SMAL_UTURN_TFVL, 0) +
 				   NVL(C.MDDL_UTURN_TFVL, 0) +
 				   NVL(C.LRG_UTURN_TFVL,  0)
-			   ) totTfvl
+			   ) AS totTfvl
 		 FROM TB_SC_IXR_CMRA_MNGM A
 		 JOIN TB_SC_IXR_MNGM B
 		   ON A.IXR_ID = B.IXR_ID
@@ -280,26 +245,173 @@
 		  AND C.CLCT_DT BETWEEN #{from} AND #{to}
 		ORDER BY C.CLCT_DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD
 	</select>
+<!--	<select id="findIxrHH" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
+<!--		WITH DATE_RANGE AS (-->
+<!--			SELECT TO_CHAR(TO_DATE(#{from}, 'YYYYMMDDHH24MISS') + (LEVEL - 1) / 24, 'YYYYMMDDHH24MISS') AS CLCT_DT-->
+<!--			FROM DUAL-->
+<!--			CONNECT BY LEVEL <![CDATA[<=]]> (TO_DATE(#{to}, 'YYYYMMDDHH24MISS') - TO_DATE(#{from}, 'YYYYMMDDHH24MISS') + 1) * 24-->
+<!--		),-->
+<!--		IXR_COMBINATIONS AS (-->
+<!--			SELECT DISTINCT A.IXR_ID, A.DRCT_DVSN_CD-->
+<!--			  FROM TB_SC_IXR_CMRA_MNGM A-->
+<!--		),-->
+<!--		BASE_DATA AS (-->
+<!--			SELECT D.CLCT_DT, I.IXR_ID, I.DRCT_DVSN_CD-->
+<!--			  FROM DATE_RANGE D-->
+<!--			 CROSS JOIN IXR_COMBINATIONS I-->
+<!--		),-->
+<!--		TRAFFIC_DATA AS (-->
+<!--			SELECT IXR_ID, DRCT_DVSN_CD, CLCT_DT AS CLCT_DT,-->
+<!--				   SUM(NVL(SMAL_LEFT_TFVL,  0)) AS SMAL_LEFT_TFVL,-->
+<!--				   SUM(NVL(MDDL_LEFT_TFVL,  0)) AS MDDL_LEFT_TFVL,-->
+<!--				   SUM(NVL(LRG_LEFT_TFVL,   0)) AS LRG_LEFT_TFVL,-->
+<!--				   SUM(NVL(SMAL_GO_TFVL,    0)) AS SMAL_GO_TFVL,-->
+<!--				   SUM(NVL(MDDL_GO_TFVL,    0)) AS MDDL_GO_TFVL,-->
+<!--				   SUM(NVL(LRG_GO_TFVL,     0)) AS LRG_GO_TFVL,-->
+<!--				   SUM(NVL(SMAL_RGHT_TFVL,  0)) AS SMAL_RGHT_TFVL,-->
+<!--				   SUM(NVL(MDDL_RGHT_TFVL,  0)) AS MDDL_RGHT_TFVL,-->
+<!--				   SUM(NVL(LRG_RGHT_TFVL,   0)) AS LRG_RGHT_TFVL,-->
+<!--				   SUM(NVL(SMAL_UTURN_TFVL, 0)) AS SMAL_UTURN_TFVL,-->
+<!--				   SUM(NVL(MDDL_UTURN_TFVL, 0)) AS MDDL_UTURN_TFVL,-->
+<!--				   SUM(NVL(LRG_UTURN_TFVL,  0)) AS LRG_UTURN_TFVL-->
+<!--			  FROM TB_SC_ACRD_STAT_HH-->
+<!--		     WHERE IXR_ID IN-->
+<!--			<foreach collection="ixrIds" item="item" open="(" separator="," close=")">-->
+<!--				#{item}-->
+<!--			</foreach>-->
+<!--		       AND CLCT_DT BETWEEN #{from} AND #{to}-->
+<!--		     GROUP BY IXR_ID, DRCT_DVSN_CD, CLCT_DT-->
+<!--		)-->
+<!--		SELECT B.CLCT_DT AS clctDt,-->
+<!--			   A.IXR_ID AS ixrId,-->
+<!--			   X.IXR_NM AS ixrNm,-->
+<!--			   NVL(A.ISTL_LCTN, '-') AS cmraNm,-->
+<!--			   A.DRCT_DVSN_CD_R AS drctDvsnCdR,-->
+<!--			   NVL(T.SMAL_LEFT_TFVL,  0) smalLeftTfvl,-->
+<!--			   NVL(T.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,-->
+<!--			   NVL(T.LRG_LEFT_TFVL,   0) lrgLeftTfvl,-->
+<!--			   NVL(T.SMAL_GO_TFVL,    0) smalGoTfvl,-->
+<!--			   NVL(T.MDDL_GO_TFVL,    0) mddlGoTfvl,-->
+<!--			   NVL(T.LRG_GO_TFVL,     0) lrgGoTfvl,-->
+<!--			   NVL(T.SMAL_RGHT_TFVL,  0) smalRghtTfvl,-->
+<!--			   NVL(T.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,-->
+<!--			   NVL(T.LRG_RGHT_TFVL,   0) lrgRghtTfvl,-->
+<!--			   NVL(T.SMAL_UTURN_TFVL, 0) smalUturnTfvl,-->
+<!--			   NVL(T.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,-->
+<!--			   NVL(T.LRG_UTURN_TFVL,  0) lrgUturnTfvl,-->
+<!--			   (-->
+<!--				   NVL(T.SMAL_LEFT_TFVL,  0) +-->
+<!--				   NVL(T.MDDL_LEFT_TFVL,  0) +-->
+<!--				   NVL(T.LRG_LEFT_TFVL,   0) +-->
+<!--				   NVL(T.SMAL_GO_TFVL,    0) +-->
+<!--				   NVL(T.MDDL_GO_TFVL,    0) +-->
+<!--				   NVL(T.LRG_GO_TFVL,     0) +-->
+<!--				   NVL(T.SMAL_RGHT_TFVL,  0) +-->
+<!--				   NVL(T.MDDL_RGHT_TFVL,  0) +-->
+<!--				   NVL(T.LRG_RGHT_TFVL,   0) +-->
+<!--				   NVL(T.SMAL_UTURN_TFVL, 0) +-->
+<!--				   NVL(T.MDDL_UTURN_TFVL, 0) +-->
+<!--				   NVL(T.LRG_UTURN_TFVL,  0)-->
+<!--			   ) AS totTfvl-->
+<!--		  FROM BASE_DATA B-->
+<!--		  LEFT JOIN TB_SC_IXR_CMRA_MNGM A ON B.IXR_ID = A.IXR_ID AND B.DRCT_DVSN_CD = A.DRCT_DVSN_CD-->
+<!--		  LEFT JOIN TB_SC_IXR_MNGM X ON A.IXR_ID = X.IXR_ID-->
+<!--		  LEFT JOIN TRAFFIC_DATA T ON B.IXR_ID = T.IXR_ID AND B.DRCT_DVSN_CD = T.DRCT_DVSN_CD AND B.CLCT_DT = T.CLCT_DT-->
+<!--		 ORDER BY B.CLCT_DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD-->
+<!--	</select>-->
 
+<!--	<select id="findIxrDD" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
+<!--		WITH DATE_RANGE AS (-->
+<!--			SELECT TO_CHAR(TO_DATE(#{from}, 'YYYYMMDDHH24MISS') + LEVEL - 1, 'YYYYMMDDHH24MISS') AS CLCT_DT-->
+<!--			FROM DUAL-->
+<!--			CONNECT BY LEVEL <![CDATA[<=]]> TO_DATE(#{to}, 'YYYYMMDDHH24MISS') - TO_DATE(#{from}, 'YYYYMMDDHH24MISS') + 1-->
+<!--		),-->
+<!--		IXR_COMBINATIONS AS (-->
+<!--			SELECT DISTINCT A.IXR_ID, A.DRCT_DVSN_CD-->
+<!--			  FROM TB_SC_IXR_CMRA_MNGM A-->
+<!--		),-->
+<!--		BASE_DATA AS (-->
+<!--			SELECT D.CLCT_DT, I.IXR_ID, I.DRCT_DVSN_CD-->
+<!--			  FROM DATE_RANGE D-->
+<!--			 CROSS JOIN IXR_COMBINATIONS I-->
+<!--		),-->
+<!--		TRAFFIC_DATA AS (-->
+<!--			SELECT IXR_ID, DRCT_DVSN_CD, CLCT_DT AS CLCT_DT,-->
+<!--				   SUM(NVL(SMAL_LEFT_TFVL,  0)) AS SMAL_LEFT_TFVL,-->
+<!--				   SUM(NVL(MDDL_LEFT_TFVL,  0)) AS MDDL_LEFT_TFVL,-->
+<!--				   SUM(NVL(LRG_LEFT_TFVL,   0)) AS LRG_LEFT_TFVL,-->
+<!--				   SUM(NVL(SMAL_GO_TFVL,    0)) AS SMAL_GO_TFVL,-->
+<!--				   SUM(NVL(MDDL_GO_TFVL,    0)) AS MDDL_GO_TFVL,-->
+<!--				   SUM(NVL(LRG_GO_TFVL,     0)) AS LRG_GO_TFVL,-->
+<!--				   SUM(NVL(SMAL_RGHT_TFVL,  0)) AS SMAL_RGHT_TFVL,-->
+<!--				   SUM(NVL(MDDL_RGHT_TFVL,  0)) AS MDDL_RGHT_TFVL,-->
+<!--				   SUM(NVL(LRG_RGHT_TFVL,   0)) AS LRG_RGHT_TFVL,-->
+<!--				   SUM(NVL(SMAL_UTURN_TFVL, 0)) AS SMAL_UTURN_TFVL,-->
+<!--				   SUM(NVL(MDDL_UTURN_TFVL, 0)) AS MDDL_UTURN_TFVL,-->
+<!--				   SUM(NVL(LRG_UTURN_TFVL,  0)) AS LRG_UTURN_TFVL-->
+<!--			  FROM TB_SC_ACRD_STAT_DD-->
+<!--		     WHERE IXR_ID IN-->
+<!--			<foreach collection="ixrIds" item="item" open="(" separator="," close=")">-->
+<!--				#{item}-->
+<!--			</foreach>-->
+<!--		       AND CLCT_DT BETWEEN #{from} AND #{to}-->
+<!--		     GROUP BY IXR_ID, DRCT_DVSN_CD, CLCT_DT-->
+<!--		)-->
+<!--		SELECT B.CLCT_DT AS clctDt,-->
+<!--			   A.IXR_ID AS ixrId,-->
+<!--			   X.IXR_NM AS ixrNm,-->
+<!--			   NVL(A.ISTL_LCTN, '-') AS cmraNm,-->
+<!--			   A.DRCT_DVSN_CD_R AS drctDvsnCdR,-->
+<!--			   NVL(T.SMAL_LEFT_TFVL,  0) smalLeftTfvl,-->
+<!--			   NVL(T.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,-->
+<!--			   NVL(T.LRG_LEFT_TFVL,   0) lrgLeftTfvl,-->
+<!--			   NVL(T.SMAL_GO_TFVL,    0) smalGoTfvl,-->
+<!--			   NVL(T.MDDL_GO_TFVL,    0) mddlGoTfvl,-->
+<!--			   NVL(T.LRG_GO_TFVL,     0) lrgGoTfvl,-->
+<!--			   NVL(T.SMAL_RGHT_TFVL,  0) smalRghtTfvl,-->
+<!--			   NVL(T.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,-->
+<!--			   NVL(T.LRG_RGHT_TFVL,   0) lrgRghtTfvl,-->
+<!--			   NVL(T.SMAL_UTURN_TFVL, 0) smalUturnTfvl,-->
+<!--			   NVL(T.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,-->
+<!--			   NVL(T.LRG_UTURN_TFVL,  0) lrgUturnTfvl,-->
+<!--			   (-->
+<!--				   NVL(T.SMAL_LEFT_TFVL,  0) +-->
+<!--				   NVL(T.MDDL_LEFT_TFVL,  0) +-->
+<!--				   NVL(T.LRG_LEFT_TFVL,   0) +-->
+<!--				   NVL(T.SMAL_GO_TFVL,    0) +-->
+<!--				   NVL(T.MDDL_GO_TFVL,    0) +-->
+<!--				   NVL(T.LRG_GO_TFVL,     0) +-->
+<!--				   NVL(T.SMAL_RGHT_TFVL,  0) +-->
+<!--				   NVL(T.MDDL_RGHT_TFVL,  0) +-->
+<!--				   NVL(T.LRG_RGHT_TFVL,   0) +-->
+<!--				   NVL(T.SMAL_UTURN_TFVL, 0) +-->
+<!--				   NVL(T.MDDL_UTURN_TFVL, 0) +-->
+<!--				   NVL(T.LRG_UTURN_TFVL,  0)-->
+<!--			   ) AS totTfvl-->
+<!--		  FROM BASE_DATA B-->
+<!--		  LEFT JOIN TB_SC_IXR_CMRA_MNGM A ON B.IXR_ID = A.IXR_ID AND B.DRCT_DVSN_CD = A.DRCT_DVSN_CD-->
+<!--		  LEFT JOIN TB_SC_IXR_MNGM X ON A.IXR_ID = X.IXR_ID-->
+<!--		  LEFT JOIN TRAFFIC_DATA T ON B.IXR_ID = T.IXR_ID AND B.DRCT_DVSN_CD = T.DRCT_DVSN_CD AND B.CLCT_DT = T.CLCT_DT-->
+<!--		 ORDER BY B.CLCT_DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD-->
+<!--	 </select>-->
 	<select id="findIxrDD" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">
 		SELECT C.CLCT_DT clctDt,
 			   A.IXR_ID AS ixrId,
 			   B.IXR_NM AS ixrNm,
 			   NVL(A.ISTL_LCTN, '-') AS cmraNm,
-			   DECODE(A.DRCT_DVSN_CD, 1, '북', 2, '동', 3, '남', 4, '서', 5, '북동', 6, '남동', 7, '남서', 8, '북서', '-') AS drctDvsnCd,
 			   A.DRCT_DVSN_CD_R AS drctDvsnCdR,
-			   NVL(C.SMAL_LEFT_TFVL,  0) smalLeftTfvl,
-			   NVL(C.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,
-			   NVL(C.LRG_LEFT_TFVL,   0) lrgLeftTfvl,
-			   NVL(C.SMAL_GO_TFVL,    0) smalGoTfvl,
-			   NVL(C.MDDL_GO_TFVL,    0) mddlGoTfvl,
-			   NVL(C.LRG_GO_TFVL,     0) lrgGoTfvl,
-			   NVL(C.SMAL_RGHT_TFVL,  0) smalRghtTfvl,
-			   NVL(C.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,
-			   NVL(C.LRG_RGHT_TFVL,   0) lrgRghtTfvl,
-			   NVL(C.SMAL_UTURN_TFVL, 0) smalUturnTfvl,
-			   NVL(C.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,
-			   NVL(C.LRG_UTURN_TFVL,  0) lrgUturnTfvl,
+			   NVL(C.SMAL_LEFT_TFVL,  0) AS smalLeftTfvl,
+			   NVL(C.MDDL_LEFT_TFVL,  0) AS mddlLeftTfvl,
+			   NVL(C.LRG_LEFT_TFVL,   0) AS lrgLeftTfvl,
+			   NVL(C.SMAL_GO_TFVL,    0) AS smalGoTfvl,
+			   NVL(C.MDDL_GO_TFVL,    0) AS mddlGoTfvl,
+			   NVL(C.LRG_GO_TFVL,     0) AS lrgGoTfvl,
+			   NVL(C.SMAL_RGHT_TFVL,  0) AS smalRghtTfvl,
+			   NVL(C.MDDL_RGHT_TFVL,  0) AS mddlRghtTfvl,
+			   NVL(C.LRG_RGHT_TFVL,   0) AS lrgRghtTfvl,
+			   NVL(C.SMAL_UTURN_TFVL, 0) AS smalUturnTfvl,
+			   NVL(C.MDDL_UTURN_TFVL, 0) AS mddlUturnTfvl,
+			   NVL(C.LRG_UTURN_TFVL,  0) AS lrgUturnTfvl,
 			   (
 				   NVL(C.SMAL_LEFT_TFVL,  0) +
 				   NVL(C.MDDL_LEFT_TFVL,  0) +
@@ -313,7 +425,7 @@
 				   NVL(C.SMAL_UTURN_TFVL, 0) +
 				   NVL(C.MDDL_UTURN_TFVL, 0) +
 				   NVL(C.LRG_UTURN_TFVL,  0)
-			   ) totTfvl
+			   ) AS totTfvl
 		 FROM TB_SC_IXR_CMRA_MNGM A
 		 JOIN TB_SC_IXR_MNGM B
 		   ON A.IXR_ID = B.IXR_ID
@@ -325,49 +437,6 @@
 		ORDER BY C.CLCT_DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD
     </select>
 
-<!--	<select id="findIxrMN" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT C.CLCT_DT clctDt,-->
-<!--			   A.IXR_ID AS ixrId,-->
-<!--			   B.IXR_NM AS ixrNm,-->
-<!--			   NVL(A.ISTL_LCTN, '-') AS cmraNm,-->
-<!--			   DECODE(A.DRCT_DVSN_CD, 1, '북', 2, '동', 3, '남', 4, '서', 5, '북동', 6, '남동', 7, '남서', 8, '북서', '-') AS drctDvsnCd,-->
-<!--			   A.DRCT_DVSN_CD_R AS drctDvsnCdR,-->
-<!--			   NVL(C.SMAL_LEFT_TFVL,  0) smalLeftTfvl,-->
-<!--			   NVL(C.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,-->
-<!--			   NVL(C.LRG_LEFT_TFVL,   0) lrgLeftTfvl,-->
-<!--			   NVL(C.SMAL_GO_TFVL,    0) smalGoTfvl,-->
-<!--			   NVL(C.MDDL_GO_TFVL,    0) mddlGoTfvl,-->
-<!--			   NVL(C.LRG_GO_TFVL,     0) lrgGoTfvl,-->
-<!--			   NVL(C.SMAL_RGHT_TFVL,  0) smalRghtTfvl,-->
-<!--			   NVL(C.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,-->
-<!--			   NVL(C.LRG_RGHT_TFVL,   0) lrgRghtTfvl,-->
-<!--			   NVL(C.SMAL_UTURN_TFVL, 0) smalUturnTfvl,-->
-<!--			   NVL(C.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,-->
-<!--			   NVL(C.LRG_UTURN_TFVL,  0) lrgUturnTfvl,-->
-<!--			   (-->
-<!--				   NVL(C.SMAL_LEFT_TFVL,  0) +-->
-<!--				   NVL(C.MDDL_LEFT_TFVL,  0) +-->
-<!--				   NVL(C.LRG_LEFT_TFVL,   0) +-->
-<!--				   NVL(C.SMAL_GO_TFVL,    0) +-->
-<!--				   NVL(C.MDDL_GO_TFVL,    0) +-->
-<!--				   NVL(C.LRG_GO_TFVL,     0) +-->
-<!--				   NVL(C.SMAL_RGHT_TFVL,  0) +-->
-<!--				   NVL(C.MDDL_RGHT_TFVL,  0) +-->
-<!--				   NVL(C.LRG_RGHT_TFVL,   0) +-->
-<!--				   NVL(C.SMAL_UTURN_TFVL, 0) +-->
-<!--				   NVL(C.MDDL_UTURN_TFVL, 0) +-->
-<!--				   NVL(C.LRG_UTURN_TFVL,  0)-->
-<!--			   ) totTfvl-->
-<!--		 FROM TB_SC_IXR_CMRA_MNGM A-->
-<!--		 JOIN TB_SC_IXR_MNGM B-->
-<!--		   ON A.IXR_ID = B.IXR_ID-->
-<!--		 LEFT OUTER JOIN TB_SC_ACRD_STAT_MN C-->
-<!--		              ON C.IXR_ID = A.IXR_ID-->
-<!--				     AND C.DRCT_DVSN_CD = A.DRCT_DVSN_CD-->
-<!--		WHERE A.IXR_ID IN (<foreach collection="ixrIds" item="item" separator=",">#{item}</foreach>)-->
-<!--		  AND C.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--		ORDER BY C.CLCT_DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD-->
-<!--	</select>-->
 	<select id="findIxrMN" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">
 		SELECT C.CLCT_DT || '01000000' clctDt,
 			   A.IXR_ID AS ixrId,
@@ -375,18 +444,18 @@
 			   NVL(A.ISTL_LCTN, '-') AS cmraNm,
 			   DECODE(A.DRCT_DVSN_CD, 1, '북', 2, '동', 3, '남', 4, '서', 5, '북동', 6, '남동', 7, '남서', 8, '북서', '-') AS drctDvsnCd,
 			   A.DRCT_DVSN_CD_R AS drctDvsnCdR,
-			   NVL(C.SMAL_LEFT_TFVL,  0) smalLeftTfvl,
-			   NVL(C.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,
-			   NVL(C.LRG_LEFT_TFVL,   0) lrgLeftTfvl,
-			   NVL(C.SMAL_GO_TFVL,    0) smalGoTfvl,
-			   NVL(C.MDDL_GO_TFVL,    0) mddlGoTfvl,
-			   NVL(C.LRG_GO_TFVL,     0) lrgGoTfvl,
-			   NVL(C.SMAL_RGHT_TFVL,  0) smalRghtTfvl,
-			   NVL(C.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,
-			   NVL(C.LRG_RGHT_TFVL,   0) lrgRghtTfvl,
-			   NVL(C.SMAL_UTURN_TFVL, 0) smalUturnTfvl,
-			   NVL(C.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,
-			   NVL(C.LRG_UTURN_TFVL,  0) lrgUturnTfvl,
+			   NVL(C.SMAL_LEFT_TFVL,  0) AS smalLeftTfvl,
+			   NVL(C.MDDL_LEFT_TFVL,  0) AS mddlLeftTfvl,
+			   NVL(C.LRG_LEFT_TFVL,   0) AS lrgLeftTfvl,
+			   NVL(C.SMAL_GO_TFVL,    0) AS smalGoTfvl,
+			   NVL(C.MDDL_GO_TFVL,    0) AS mddlGoTfvl,
+			   NVL(C.LRG_GO_TFVL,     0) AS lrgGoTfvl,
+			   NVL(C.SMAL_RGHT_TFVL,  0) AS smalRghtTfvl,
+			   NVL(C.MDDL_RGHT_TFVL,  0) AS mddlRghtTfvl,
+			   NVL(C.LRG_RGHT_TFVL,   0) AS lrgRghtTfvl,
+			   NVL(C.SMAL_UTURN_TFVL, 0) AS smalUturnTfvl,
+			   NVL(C.MDDL_UTURN_TFVL, 0) AS mddlUturnTfvl,
+			   NVL(C.LRG_UTURN_TFVL,  0) AS lrgUturnTfvl,
 			   (
 					   NVL(C.SMAL_LEFT_TFVL,  0) +
 					   NVL(C.MDDL_LEFT_TFVL,  0) +
@@ -400,7 +469,7 @@
 					   NVL(C.SMAL_UTURN_TFVL, 0) +
 					   NVL(C.MDDL_UTURN_TFVL, 0) +
 					   NVL(C.LRG_UTURN_TFVL,  0)
-				   ) totTfvl
+				   ) AS totTfvl
 		FROM TB_SC_IXR_CMRA_MNGM A
 				 JOIN TB_SC_IXR_MNGM B
 					  ON A.IXR_ID = B.IXR_ID
@@ -427,179 +496,105 @@
 		WHERE A.IXR_ID IN (<foreach collection="ixrIds" item="item" separator=",">#{item}</foreach>)
 		ORDER BY C.CLCT_DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD
 	</select>
-
-<!--	<select id="findIxrDD" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT-->
-<!--			CLCT_DT AS clctDt,-->
-<!--			IXR_ID AS ixrId,-->
-<!--			IXR_NM AS ixrNm,-->
-<!--			GO_TFVL AS goTfvl,-->
-<!--			LEFT_TFVL AS leftTfvl,-->
-<!--			RGHT_TFVL AS rghtTfvl,-->
-<!--			UTURN_TFVL AS uturnTfvl,-->
-<!--			(GO_TFVL + LEFT_TFVL + UTURN_TFVL + RGHT_TFVL) AS totTfvl-->
-<!--		FROM (SELECT-->
-<!--				  A.CLCT_DT,-->
-<!--				  A.IXR_ID,-->
-<!--				  B.IXR_NM,-->
-<!--				  SUM(NVL(A.LRG_GO_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_GO_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_GO_TFVL, 0)-->
-<!--					  + NVL(A.BUS_DVRS_LANE_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_GO_TFVL, 0)-->
-<!--					  ) GO_TFVL,-->
-<!--				  SUM(NVL(A.LRG_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.BUS_DVRS_LANE_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_LEFT_TFVL, 0)-->
-<!--					  ) LEFT_TFVL,-->
-<!--				  SUM(NVL(A.LRG_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_RGHT_TFVL, 0)-->
-<!--					  ) RGHT_TFVL,-->
-<!--				  SUM(NVL(A.UTURN_TFVL, 0)-->
-<!--					  + NVL(A.LRG_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_UTURN_TFVL, 0)-->
-<!--					  ) UTURN_TFVL-->
-<!--			  FROM TB_SC_ACRD_STAT_DD A, TB_SC_IXR_MNGM B-->
-<!--			  WHERE (1=1)-->
-<!--				AND A.IXR_ID IN (<foreach collection="ixrIds" item="item" separator=",">#{item}</foreach>)-->
-<!--				AND A.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--				AND A.IXR_ID = B.IXR_ID-->
-<!--			  GROUP BY A.CLCT_DT, A.IXR_ID, B.IXR_NM)-->
-<!--		ORDER BY CLCT_DT, TO_NUMBER(IXR_ID), IXR_NM-->
-<!--	</select>-->
-
 <!--	<select id="findIxrMN" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT-->
-<!--			CLCT_DT AS clctDt,-->
-<!--			IXR_ID AS ixrId,-->
-<!--			IXR_NM AS ixrNm,-->
-<!--			GO_TFVL AS goTfvl,-->
-<!--			LEFT_TFVL AS leftTfvl,-->
-<!--			RGHT_TFVL AS rghtTfvl,-->
-<!--			UTURN_TFVL AS uturnTfvl,-->
-<!--			(GO_TFVL + LEFT_TFVL + UTURN_TFVL + RGHT_TFVL) AS totTfvl-->
-<!--		FROM (SELECT-->
-<!--				  A.CLCT_DT,-->
-<!--				  A.IXR_ID,-->
-<!--				  B.IXR_NM,-->
-<!--				  SUM(NVL(A.LRG_GO_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_GO_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_GO_TFVL, 0)-->
-<!--					  + NVL(A.BUS_DVRS_LANE_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_GO_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_GO_TFVL, 0)-->
-<!--					  ) GO_TFVL,-->
-<!--				  SUM(NVL(A.LRG_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.BUS_DVRS_LANE_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_LEFT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_LEFT_TFVL, 0)-->
-<!--					  ) LEFT_TFVL,-->
-<!--				  SUM(NVL(A.LRG_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_RGHT_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_RGHT_TFVL, 0)-->
-<!--					  ) RGHT_TFVL,-->
-<!--				  SUM(NVL(A.UTURN_TFVL, 0)-->
-<!--					  + NVL(A.LRG_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.MDDL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.SMAL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_LRG_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_MDDL_UTURN_TFVL, 0)-->
-<!--					  + NVL(A.PCE_SMAL_UTURN_TFVL, 0)-->
-<!--					  ) UTURN_TFVL-->
-<!--			  FROM TB_SC_ACRD_STAT_MN A, TB_SC_IXR_MNGM B-->
-<!--			  WHERE (1=1)-->
-<!--				AND A.IXR_ID IN (<foreach collection="ixrIds" item="item" separator=",">#{item}</foreach>)-->
-<!--				AND A.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--				AND A.IXR_ID = B.IXR_ID-->
-<!--			  GROUP BY A.CLCT_DT, A.IXR_ID, B.IXR_NM)-->
-<!--		ORDER BY CLCT_DT, IXR_ID, IXR_NM-->
-<!--	</select>-->
-<!--	<select id="findIxr15M" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT B.IXR_ID AS ixrId,-->
-<!--			   B.IXR_NM AS ixrNm,-->
-<!--			   A.CLCT_DT AS statDt,-->
-<!--			   A.SRVC_LVL AS srvcLvl,-->
-<!--			   A.DELY_HH AS delyHh,-->
-<!--			   A.TFVL AS tfvl-->
-<!--		  FROM TB_SC_IXR_STAT_15M A-->
-<!--		 INNER JOIN TB_SC_IXR_MNGM B-->
-<!--		    ON A.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--		   AND A.IXR_ID = B.IXR_ID-->
-<!--		   AND B.IXR_ID IN (#{ixrIds})-->
+<!--		WITH DATE_RANGE AS (-->
+<!--			SELECT TO_CHAR(ADD_MONTHS(TO_DATE(#{from}, 'YYYYMMDDHH24MISS'), LEVEL - 1), 'YYYYMM') AS DT-->
+<!--			  FROM DUAL-->
+<!--		   CONNECT BY LEVEL <![CDATA[<=]]> MONTHS_BETWEEN(TO_DATE(#{to}, 'YYYYMMDDHH24MISS'), TO_DATE(#{from}, 'YYYYMMDDHH24MISS')) + 1-->
+<!--		),-->
+<!--		IXR_COMBINATIONS AS (-->
+<!--			SELECT DISTINCT A.IXR_ID, A.DRCT_DVSN_CD-->
+<!--			  FROM TB_SC_IXR_CMRA_MNGM A-->
+<!--		),-->
+<!--		BASE_DATA AS (-->
+<!--			SELECT D.DT, I.IXR_ID, I.DRCT_DVSN_CD-->
+<!--			  FROM DATE_RANGE D-->
+<!--		     CROSS JOIN IXR_COMBINATIONS I-->
+<!--		),-->
+<!--		TRAFFIC_DATA AS (-->
+<!--			SELECT IXR_ID, DRCT_DVSN_CD, SUBSTR(CLCT_DT, 1, 6) AS DT,-->
+<!--				SUM(SMAL_LEFT_TFVL) AS SMAL_LEFT_TFVL,-->
+<!--				SUM(MDDL_LEFT_TFVL) AS MDDL_LEFT_TFVL,-->
+<!--				SUM(LRG_LEFT_TFVL) AS LRG_LEFT_TFVL,-->
+<!--				SUM(SMAL_GO_TFVL) AS SMAL_GO_TFVL,-->
+<!--				SUM(MDDL_GO_TFVL) AS MDDL_GO_TFVL,-->
+<!--				SUM(LRG_GO_TFVL) AS LRG_GO_TFVL,-->
+<!--				SUM(SMAL_RGHT_TFVL) AS SMAL_RGHT_TFVL,-->
+<!--				SUM(MDDL_RGHT_TFVL) AS MDDL_RGHT_TFVL,-->
+<!--				SUM(LRG_RGHT_TFVL) AS LRG_RGHT_TFVL,-->
+<!--				SUM(SMAL_UTURN_TFVL) AS SMAL_UTURN_TFVL,-->
+<!--				SUM(MDDL_UTURN_TFVL) AS MDDL_UTURN_TFVL,-->
+<!--				SUM(LRG_UTURN_TFVL) AS LRG_UTURN_TFVL-->
+<!--			 FROM TB_SC_ACRD_STAT_DD-->
+<!--			WHERE IXR_ID IN (<foreach collection="ixrIds" item="item" separator=",">#{item}</foreach>)-->
+<!--			  AND CLCT_DT BETWEEN #{from} AND #{to}-->
+<!--		    GROUP BY IXR_ID, DRCT_DVSN_CD, SUBSTR(CLCT_DT, 1, 6)-->
+<!--		)-->
+<!--		SELECT B.DT || '01000000' AS clctDt,-->
+<!--			A.IXR_ID AS ixrId,-->
+<!--			X.IXR_NM AS ixrNm,-->
+<!--			NVL(A.ISTL_LCTN, '-') AS cmraNm,-->
+<!--			A.DRCT_DVSN_CD_R AS drctDvsnCdR,-->
+<!--			NVL(T.SMAL_LEFT_TFVL,  0) smalLeftTfvl,-->
+<!--			NVL(T.MDDL_LEFT_TFVL,  0) mddlLeftTfvl,-->
+<!--			NVL(T.LRG_LEFT_TFVL,   0) lrgLeftTfvl,-->
+<!--			NVL(T.SMAL_GO_TFVL,    0) smalGoTfvl,-->
+<!--			NVL(T.MDDL_GO_TFVL,    0) mddlGoTfvl,-->
+<!--			NVL(T.LRG_GO_TFVL,     0) lrgGoTfvl,-->
+<!--			NVL(T.SMAL_RGHT_TFVL,  0) smalRghtTfvl,-->
+<!--			NVL(T.MDDL_RGHT_TFVL,  0) mddlRghtTfvl,-->
+<!--			NVL(T.LRG_RGHT_TFVL,   0) lrgRghtTfvl,-->
+<!--			NVL(T.SMAL_UTURN_TFVL, 0) smalUturnTfvl,-->
+<!--			NVL(T.MDDL_UTURN_TFVL, 0) mddlUturnTfvl,-->
+<!--			NVL(T.LRG_UTURN_TFVL,  0) lrgUturnTfvl,-->
+<!--			(-->
+<!--				NVL(T.SMAL_LEFT_TFVL,  0) +-->
+<!--				NVL(T.MDDL_LEFT_TFVL,  0) +-->
+<!--				NVL(T.LRG_LEFT_TFVL,   0) +-->
+<!--				NVL(T.SMAL_GO_TFVL,    0) +-->
+<!--				NVL(T.MDDL_GO_TFVL,    0) +-->
+<!--				NVL(T.LRG_GO_TFVL,     0) +-->
+<!--				NVL(T.SMAL_RGHT_TFVL,  0) +-->
+<!--				NVL(T.MDDL_RGHT_TFVL,  0) +-->
+<!--				NVL(T.LRG_RGHT_TFVL,   0) +-->
+<!--				NVL(T.SMAL_UTURN_TFVL, 0) +-->
+<!--				NVL(T.MDDL_UTURN_TFVL, 0) +-->
+<!--				NVL(T.LRG_UTURN_TFVL,  0)-->
+<!--			) AS totTfvl-->
+<!--		  FROM BASE_DATA B-->
+<!--		  LEFT JOIN TB_SC_IXR_CMRA_MNGM A ON B.IXR_ID = A.IXR_ID AND B.DRCT_DVSN_CD = A.DRCT_DVSN_CD-->
+<!--		  LEFT JOIN TB_SC_IXR_MNGM X ON A.IXR_ID = X.IXR_ID-->
+<!--		  LEFT JOIN TRAFFIC_DATA T ON B.IXR_ID = T.IXR_ID AND B.DRCT_DVSN_CD = T.DRCT_DVSN_CD AND B.DT = T.DT-->
+<!--		 ORDER BY B.DT, TO_NUMBER(A.IXR_ID), A.DRCT_DVSN_CD-->
 <!--	</select>-->
 
-<!--	<select id="findIxrHH" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT B.IXR_ID AS ixrId,-->
-<!--		B.IXR_NM AS ixrNm,-->
-<!--		A.CLCT_DT AS statDt,-->
-<!--		A.SRVC_LVL AS srvcLvl,-->
-<!--		A.DELY_HH AS delyHh,-->
-<!--		A.TFVL AS tfvl-->
-<!--		FROM TB_SC_IXR_STAT_HH A-->
-<!--		INNER JOIN TB_SC_IXR_MNGM B-->
-<!--		ON A.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--		AND A.IXR_ID = B.IXR_ID-->
-<!--		AND B.IXR_ID IN	(#{ixrIds})-->
-<!--	</select>-->
-
-<!--	<select id="findIxrDD" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT B.IXR_ID AS ixrId,-->
-<!--		B.IXR_NM AS ixrNm,-->
-<!--		A.CLCT_DT AS statDt,-->
-<!--		A.SRVC_LVL AS srvcLvl,-->
-<!--		A.DELY_HH AS delyHh,-->
-<!--		A.TFVL AS tfvl-->
-<!--		FROM TB_SC_IXR_STAT_DD A-->
-<!--		INNER JOIN TB_SC_IXR_MNGM B-->
-<!--		ON A.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--		AND A.IXR_ID = B.IXR_ID-->
-<!--		AND B.IXR_ID IN	(#{ixrIds})-->
-<!--	</select>-->
-
-<!--	<select id="findIxrMN" parameterType="java.util.HashMap" resultType="egovframework.com.its.web.server.vo.ScIxrTrafficVO" fetchSize="1000">-->
-<!--		SELECT B.IXR_ID AS ixrId,-->
-<!--		B.IXR_NM AS ixrNm,-->
-<!--		A.CLCT_DT AS statDt,-->
-<!--		A.SRVC_LVL AS srvcLvl,-->
-<!--		A.DELY_HH AS delyHh,-->
-<!--		A.TFVL AS tfvl-->
-<!--		FROM TB_SC_IXR_STAT_MN A-->
-<!--		INNER JOIN TB_SC_IXR_MNGM B-->
-<!--		ON A.CLCT_DT BETWEEN #{from} AND #{to}-->
-<!--		AND A.IXR_ID = B.IXR_ID-->
-<!--		AND B.IXR_ID IN	(#{ixrIds})-->
-<!--	</select>-->
 
 	<select id="getIxrList" resultType="egovframework.com.its.web.server.vo.ScIxrVO" fetchSize="1000">
 		SELECT
-			IXR_ID,
-		    IXR_NM,
-		    IXR_X_CRDN,
-			IXR_Y_CRDN
-		FROM TB_SC_IXR_MNGM
-		WHERE USE_EN = 1
-		ORDER BY CAST(IXR_ID AS decimal) ASC
+			A.IXR_ID,
+			A.IXR_NM,
+			A.IXR_X_CRDN,
+			A.IXR_Y_CRDN,
+			COUNT(B.IXR_ID) CMRA_COUNT
+		FROM (SELECT * FROM TB_SC_IXR_MNGM WHERE USE_EN = 1) A
+		JOIN TB_SC_IXR_CMRA_MNGM B
+		  ON A.IXR_ID = B.IXR_ID
+	   GROUP BY A.IXR_ID, A.IXR_NM, A.IXR_X_CRDN, A.IXR_Y_CRDN
+	   ORDER BY CAST(IXR_ID AS decimal) ASC
 	</select>
-	
+
+	<select id="getRoadCount" parameterType="java.lang.String" resultType="java.lang.Integer">
+		SELECT COUNT(*)
+		 FROM (SELECT * FROM TB_ATRD WHERE DEL_YN = 'N' AND ATRD_ID IN <foreach collection="atrdIds" open="(" separator="," close=")" item="item">#{item}</foreach>) A
+		 JOIN TB_ATRD_RLTN_ROAD B
+		   ON A.ATRD_ID = B.ATRD_ID
+	</select>
+
+	<select id="getCameraCount" parameterType="java.lang.String" resultType="java.lang.Integer">
+		SELECT COUNT(*)
+		 FROM (SELECT * FROM TB_SC_IXR_MNGM WHERE USE_EN = 1 AND IXR_ID IN <foreach collection="ixrIds" open="(" separator="," close=")" item="item">#{item}</foreach>) A
+		 JOIN TB_SC_IXR_CMRA_MNGM B
+		   ON A.IXR_ID = B.IXR_ID
+	</select>
+
 </mapper>

+ 428 - 133
src/main/webapp/WEB-INF/jsp/egovframework/statistics/info.jsp

@@ -23,7 +23,7 @@
                         </c:if>
                     </div>
                     <div class="list-select">
-                        <select name="infoList" id="infoList">
+                        <select name="infoList" id="infoList" onchange="optionChange(this)">
                             <option>도로목록</option>
                             <c:if test="${atrdNmList.size() > 0}">
                                 <c:forEach var="item" items="${atrdNmList}" varStatus="status">
@@ -67,20 +67,20 @@
                 <div class="condition2 statisticList">
                     <label>검색선택</label>
                     <span>
-                        <select id="infoYear" name="year" class="statis-search-select" style="width: 70px;">
+                        <select id="infoYear" name="year" class="statis-search-select" onchange="selectDateChanger(true, false)" style="width: 70px;">
                             <c:forEach begin="2022" end="${thisYear}" varStatus="status">
                                     <option value="${status.current}" <c:if test="${status.current == thisYear}">selected</c:if>>${status.current}</option>
                         	</c:forEach>
                         </select>
                         <label for="infoYear">년</label>
-                            <select id="infoMonth" name="month" class="statis-search-select" style="width: 50px;">
-                            <c:forEach begin="1" end="12" varStatus="status">
+                            <select id="infoMonth" name="month" class="statis-search-select" style="width: 50px;" onchange="selectDateChanger(false, false)">
+                            <c:forEach begin="1" end="${thisMonth}" varStatus="status">
                                     <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == thisMonth}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
                         </c:forEach>
                         </select>
                         <label for="infoMonth" class="infoMonth">월</label>
                             <select id="infoDate" name="date" class="statis-search-select" style="width: 50px;">
-                            <c:forEach begin="1" end="${lastDate}" varStatus="status">
+                            <c:forEach begin="1" end="${thisDate}" varStatus="status">
                                     <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == (thisDate - 1)}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
                         </c:forEach>
                         </select>
@@ -94,22 +94,22 @@
                     </span>
                     <span>~</span>
                     <span>
-                        <select id="infoYear2" name='year2' class="statis-search-select" style="width: 70px;">
+                        <select id="infoYear2" name='year2' class="statis-search-select" style="width: 70px;" onchange="selectDateChanger(true, true)">
                         <c:forEach begin="2022" end="${thisYear}" varStatus="status">
                                 <option value="${status.current}" <c:if test="${status.current == thisYear}">selected</c:if>>${status.current}</option>
                         </c:forEach>
                         </select>
                         <label for="infoYear2">년</label>
 
-                        <select id="infoMonth2" name='month2' class="statis-search-select" style="width: 50px;">
-                        <c:forEach begin="1" end="12" varStatus="status">
+                        <select id="infoMonth2" name='month2' class="statis-search-select" style="width: 50px;" onchange="selectDateChanger(false, true)">
+                        <c:forEach begin="1" end="${thisMonth}" varStatus="status">
                                 <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == thisMonth}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
                         </c:forEach>
                         </select>
                         <label for="infoMonth2" class="infoMonth2">월</label>
 
                         <select id="infoDate2" name='date2' class="statis-search-select" style="width: 50px;">
-                        <c:forEach begin="1" end="${lastDate}" varStatus="status">
+                        <c:forEach begin="1" end="${thisDate}" varStatus="status">
                                 <option value="<fmt:formatNumber pattern="00" value="${status.current}"/>" <c:if test="${status.current == (thisDate - 1)}">selected</c:if>><fmt:formatNumber pattern="00" value="${status.current}"/></option>
                         </c:forEach>
                         </select>
@@ -165,10 +165,11 @@
                     </thead>
                     <tbody class="content">
 	                    <tr>
-	                        <td colspan="5" class="nodata" style="height:155px; text-align: center;">데이터가 없습니다.</td>
+	                        <td colspan="5" class="nodata" style="height:155px; text-align: center;">표출할 정보가 없습니다.</td>
 	                    </tr>
                     </tbody>
                 </table>
+                <div class="pagination"></div>
             </div>
         </div>
     </article>
@@ -178,6 +179,8 @@
 <script src="/js/xlsx-0.18.12.min.js"></script>
 <script src="/js/fileSaver-1.3.8.min.js"></script>
 <script type="text/javascript">
+    let _statLimit = ${statLimit};
+    let atrdList = '${atrdNmList}';
     let _AtrdSideMarkerArr = [];
     let _FwdLineArr = [];
     let _BwdLineArr = [];
@@ -187,6 +190,19 @@
     let _RoadPolyArr = [];
     let _RoadPolyBackArr = [];
     let _beforeLevel;
+    let _seletData = [];
+    let _type = null;
+
+    const regex = /atrd_id=([\w,]+).*?road_count=(\w+)/g;
+    const idToRoadCountMap = new Map();
+    for (const match of atrdList.matchAll(regex)) {
+        let ids = match[1].slice(0, -1);
+        let roadCount = Number(match[2]);
+        if (ids.split(",").length > 2) {
+            roadCount = (Number(match[2])/ids.split(",").length) * 2;
+        }
+        idToRoadCountMap.set(ids, roadCount);
+    }
 
     const mapContainer = document.getElementById('statistics_map'), // 지도를 표시할 div
         mapOption = {
@@ -204,6 +220,14 @@
         $(el).toggleClass('on');
     }
 
+    function optionChange(el) {
+        const value = $(el).val();
+        totMoveEvent('list-box', 'selected-ixr');
+        if (value) {
+            $('label[for="ixr_'+value+'"]').click();
+            $('label[for="ixr_'+value+'"]').dblclick();
+        }
+    }
 
     /**
      * 좌측 상단 도로명 위, 아래 이동 이벤트
@@ -544,45 +568,49 @@
     }
 
     function excelDown(){
-    	if ($('.nodata')[0]) {
-    		return alert('조회된 소통정보가 없습니다.');
-    	}
-        exportExcel();
-    }
-    const excelHandler = {
-        getExcelFileName : function() {
-            return '소통통계 - ' + $('#sTimeType option:checked').text() + '_' + nowTime() + '.xlsx'; // 테이블이름
-        },
-        getSheetName : function() {
-            return 'Table Sheet'; // 엑셀 하단 시트 이름
-        },
-        getExcelData : function() { // EXCEL로 변환하고자 하는 테이블 선택
-            return document.getElementById('info-road-list');
-        },
-        getWorksheet : function() {
-            return XLSX.utils.table_to_sheet(this.getExcelData());
+        if (!_seletData || !_seletData.list || !_seletData.list.length) {
+            return alert('조회된 교차로 교통량 정보가 없습니다.');
         }
+        exportExcel();
     }
-    function s2ab(s) {
-        var buf = new ArrayBuffer(s.length); //convert s to arrayBuffer
-        var view = new Uint8Array(buf);  //create uint8array as viewer
-        for (var i=0; i<s.length; i++) view[i] = s.charCodeAt(i) & 0xFF; //convert to octet
-        return buf;
-    }
+
     function exportExcel() {
-        var wb = XLSX.utils.book_new(); // step 1. workbook 생성
-        var newWorksheet = excelHandler.getWorksheet(); // step 2. 시트 만들기
-        XLSX.utils.book_append_sheet(wb, newWorksheet, excelHandler.getSheetName()); // step 3. workbook에 새로만든 워크시트에 이름을 주고 붙인다.
-        var wbout = XLSX.write(wb, {bookType:'xlsx',  type: 'binary'}); // step 4. 엑셀 파일 만들기
-        saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), excelHandler.getExcelFileName()); // step 5. 엑셀 파일 내보내기
+        const fileName = '소통통계 - '+ $('option[value="'+_type+'"]').text() + '_' + nowTime() + '.xlsx';
+        const link = document.createElement('a');
+        const blob = base64ToBlob(_seletData.excel, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+        const url = URL.createObjectURL(blob);
+
+        link.href = url;
+        link.download = fileName;  // 다운로드할 파일 이름 설정
+        link.click();
+        URL.revokeObjectURL(url);  // URL 객체 해제
+    }
+
+    function base64ToBlob(base64, mimeType) {
+        const byteCharacters = atob(base64);  // Base64를 디코딩
+        const byteArrays = [];
+
+        for (let offset = 0; offset < byteCharacters.length; offset += 1024) {
+            const slice = byteCharacters.slice(offset, offset + 1024);
+            const byteNumbers = new Array(slice.length);
+
+            for (let i = 0; i < slice.length; i++) {
+                byteNumbers[i] = slice.charCodeAt(i);
+            }
+
+            const byteArray = new Uint8Array(byteNumbers);
+            byteArrays.push(byteArray);
+        }
+
+        return new Blob(byteArrays, { type: mimeType });
     }
     
 	function csvDown(){
-		if ($('.nodata')[0]) {
-    		return alert('조회된 소통정보가 없습니다.');
-    	}
-    	var csv = [];
-    	var row = [];
+        if (!_seletData || !_seletData.list || !_seletData.list.length) {
+            return alert('조회된 교차로 교통량 정보가 없습니다.');
+        }
+    	const csv = [];
+    	const row = [];
     	
     	//1열에는 컬럼명
     	row.push(
@@ -594,33 +622,59 @@
     	);
     	  
     	csv.push(row.join(","));
-    	for (let ii = 0; ii < $('.content').children().length; ii++) {
-    		let tr = $('.content').children().eq(ii);
-   			row = [];
-    		for (let jj = 0; jj < tr.children().length; jj ++) {
-    			row.push(tr.children().eq(jj).text());
-    		}
-    	    csv.push(row.join(","));
-    	}
-    	csv = csv.join("\n");
-    	
-    	const filename = '소통통계 - ' + $('#sTimeType option:checked').text() + '_' + nowTime() + '.csv';
+        for (let obj of _seletData.list) {
+            const statDt = obj.stat_dt;
+
+            let prcnDt = '-';
+            if (statDt) {
+                const yyyy = statDt.substring(0,4);
+                const mm   = statDt.substring(4,6);
+                const dd   = statDt.substring(6,8);
+                const hh   = statDt.substring(8,10);
+                const mi   = statDt.substring(10,12);
+                const ss   = statDt.substring(12,14);
+
+                if (_type === 'sYear') {
+                    prcnDt = yyyy + '년';
+                }
+                else if (_type === 'sMonth') {
+                    prcnDt = yyyy + '년 ' + mm + '월';
+                }
+                else if (_type === 'sDay') {
+                    prcnDt = yyyy + '/' + mm + '/' + dd;
+                }
+                else {
+                    prcnDt = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mi + ':' + ss;
+                }
+            }
+
+            const row = [
+                obj.road_nm + "," +
+                obj.strt_nm + " - " + obj.end_nm + "," +
+                prcnDt + "," +
+                obj.sped + "," +
+                obj.trvl_hh
+            ]
+            csv.push(row);
+        }
+        csv = csv.join("\n");
+
+    	const filename = '소통통계 - ' + $('option[value="'+_type+'"]').text() + '_' + nowTime() + '.csv';
     	
-		var csvFile;
-		var downloadLink;
-		
 		//한글 처리를 해주기 위해 BOM 추가하기
 		const BOM = "\uFEFF";
 		csv = BOM + csv;
 		
-		csvFile = new Blob([csv], { type: "text/csv" });
-		downloadLink = document.querySelector(".csv-a");
+		const csvFile = new Blob([csv], { type: "text/csv" });
+		const downloadLink = document.querySelector(".csv-a");
 		downloadLink.download = filename;
 		downloadLink.href = window.URL.createObjectURL(csvFile);
 		downloadLink.style.display = "none";
 		document.body.appendChild(downloadLink);
 		downloadLink.click();
+        downloadLink.remove();
 	}
+
 	function nowTime() {
 		let now = new Date();
 	    let year = now.getFullYear().toString();
@@ -661,20 +715,16 @@
 
     $('#sTimeType').change(function(){
         let sTimeTypeVal = $('#sTimeType option:selected').val();
-        if(sTimeTypeVal === 'sDay')
-        {
+        if (sTimeTypeVal === 'sDay') {
             inputVisible(['infoMonth', 'infoDate', 'infoMonth2', 'infoDate2'], ['infoHour', 'infoHour2']);
         }
-        else if(sTimeTypeVal === 'sMonth')
-        {
+        else if (sTimeTypeVal === 'sMonth') {
             inputVisible(['infoMonth', 'infoMonth2'], ['infoDate', 'infoDate2', 'infoHour', 'infoHour2']);
         }
-        else if(sTimeTypeVal === 'sYear')
-        {
+        else if (sTimeTypeVal === 'sYear') {
             inputVisible([], ['infoMonth', 'infoMonth2', 'infoDate', 'infoDate2', 'infoHour', 'infoHour2']);
         }
-        else
-        {
+        else {
             inputVisible(['infoMonth', 'infoMonth2', 'infoDate', 'infoDate2', 'infoHour', 'infoHour2'], []);
         }
     });
@@ -684,8 +734,8 @@
 
 	function btnActiveState(stts){
     	const searchBtn = $('.btn-main.search-btn');
-    	const excelBtn  = $('.btn-sub excel-btn');
-    	const csvBtn    = $('.btn-sub csv-btn');
+    	const excelBtn  = $('.btn-sub.excel-btn');
+    	const csvBtn    = $('.btn-sub.csv-btn');
 		searchBtn.prop('disabled', stts);
     	excelBtn.prop('disabled', stts);
     	csvBtn.prop('disabled', stts);
@@ -696,6 +746,7 @@
         const selectList = $('.list-select');
         // let ids = "";
         let ids = [];
+        let checkIds = [];
         if (selectList.css('display') === 'none') {
             const checkArr = $('.selected-atrd input[type="checkbox"]');
             for (let ii = 0; ii < checkArr.length; ii++) {
@@ -703,8 +754,8 @@
                 if (check.next().css('display') !== 'none') {
                     // console.log(check.val());
                     const atrdIds = check.val();
+                    checkIds.push(atrdIds);
                     if (atrdIds.includes(',')) {
-
                         const arr = atrdIds.split(',');
                         ids.push(arr[0], arr[1]);
                     }
@@ -714,37 +765,58 @@
         else {
             const infoList = $('#infoList');
             if (infoList.val().includes(',')) {
-                const atrdIds = infoList.val();
-                // ids += atrdIds + ',';
-                ids.push(atrdIds);
+                const atrdIds = infoList.val().split(',');
+                checkIds.push(infoList.val());
+                ids.push(atrdIds[0], atrdIds[1]);
             }
         }
+        const selectSize = ids.length;
+
+        if (selectSize === 0) return alert('도로목록을 선택해주세요.');// 선택 도로목록 확인
 
-        if (ids.length === 0) return alert('도로목록을 선택해주세요.');// 선택 도로목록 확인
+        const year2El  = $("#infoYear2");
+        const month2El = $("#infoMonth2");
+        const date2El  = $("#infoDate2");
+        const hour2El  = $("#infoHour2");
 
         let year  = $("#infoYear").val();
         let month = $("#infoMonth").val();
         let date  = $("#infoDate").val();
         let hour  = $("#infoHour").val();
-        let from;
 
-        let year2  = $("#infoYear2").val();
-        let month2 = $("#infoMonth2").val();
-        let date2  = $("#infoDate2").val();
-        let hour2  = $("#infoHour2").val();
-        let to;
+        let year2  = year2El.val();
+        let month2 = month2El.val();
+        let date2  = date2El.val();
+        let hour2  = hour2El.val();
+
+        let from, to, fromDate, toDate, fromDateWord;
 
         if (type === 'sTime') {
             from = year+month+date+hour + '0000';
-            to = year2+month2+date2+hour2 + '0000';
+            to = year2+month2+date2+hour2 + '5959';
+            toDate = new Date(year2 + "-" +month2 + "-" + date2 + " " + hour2 + ":59:59");
+            fromDateWord = year + "-" +month + "-" + date + " " + hour + ":00:00";
+            fromDate = new Date(fromDateWord);
+            fromDate.setDate(new Date(fromDate).getDate() - 1);
+            fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
         }
         else if (type === 'sDay') {
             from = year+month+date + '000000';
             to = year2+month2+date2 + '235959';
+            toDate = new Date(year2 + "-" +month2 + "-" + date2 + " 23:59:59");
+            fromDateWord = year + "-" +month + "-" + date + " 00:00:00";
+            fromDate = new Date(fromDateWord);
+            fromDate.setDate(new Date(fromDate).getDate() - 1);
+            fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
         }
         else if (type === 'sMonth') {
             from = year+month + '01000000';
             to = year2+month2 + '31235959';
+            toDate = new Date(year2 + "-" +month2 + "-31 23:59:59");
+            fromDateWord = year + "-" +month + "-01 00:00:00";
+            fromDate = new Date(fromDateWord);
+            fromDate.setDate(new Date(fromDate).getDate() - 1);
+            fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
         }
         else if (type === 'sYear') {
             from = year + '0101000000';
@@ -754,8 +826,29 @@
         if (from > to) {
 			return alert("검색 시작 일이 종료 일보다 큽니다.");
 		}
-        
-        // ids = ids.substr(0, ids.lastIndexOf(","));
+
+        const limitDate = getLimitDate(selectSize, type, fromDate, checkIds);
+
+        if (type !== 'sYear' && toDate > limitDate) {
+            const yyyy = limitDate.getFullYear();
+            const MM = String(limitDate.getMonth() + 1).padStart(2, '0');
+            const dd = String(limitDate.getDate()).padStart(2, '0');
+            const HH = String(limitDate.getHours()).padStart(2, '0');
+            const mm = String(limitDate.getMinutes()).padStart(2, '0');
+            const ss = String(limitDate.getSeconds()).padStart(2, '0');
+            const formattedDate = `\${yyyy}-\${MM}-\${dd} \${HH}:\${mm}:\${ss}`;
+            if (confirm("현재 조회 기간이 데이터 조회 최대 기간을 초과하였습니다. \n조회 기간을 변경하여 데이터를 조회" +
+                "하시겠습니까?\n검색 기간 : " + fromDateWord + " ~ " + formattedDate)) {
+                to = yyyy + MM + dd + HH + mm + ss;
+                year2El.val(yyyy).change();
+                month2El.val(MM).change();
+                date2El.val(dd);
+                hour2El.val(HH);
+            }
+            else {
+                return;
+            }
+        }
 
         const params = {
             atrdIds : ids,
@@ -765,66 +858,23 @@
         }
 
         const tbody = $('.content');
+        const pagination = $('.pagination');
 
+        pagination.empty();
         tbody.empty();
+        $('#limit').val(0);
         $.ajax({
             type:'POST',
             url: "/statistics/getAtrdStat.do",
             traditional : true,
             data: params,
             success: function(data){
-
-                const cnt = data.length.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
+                _seletData = data;
+                _type = type;
+                const cnt = _seletData.list.length.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
                 alert("데이터 " + cnt + "건이 조회되었습니다.");
                 $('#limit').val(cnt);
-                let vertexData = data;
-
-                let str = '';
-                const table = $('.info-road-list');
-                if (data.length === 0) {
-                    table.css('height', '100%');
-                    tbody.append($('<tr><td colspan="5" class="nodata" style="height:155px">데이터가 없습니다.</td></tr>'));
-                }
-                else {
-                    table.css('height', 'auto');
-                    for (let ii = 0; ii < vertexData.length; ii++) {
-                        const obj = vertexData[ii];
-                        const statDt = obj.stat_dt;
-
-                        let prcnDt = '-';
-                        if (statDt) {
-                            const yyyy = statDt.substring(0,4);
-                            const mm   = statDt.substring(4,6);
-                            const dd   = statDt.substring(6,8);
-                            const hh   = statDt.substring(8,10);
-                            const mi   = statDt.substring(10,12);
-                            const ss   = statDt.substring(12,14);
-
-                            if (type === 'sYear') {
-                                prcnDt = yyyy + '년';
-                            }
-                            else if (type === 'sMonth') {
-                                prcnDt = yyyy + '년 ' + mm + '월';
-                            }
-                            else if (type === 'sDay') {
-                                prcnDt = yyyy + '/' + mm + '/' + dd;
-                            }
-                            else {
-                                prcnDt = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mi + ':' + ss;
-                            }
-                        }
-
-                        str += '<tr>';
-                        str += '<td>'+obj.road_nm+'</td>';
-                        str += '<td>'+obj.strt_nm+' - '+obj.end_nm+'</td>';
-                        str += '<td>'+prcnDt+'</td>';
-                        str += '<td>'+obj.sped+'</td>';
-                        str += '<td>'+obj.trvl_hh+'</td>';
-                        str += '</tr>';
-                    }
-                    tbody.html(str);
-
-                }
+                // let vertexData = data;
             },
             beforeSend : function(){
                 $('#loading').show();
@@ -833,9 +883,254 @@
             complete : function(){
                 $('#loading').hide();
                 btnActiveState(false);
+                drawTable(1);
+                // loadPage(1);
             }
         });
 
     }
+    function appendDataToTable(startIndex, endIndex) {
+        let str = '';
+        const tbody = $('.content');
+        const table = $('.info-road-list');
+        if (_seletData.list.length === 0) {
+            table.css('height', '100%');
+            tbody.append($('<tr><td colspan="5" class="nodata" style="height:155px">표출할 정보가 없습니다.</td></tr>'));
+        }
+        else {
+            table.css('height', 'auto');
+            const paginatedData = _seletData.list.slice(startIndex, endIndex);
+
+            paginatedData.forEach(item => {
+                const row = document.createElement('tr');
+                const statDt = item.stat_dt;
+                const yyyy = statDt.substring(0,4);
+                const mm   = statDt.substring(4,6);
+                const dd   = statDt.substring(6,8);
+                const hh   = statDt.substring(8,10);
+                const mi   = statDt.substring(10,12);
+                const ss   = statDt.substring(12,14);
+                let prcnDt;
+                if (_type === 'sYear') {
+                    prcnDt = yyyy + '년';
+                }
+                else if (_type === 'sMonth') {
+                    prcnDt = yyyy + '년 ' + mm + '월';
+                }
+                else if (_type === 'sDay') {
+                    prcnDt = yyyy + '/' + mm + '/' + dd;
+                }
+                else {
+                    prcnDt = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mi + ':' + ss;
+                }
+                row.innerHTML = `<td>\${item.road_nm}</td>
+                                 <td>\${item.strt_nm} - \${item.end_nm}</td>
+                                 <td>\${prcnDt}</td>
+                                 <td>\${item.sped} km/h</td>
+                                 <td>\${item.trvl_hh}</td>`;
+                tbody.append(row);
+            });
+        }
+    }
+
+    function loadPage(page) {
+        const rowsPerPage = 10;
+
+        const startIndex = (page - 1) * rowsPerPage;
+        const endIndex = page * rowsPerPage;
+        appendDataToTable(startIndex, endIndex);
+    }
+    //
+    // let currentPage = 1;
+    // const tableContainer = $('.road_info-box.sub-road_info');
+    // tableContainer.on('scroll', ()=>{
+    //     if ($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight - 5) {
+    //         currentPage++;
+    //         loadPage(currentPage);
+    //     }
+    // })
+
+
+
+    function getLimitDate(selectSize, type, startDate, ids) {
+        let tot = 0;
+        for(let id of ids) {
+            tot += idToRoadCountMap.get(id) || 0;
+        }
+
+        const limitRate = Math.floor(_statLimit / tot);
+        const date = new Date(startDate);
+        let setMethod = "Hours";
+        if (type === 'sDay') {
+            setMethod = "Date";
+        }
+        else if (type === 'sMonth') {
+            setMethod = "Month";
+        }
+
+        return new Date(date["set" + setMethod](date["get" + setMethod]() + limitRate));
+    }
+
+    function drawTable(idx) {
+        let str = '';
+        const tbody = $('.content');
+        const table = $('.info-road-list');
+        const pagination = $('.pagination');
+        pagination.hide();
+        const rowsPerPage = 14;
+        const pagesPerGroup = 10;
+
+        if (!_seletData || !_seletData.list || _seletData.list.length === 0) {
+            table.css('height', '100%');
+            str = $('<tr><td colspan="5" class="nodata" style="height:155px">표출할 정보가 없습니다.</td></tr>');
+        }
+        else {
+            table.css('height', 'auto');
+            let start = (idx - 1) * rowsPerPage;
+            let end = start + rowsPerPage;
+            let max = _seletData.list.length;
+
+            if (max < end) end = max;
+            let pageStr = "";
+            if (max > rowsPerPage) {
+                const totalPage = Math.ceil(max / rowsPerPage);
+                const currentGroup = Math.ceil(idx / pagesPerGroup);
+                const startPage = (currentGroup - 1) * pagesPerGroup + 1;
+                const endPage = Math.min(startPage + pagesPerGroup - 1, totalPage);
+                pagination.css('display', 'flex');
+                if (startPage > 10) {
+                    pageStr += "<div onclick='drawTable("+(startPage - 1)+")'>[PREV]</div>";
+                }
+
+                for (let ii = startPage; ii <= endPage; ii++) {
+                    let className = "";
+                    if (ii === idx) {
+                        className = "on";
+                    }
+                    pageStr += "<div class='"+className+"' onclick='drawTable("+ ii +")'>["+ii+"]</div>";
+                }
+
+                if (totalPage > endPage) {
+                    pageStr += "<div onclick='drawTable("+(endPage + 1)+")'>[NEXT]</div>";
+                }
+                pagination.html(pageStr);
+            }
+
+            for (let ii = start; ii < end; ii++) {
+                const obj = _seletData.list[ii];
+                const statDt = obj.stat_dt;
+
+                let prcnDt = '-';
+                if (statDt) {
+                    const yyyy = statDt.substring(0,4);
+                    const mm   = statDt.substring(4,6);
+                    const dd   = statDt.substring(6,8);
+                    const hh   = statDt.substring(8,10);
+                    const mi   = statDt.substring(10,12);
+                    const ss   = statDt.substring(12,14);
+
+                    if (_type === 'sYear') {
+                        prcnDt = yyyy + '년';
+                    }
+                    else if (_type === 'sMonth') {
+                        prcnDt = yyyy + '년 ' + mm + '월';
+                    }
+                    else if (_type === 'sDay') {
+                        prcnDt = yyyy + '/' + mm + '/' + dd;
+                    }
+                    else {
+                        prcnDt = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mi + ':' + ss;
+                    }
+                }
+                str += '<tr>';
+                str += '<td>'+obj.road_nm+'</td>';
+                str += '<td>'+obj.strt_nm+' - '+obj.end_nm+'</td>';
+                str += '<td>'+prcnDt+'</td>';
+                str += '<td>'+obj.sped+'</td>';
+                str += '<td>'+obj.trvl_hh+'</td>';
+                str += '</tr>';
+            }
+        }
+        tbody.html(str);
+    }
+
+    /**
+     * 년도 날짜에 따른 월, 일 범위값 설정
+     * @param(boolean) isSetMonth 일만 변경할건지
+     */
+    function selectDateChanger(isSetMonth, isTo) {
+        const year          = $("#infoYear");
+        const month         = $("#infoMonth");
+        const day           = $("#infoDate");
+        const toYear        = $("#infoYear2");
+        const toMonth       = $("#infoMonth2");
+        const toDay         = $("#infoDate2");
+
+        let now 	 		= new Date();
+        let nowDay   		= now.getDate();
+        let nowYear  		= now.getFullYear();
+        let nowMonth 		= now.getMonth() + 1;
+        let dayVal   		= day.val();
+        let monthVal 		= month.val();
+        let yearVal         = year.val();
+        let monthEl         = month;
+        let dayEl           = day;
+        if (isTo) {
+            dayVal  = toDay.val();
+            monthVal = toMonth.val();
+            yearVal = toYear.val();
+            monthEl = toMonth;
+            dayEl  = toDay;
+        }
+
+        let lastDate        = new Date(yearVal, monthVal, 0);
+        let minDay 	    	= lastDate.getDate();
+        let minMonth        = 12;
+
+        if (nowYear.toString() === yearVal) {
+            minMonth = nowMonth;
+            if (nowMonth === Number(monthVal)) minDay = nowDay;
+        }
+        if (isSetMonth) {
+            setSelectOption(minMonth, monthVal, monthEl);
+        }
+        setSelectOption(minDay, dayVal, dayEl);
+    }
+
+    /**
+     * 날짜 Select Box option Setting
+     * @param min 마지막 값
+     * @param value 현재 값
+     * @param target 요소
+     * @param word 날짜 글자
+     */
+    function setSelectOption(min, value, target) {
+        let option = getOption(min);
+        target.html(option);
+        if (min < Number(value)) {
+            target.val('01');
+        }
+        else {
+            target.val(value);
+        }
+    }
+
+    /**
+     * 날짜 Select option for문
+     * @param rate 종료 값
+     * @param word 날짜 글짜
+     * @returns {string}
+     */
+    function getOption(rate) {
+        let option = ''
+        for ( let ii = 1; ii <= rate; ii++) {
+            let value = ii;
+            if (ii < 10) {
+                value = ('0'+ii);
+            }
+            option += '<option value="'+value+'">'+value + '</option>';
+        }
+        return option;
+    }
 
 </script>

+ 177 - 99
src/main/webapp/WEB-INF/jsp/egovframework/statistics/trafficStats.jsp

@@ -3,6 +3,7 @@
 <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
 <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
 <%@ page import="java.text.NumberFormat" %>
+<%@ page import="java.util.Map" %>
 
 <section class="main-container">
     <!-- 좌측목록 지도 영역 -->
@@ -210,6 +211,13 @@
 <%--<script src="/js/xlsx-0.18.12.min.js"></script>--%>
 <script src="/js/fileSaver-1.3.8.min.js"></script>
 <script type="text/javascript">
+    let _statLimit = ${statLimit};
+    let listInfo = '${list}';
+
+    const matches = [...listInfo.matchAll(/IXR_ID=(\w+).*?CMRA_COUNT=(\d+)/g)];
+
+    // Map 객체 생성
+    const idToCameraCount = new Map(matches.map(match => [match[1], Number(match[2])]));
     let _ixrMap = new Map();
     let _seletData = [];
     let _Param = null;
@@ -410,8 +418,6 @@
             this.isClick = true;
             _ixrMap.set('click', this);
         }
-
-
     }
 
     /**
@@ -452,34 +458,14 @@
     }
 
     function excelDown(){
-    	if ($('.nodata')[0]) {
-    		return alert('조회된 소통정보가 없습니다.');
+    	if (!_seletData || !_seletData.list || !_seletData.list.length) {
+    		return alert('조회된 교차로 교통량 정보가 없습니다.');
     	}
         exportExcel();
     }
-    const excelHandler = {
-        getExcelFileName : function() {
-            return '교차로 교통량 통계 - ' + $('#sTimeType option:checked').text() + '_' + nowTime() + '.xlsx'; // 테이블이름
-        },
-        getSheetName : function() {
-            return 'Table Sheet'; // 엑셀 하단 시트 이름
-        },
-        getExcelData : function() { // EXCEL로 변환하고자 하는 테이블 선택
-            return getExcelTable();
-            // return document.getElementById('info-road-list');
-        },
-        getWorksheet : function() {
-            return XLSX.utils.table_to_sheet(this.getExcelData());
-        }
-    }
-    function s2ab(s) {
-        var buf = new ArrayBuffer(s.length); //convert s to arrayBuffer
-        var view = new Uint8Array(buf);  //create uint8array as viewer
-        for (var i=0; i<s.length; i++) view[i] = s.charCodeAt(i) & 0xFF; //convert to octet
-        return buf;
-    }
+
     function exportExcel() {
-        const fileName = '교차로 교통량 통계 - ' + $('#sTimeType option:checked').text() + '_' + nowTime() + '.xlsx';
+        const fileName = '교차로 교통량 통계 - ' + $('option[value="'+_type+'"]').text() + '_' + nowTime() + '.xlsx';
         const link = document.createElement('a');
         const blob = base64ToBlob(_seletData.excel, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
         const url = URL.createObjectURL(blob);
@@ -488,33 +474,6 @@
         link.download = fileName;  // 다운로드할 파일 이름 설정
         link.click();
         URL.revokeObjectURL(url);  // URL 객체 해제
-        // getExcelTable(fileName);
-        // $.ajax({
-        //     url: "/common/downExcel.do",
-        //     method : "POST",
-        //     traditional: true,
-        //     data : _Param,
-        //     success : (res)=> {
-        //         // alert(res.message);
-        //         if (res.success === 'S') {
-        //             const link = document.createElement('a');
-        //             const blob = base64ToBlob(res.base64, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
-        //             const url = URL.createObjectURL(blob);
-        //
-        //             link.href = url;
-        //             link.download = fileName;  // 다운로드할 파일 이름 설정
-        //             link.click();
-        //             URL.revokeObjectURL(url);  // URL 객체 해제
-        //         }
-        //         else {
-        //             alert(res.message);
-        //         }
-        //     },
-        //     error : (error)=> {
-        //         alert(error);
-        //     }
-        // })
-
     }
 
     function base64ToBlob(base64, mimeType) {
@@ -537,9 +496,9 @@
     }
     
 	function csvDown(){
-		if ($('.nodata')[0]) {
-    		return alert('조회된 소통정보가 없습니다.');
-    	}
+        if (!_seletData || !_seletData.list || !_seletData.list.length) {
+            return alert('조회된 교차로 교통량 정보가 없습니다.');
+        }
     	//1열에는 컬럼명
         let csv = [
             [ "ID," +
@@ -563,7 +522,7 @@
             ]
        ];
 
-        for (let obj of _seletData) {
+        for (let obj of _seletData.list) {
             let tot_tfvl        = numberComma(obj.tot_tfvl);
             let smal_go_tfvl    = numberComma(obj.smal_go_tfvl);
             let mddl_go_tfvl    = numberComma(obj.mddl_go_tfvl);
@@ -619,7 +578,7 @@
         }
     	csv = csv.join("\n");
     	
-    	const filename = '교차로 교통량 통계 - '+ $('#sTimeType option:checked').text() + '_' + nowTime() + '.csv';
+    	const filename = '교차로 교통량 통계 - '+ $('option[value="'+_type+'"]').text() + '_' + nowTime() + '.csv';
     	
 		var csvFile;
 		var downloadLink;
@@ -693,8 +652,8 @@
 
 	function btnActiveState(stts){
     	const searchBtn = $('.btn-main.search-btn');
-    	const excelBtn  = $('.btn-sub excel-btn');
-    	const csvBtn    = $('.btn-sub csv-btn');
+    	const excelBtn  = $('.btn-sub.excel-btn');
+    	const csvBtn    = $('.btn-sub.csv-btn');
 		searchBtn.prop('disabled', stts);
     	excelBtn.prop('disabled', stts);
     	csvBtn.prop('disabled', stts);
@@ -722,71 +681,82 @@
                 ids.push(infoList.val());
             }
         }
-
-        if (ids.length === 0) return alert('교차로 목록을 선택해주세요.');// 선택 도로목록 확인
+        const selectSize = ids.length;
+        if (selectSize === 0) return alert('교차로 목록을 선택해주세요.');// 선택 도로목록 확인
 
         let year  = $("#infoYear").val();
         let month = $("#infoMonth").val();
         let date  = $("#infoDate").val();
         let hour  = $("#infoHour").val();
         let from;
-
-        let year2  = $("#infoYear2").val();
-        let month2 = $("#infoMonth2").val();
-        let date2  = $("#infoDate2").val();
-        let hour2  = $("#infoHour2").val();
+        const year2El  = $("#infoYear2");
+        const month2El = $("#infoMonth2");
+        const date2El  = $("#infoDate2");
+        const hour2El  = $("#infoHour2");
+
+        let year2  = year2El.val();
+        let month2 = month2El.val();
+        let date2  = date2El.val();
+        let hour2  = hour2El.val();
         let to;
-        let max;
         let fromDate;
-        let word;
-
+        let toDate;
+        let fromDateWord;
         if (type === '15M' || type === 'HH') {
             from = year+month+date+hour + '0000';
-            to = year2+month2+date2+hour2 + '0000';
-            let toDate = new Date(year2 + "-" +month2 + "-" + date2 + " 00:00:00");
-            fromDate = new Date(year + "-" +month + "-" + date + " 00:00:00");
-            max = new Date(toDate.setMonth(toDate.getMonth() - 1));
-            word = "1개월";
-            if (type === 'HH') {
-                max = new Date(toDate.setMonth(toDate.getMonth() - 2));
-                word = "2개월";
-            }
+            to = year2+month2+date2+hour2 + '5959';
+            toDate = new Date(year2 + "-" +month2 + "-" + date2 + " " + hour2 + ":59:59");
+            fromDateWord = year + "-" +month + "-" + date + " " + hour + ":00:00";
+            fromDate = new Date(fromDateWord);
+            fromDate.setDate(new Date(fromDate).getDate() - 1);
+            fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
         }
         else if (type === 'DD') {
             from = year+month+date + '000000';
             to = year2+month2+date2 + '235959';
-            let toDate = new Date(year2 + "-" +month2 + "-" + date2 + " 00:00:00");
-            fromDate = new Date(year + "-" +month + "-" + date + " 00:00:00");
-            max = new Date(toDate.setMonth(toDate.getMonth() - 6));
-            word = "6개월";
+            toDate = new Date(year2 + "-" +month2 + "-" + date2 + " 23:59:59");
+            fromDateWord = year + "-" +month + "-" + date + " 00:00:00";
+            fromDate = new Date(fromDateWord);
+            fromDate.setDate(new Date(fromDate).getDate() - 1);
+            fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
         }
         else if (type === 'MN') {
             from = year+month + '01000000';
             to = year2+month2 + '31235959';
-            let toDate = new Date(year2 + "-" +month2 + "-01 00:00:00");
-            fromDate = new Date(year + "-" +month + "-01 00:00:00");
-            max = new Date(toDate.setFullYear(toDate.getFullYear() - 3));
-            word = "3년";
+            toDate = new Date(year2 + "-" +month2 + "-31 23:59:59");
+            fromDateWord = year + "-" +month + "-01 00:00:00";
+            fromDate = new Date(fromDateWord);
+            fromDate.setDate(new Date(fromDate).getDate() - 1);
+            fromDate.setSeconds(new Date(fromDate).getSeconds() - 1);
         }
 
         if (from > to) {
 			return alert("검색 시작 일이 종료 일보다 큽니다.");
 		}
 
-        if (fromDate < max) {
-            return alert("검색 기간을 " + word + " 이내로 입력해주세요.");
+        const limitDate = getLimitDate(selectSize, type, fromDate, ids);
+
+        if (toDate > limitDate) {
+            const yyyy = limitDate.getFullYear();
+            const MM = String(limitDate.getMonth() + 1).padStart(2, '0');
+            const dd = String(limitDate.getDate()).padStart(2, '0');
+            const HH = String(limitDate.getHours()).padStart(2, '0');
+            const mm = String(limitDate.getMinutes()).padStart(2, '0');
+            const ss = String(limitDate.getSeconds()).padStart(2, '0');
+            const formattedDate = `\${yyyy}-\${MM}-\${dd} \${HH}:\${mm}:\${ss}`;
+            if (confirm("현재 조회 기간이 데이터 조회 최대 기간을 초과하였습니다.\n조회 기간을 변경하여 데이터를 조회하시겠습니까?" +
+                "\n검색 기간 : " + fromDateWord + " ~ " + formattedDate)) {
+                to = yyyy + MM + dd + HH + mm + ss;
+                year2El.val(yyyy).change();
+                month2El.val(MM).change();
+                date2El.val(dd);
+                hour2El.val(HH);
+            }
+            else {
+                return;
+            }
         }
 
-
-        // let excelEle = '';
-        // excelEle += '<label for="statisticDay" id="statisticDay"></label>';
-        // excelEle += '<input type="button" value="통계조회" class="search-btn" onclick="selectData()">';
-        // excelEle += '<input type="button" value="엑셀" class="excel-btn" onclick="infoWriteExcel()">';
-        // $('#search-area').empty().append(excelEle);
-        // if (ids && ids.includes(",")) {
-        //     ids = ids.substr(0, ids.lastIndexOf(","));
-        // }
-
         const params = {
             ixrIds : ids,
             from : from,
@@ -826,9 +796,117 @@
                 $('#loading').hide();
                 btnActiveState(false);
                 drawTable(1);
+                // loadPage(1);
             }
         });
+    }
+
+    // let currentPage = 1;
+    // const tableContainer = $('.road_info-box.sub-road_info');
+    // tableContainer.on('scroll', function(){
+    //     if ($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight - 5) {
+    //         currentPage++;
+    //         loadPage(currentPage);
+    //     }
+    // })
+
+    function appendDataToTable(startIndex, endIndex) {
+        const tbody = $('.content');
+        const table = $('.info-road-list');
+        if (_seletData.list.length === 0) {
+            table.css('height', '100%');
+            tbody.append($('<tr><td colspan="18" class="nodata" style="height:141px">표출할 정보가 없습니다.</td></tr>'));
+        }
+        else {
+            table.css('height', 'auto');
+            const paginatedData = _seletData.list.slice(startIndex, endIndex);
+
+            paginatedData.forEach(item => {
+                const row = document.createElement('tr');
+                const obj = item;
+                const statDt = obj.clct_dt;
+                let yyyy = statDt.substring(0, 4);
+                let mm = statDt.substring(4, 6);
+                let dd = statDt.substring(6, 8);
+                let hh = statDt.substring(8, 10);
+                let mi = statDt.substring(10, 12);
+                let ss = statDt.substring(12, 14);
+
+                let clctDt = '-';
+                if (_type === 'MN') {
+                    clctDt = yyyy + '년 ' + mm + '월';
+                } else if (_type === 'DD') {
+                    clctDt = yyyy + '/' + mm + '/' + dd;
+                } else {
+                    clctDt = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mi + ':' + ss;
+                }
+
+                let tot_tfvl = numberComma(obj.tot_tfvl);
+                let smal_go_tfvl = numberComma(obj.smal_go_tfvl);
+                let mddl_go_tfvl = numberComma(obj.mddl_go_tfvl);
+                let lrg_go_tfvl = numberComma(obj.lrg_go_tfvl);
+                let smal_left_tfvl = numberComma(obj.smal_left_tfvl);
+                let mddl_left_tfvl = numberComma(obj.mddl_left_tfvl);
+                let lrg_left_tfvl = numberComma(obj.lrg_left_tfvl);
+                let smal_right_tfvl = numberComma(obj.smal_rght_tfvl);
+                let mddl_right_tfvl = numberComma(obj.mddl_rght_tfvl);
+                let lrg_right_tfvl = numberComma(obj.lrg_rght_tfvl);
+                let smal_uturn_tfvl = numberComma(obj.smal_uturn_tfvl);
+                let mddl_uturn_tfvl = numberComma(obj.mddl_uturn_tfvl);
+                let lrg_uturn_tfvl = numberComma(obj.lrg_uturn_tfvl);
+
+                row.innerHTML = `<td>\${obj.ixr_id}</td>
+                                 <td>\${obj.ixr_nm}</td>
+                                 <td>\${obj.cmra_nm}</td>
+                                 <td>\${obj.drct_dvsn_cd_r}</td>
+                                 <td>\${clctDt}</td>
+                                 <td>\${smal_left_tfvl}</td>
+                                 <td>\${mddl_left_tfvl}</td>
+                                 <td>\${lrg_left_tfvl}</td>
+                                 <td>\${smal_go_tfvl}</td>
+                                 <td>\${mddl_go_tfvl}</td>
+                                 <td>\${lrg_go_tfvl}</td>
+                                 <td>\${smal_right_tfvl}</td>
+                                 <td>\${mddl_right_tfvl}</td>
+                                 <td>\${lrg_right_tfvl}</td>
+                                 <td>\${smal_uturn_tfvl}</td>
+                                 <td>\${mddl_uturn_tfvl}</td>
+                                 <td>\${lrg_uturn_tfvl}</td>
+                                 <td>\${tot_tfvl}</td>`
+                tbody.append(row);
+            });
+        }
+    }
+
+    function loadPage(page) {
+        const rowsPerPage = 10;
+
+        const startIndex = (page - 1) * rowsPerPage;
+        const endIndex = page * rowsPerPage;
+        appendDataToTable(startIndex, endIndex);
+    }
+
+
+
+    function getLimitDate(selectSize, type, startDate, ids) {
+        const limits = (type === '15M') ? 4 : 1;
+
+        let tot = 0;
+        for(let id of ids) {
+            tot += idToCameraCount.get(id) || 0;
+        }
+
+        const limitRate = Math.floor(_statLimit / (tot * limits));
+        const date = new Date(startDate);
+        let setMethod = "Hours";
+        if (type === 'DD') {
+            setMethod = "Date";
+        }
+        else if (type === 'MN') {
+            setMethod = "Month";
+        }
 
+        return new Date(date["set" + setMethod](date["get" + setMethod]() + limitRate));
     }
 
     function drawTable(idx) {

+ 3 - 0
src/main/webapp/common/css/contents.css

@@ -539,6 +539,9 @@ button.sitemap:focus:not(:focus-visible) {
     #wrap.statistics {
         min-height: 680px;
     }
+    .pagination > div {
+        font-size: 12px;
+    }
 }
 @media (max-width: 680px) {
     .mapArea.statistics {