여러개의 테이블을 조인하는 쿼리가 있습니다.
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초라도 줄이고자 한 쿼리에 넣으
면 되지 않을까 하여 문의 드렸습니다. 혹시나 더 필요한 정보가 있다면 말씀해주세요. 감사합니다.
비회원일 경우
select count(*) from order where settle='결제완료' and orderday<='{$row['orderday']}' and hp='{$row['hp']}'
회원 일 경우
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 를 묶어두고 미리 페이징을 하고 그 다음에 조인을 하란 말씀이신가요?
예를 들어
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 중에서 아무거나 하나만 표현됩니다.
이게 과연 의미가 있는 구문인지?
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 ;