여러개의 테이블을 조인하는 쿼리가 있습니다.
1 2 3 4 5 6 7 8 9 10 11 | select A.orderno, A.id, A. name , A.hp, B.addr, C.goodname from order A left join member B on A.id=B.id left join goods C on A.goods_num = C.num where settle= '결제완료' and (orderday between '1546527600' and '1641394799' ) group by A.orderno order by A. no desc limit 0,20 |
이런식으로 결제 목록을 구하는 쿼리 입니다.
아이디값을 이용해 그간 주문횟수를 구하고 싶은데요.
예를 들어 id가 guest 일 경우는 핸드폰번호로 일반 회원일 경우는 아이디로 구하려고 합니다.
저 쿼리안에 넣을 수가 있을까요?
위에 언급했던 order 테이블에 대한 정보입니다. 현재 50만건 정도 되는 주문 테이블입니다.
B.member는 회원의 주소나 레벨을 가져오기 위하여 쓰는 테이블입니다. row수는 약 30만건 됩니다.
C.goods 는 주문한 상품에 대한 정보를 가져오는 테이블입니다. row수는 약 10만건 됩니다.
주문 리스트는 위에 적힌데로 아이디와 상품명과 핸드폰 등등의 정보를 뿌려오구요.
주문한 상품이 2개이상일 경우를 대비해 orderno를 group by 한 상태입니다.
그냥 저 상태로 쿼리를 날리게 되면 0.5초 정도가 걸리는데요.
while안에 아이디별로 주문한 카운트를 구하다 보니 총 0.8초 정도 걸립니다. 그래서 0.3초라도 줄이고자 한 쿼리에 넣으
면 되지 않을까 하여 문의 드렸습니다. 혹시나 더 필요한 정보가 있다면 말씀해주세요. 감사합니다.
비회원일 경우
1 | select count (*) from order where settle= '결제완료' and orderday<= '{$row[' orderday ']}' and hp= '{$row[' hp ']}' |
회원 일 경우
1 | select count (*) from order where settle= '결제완료' and orderday<= '{$row[' orderday ']}' and id= '{$row[' id ']}' |
인덱스는
order 테이블
pk = no
key = orderno , settle, orderday 이렇게 되어있습니다.
총 시간이 0.8초가 아니라 저 쿼리만 0.8초 입니다. 다른 쿼리까지 합하면 페이지 로딩 완료시까지 2.4초 정도 걸립니다.
다른 쿼리는 페이징을 위하여 총 카운트 내는 쿼리(0.6)와 총 합계금액을 위하여 총주문금액을 구하는 쿼리(0.6)
그리고 담당자 구하는 쿼리(0.4)가 있습니다. 참고로 1년치 정도 검색은 1.2초 정도 걸립니다. 3년 치 검색 시간을 말씀드린겁니다. 감사합니다.
네. 인덱스가 필요합니다.
다만. 고민할 사항이 많네요.
테이블 구조로 보면 (orderno 에 대한 중복 가능성)
단순 COUNT(*) 가 맞는가? 고민해야 합니다.
주문은 1번 했는데 상품이 2개라서 2건으로 카운트 될 수 있습니다.
대충 COUNT(*) 로 할지?
정확하게 COUNT(DISTINCT orderno) 로 할지 정해야 합니다.
보통 order 테이블은 orderno 가 키가 되고
order_goods 테이블을 별도로 두는게 좋습니다.
이러면 쿼리가 좀더 간결해 질 수 있습니다.
애초에 테이블 설계에 문제가 있네요.
그리고 메인 쿼리는 조인 후에 페이징 하지 말고
페이징 후에 조인하는게 좋을 듯 하네요.
select 를 묶어두고 미리 페이징을 하고 그 다음에 조인을 하란 말씀이신가요?
예를 들어
1 2 3 4 5 6 7 | select A.orderno, A.id, A. name , A.hp, B.addr, C.goodname from ( select orderno,id, name ,hp from order where settle= '결제완료' and (orderday between '1546527600' and '1641394799' ) group by A.orderno order by A. no desc limit 0,20 ) A left join member B on A.id=B.id left join goods C on A.goods_num = C.num |
이런식이면 더 빨라질까요?
네. 조금은 빨라질 가능성은 있겠지요.
다만. 구조적인 문제 때문에 한계가 있습니다.
테이블 설계가 정규화 되지 않아 불필요한 group by 가 발생되게 되네요.
제대로된 설계였다면? group by 가 필요 없었겠죠.
그리고 goods 와 정확한 조인도 되지 않습니다.
group by 구문이 비표준입니다.
orderno 로 group by 하고 있으므로 원래는 goods_num 은 사용할 수 없습니다.
MySQL 에서만 사용 가능한 구문인데
여러 goods_num 중에서 아무거나 하나만 표현됩니다.
이렇게 아무거나 하나만 표현하는게 맞는건지?
고민이 많아집니다.
ORDER BY no 도 마찬가지입니다.
여러 no 중에서 아무거나 하나만 표현됩니다.
이게 과연 의미가 있는 구문인지?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | SELECT a.orderno , a.id , a. name , a.hp , a.orderday , b.addr , CASE WHEN a.id = 'guest' THEN ( SELECT COUNT ( DISTINCT orderno) FROM order WHERE settle = '결재완료' AND orderday <= a.orderday AND hp = a.hp) ELSE ( SELECT COUNT ( DISTINCT orderno) FROM order WHERE settle = '결재완료' AND orderday <= a.orderday AND id = a.id) END cnt FROM ( SELECT orderno , id , name , hp , orderday , MIN (goods_num) goods_num FROM order WHERE settle = '결제완료' AND orderday BETWEEN '1641049200' AND '1641394799' GROUP BY orderno, id, name , hp, orderday ORDER BY orderno DESC LIMIT 0, 20 ) a LEFT JOIN member b ON a.id = b.id LEFT JOIN goods c ON a.goods_num = c.num ; |