1. MS SQL 입니다.
2. 3개의 테이블 을 조인하려 합니다. 일정기간을 정하여 원하는 값을 집계하고 싶습니다. 데이터 량이 많아서 한달로 끊어서 집계하려고 합니다. 본 사항을 매월 1일에 자동으로 집계하고자 합니다. 예를 들면, 4/1 에 3월 집계를 자동으로 해 볼 수 있는 방법을 구합니다.
3. 특이사항:
alarm 테이블에서, 21번과 210번을 카운트해야 하는데, 210번의 경우 NDC 값이 없습니다.
alarm 내역에서 21번일 경우, 메세지 내용에 Qty:가 나오는데, 10개 이하는 카운트에서 뺍니다.
테이블 1. fac
Fac_id | Fac_name |
00014 | HOME 1 |
00040 | HOME 2 |
테이블 2.dsps
fac_id | NDC | dsps_dtm | return_qty | dsps_qty |
00014 | 00000000001 | 20151001115631 | 0 | 2 |
00014 | 00000000001 | 20151002115631 | 1 | 1 |
00014 | 00000000002 | 20151003115631 | 0 | 1 |
00014 | 00000000002 | 20151004115631 | 1 | 2 |
00014 | 00000000003 | 20151024115631 | 1 | 5 |
00014 | 00000000003 | 20151015115631 | 0 | 3 |
00014 | 00000000003 | 20151019115631 | 0 | 4 |
00014 | 00000000001 | 20151001110000 | 1 | 3 |
00040 | 00000000001 | 20151030010000 | 1 | 2 |
테이블 3 alarm
fac_id | NDC | alarm_dtm | alarm_num | alarm_message |
00014 | 00000000001 | 20151003100222 | 21 | NDC:00000000001, Qty:150, CBN:242 |
00014 | 00000000001 | 20151001120631 | 21 | NDC:00000000001, Qty:9, CBN:24 |
00014 | 00000000002 | 20151010120632 | 21 | NDC:00000000002, Qty:120, CBN:2 |
00014 | 00000000003 | 20151012121111 | 21 | NDC:00000000003, Qty:115, CBN:123 |
00014 | 00000000003 | 20151023121111 | 21 | NDC:00000000003, Qty:113, CBN:123 |
00014 | 00000000003 | 20151025121212 | 21 | NDC:00000000003, Qty:8, CBN:123 |
00040 | 00000000003 | 20151008121212 | 21 | NDC:00000000003, Qty:90, CBN:123 |
00014 | 20151008121212 | 210 | ||
00014 | 20151029121212 | 210 |
원하는 결과 값 (기간을 2015년 10월부터 2015년 11월로 제한)
Fac_id | Fac_name | sum_dsps_qty | sum_return_qty | Count_alarm_21 | count_alarm_210 |
00014 | HOME 1 | 21 | 3 | 4 | 2 |
00040 | HOME 2 | 2 | 1 | 1 | 0 |
고견 부탁드립니다.
WITH fac(Fac_id, Fac_name) AS ( SELECT '00014', 'HOME 1' UNION ALL SELECT '00040', 'HOME 2' ) , dsps(fac_id, NDC, dsps_dtm, return_qty, dsps_qty) AS ( SELECT '00014', '00000000001', '20151001115631', 0, 2 UNION ALL SELECT '00014', '00000000001', '20151002115631', 1, 1 UNION ALL SELECT '00014', '00000000002', '20151003115631', 0, 1 UNION ALL SELECT '00014', '00000000002', '20151004115631', 1, 2 UNION ALL SELECT '00014', '00000000003', '20151024115631', 1, 5 UNION ALL SELECT '00014', '00000000003', '20151015115631', 0, 3 UNION ALL SELECT '00014', '00000000003', '20151019115631', 0, 4 UNION ALL SELECT '00014', '00000000001', '20151001110000', 1, 3 UNION ALL SELECT '00040', '00000000001', '20151030010000', 1, 2 ) , alarm(fac_id, NDC, alarm_dtm, alarm_num, alarm_message) AS ( SELECT '00014', '00000000001', '20151003100222', 21, 'NDC:00000000001, Qty:150, CBN:242' UNION ALL SELECT '00014', '00000000001', '20151001120631', 21, 'NDC:00000000001, Qty:9, CBN:24' UNION ALL SELECT '00014', '00000000002', '20151010120632', 21, 'NDC:00000000002, Qty:120, CBN:2' UNION ALL SELECT '00014', '00000000003', '20151012121111', 21, 'NDC:00000000003, Qty:115, CBN:123' UNION ALL SELECT '00014', '00000000003', '20151023121111', 21, 'NDC:00000000003, Qty:113, CBN:123' UNION ALL SELECT '00014', '00000000003', '20151025121212', 21, 'NDC:00000000003, Qty:8, CBN:123' UNION ALL SELECT '00040', '00000000003', '20151008121212', 21, 'NDC:00000000003, Qty:90, CBN:123' UNION ALL SELECT '00014', Null, '20151008121212', 210, Null UNION ALL SELECT '00014', Null, '20151029121212', 210, Null ) SELECT a.Fac_id , a.Fac_name , b.sum_dsps_qty , b.sum_return_qty , c.Count_alarm_21 , c.Count_alarm_210 FROM fac a LEFT OUTER JOIN (SELECT Fac_id , SUM(dsps_qty ) sum_dsps_qty , SUM(return_qty) sum_return_qty FROM dsps GROUP BY Fac_id ) b ON a.Fac_id = b.Fac_id LEFT OUTER JOIN (SELECT Fac_id , COUNT(CASE alarm_num WHEN 21 THEN 1 END) Count_alarm_21 , COUNT(CASE alarm_num WHEN 210 THEN 1 END) Count_alarm_210 FROM alarm WHERE alarm_dtm LIKE '201510%' AND ISNULL(SUBSTRING( alarm_message , CHARINDEX('Qty:', alarm_message) + 4 , CHARINDEX('CBN:', alarm_message) - CHARINDEX('Qty:', alarm_message) - 6 ), 11) > 10 GROUP BY Fac_id ) c ON a.Fac_id = c.Fac_id ;
마농님,
감사합니다. 제가 돌려 보니(실제 DB에서), 하기와 같은 에러 메세지가 나옵니다.
Invalid length parameter passed to the LEFT or SUBSTRING function.
제가 짰던 쿼리는,
저는 10개 미만을 찾을때 기간을 10월로 찾으때 where 절에 하기와 같이 썼습니다.
alarm_message not like N'%Qty:[0-10],%'
alarm_dtm between '20151001000000' and '20151100000000'
결과값은 같은데 효율성에서 어떤게 좋은지 궁금합니다.
마지막으로 left outer join 과 ISNULL()>10 에 대해 간단한 설명 부탁 드려도 될까요?
1. 에러 원인은...
- Substring 에서 마이너스 값이 대입되어서 그렇습니다.
- 예시에 없는 'Qty:', 'CBN:' 이 없는 자료가 포함되어서 있어서 그렇습니다.
- 이런 자료들에 대한 처리를 어떻게 할지 생각해야 합니다.
2. not like ???
- NOT 이 빠져야 하는것 아닌가요?
3. alarm_dtm between '20151001000000' and '20151100000000' ?
- LIKE 나 BETWEEN 이나 성능은 동일할 듯 하구요.
- 문자열 검색이기 때문에 굳이 자리수 맞추고 11월 대입할 필요는 없습니다.
- between '201510' AND '201510' + '9' 요런식으로 해도 됩니다.
4. left outer join 은?
- Fac_id 별로 dsps 와 alarm 의 데이터가 없어도 나오게 하기 위함입니다.
- 아우터 조인이 꼭 필요한지는 고민해 보셔야 하구요.
5. ISNULL()>10 은
- 210 자료의 alarm_message 가 Null 이지만 결과에 포함되어야 하기 때문에 사용한 조건입니다.
SELECT Fac_id , COUNT(CASE alarm_num WHEN 21 THEN 1 END) Count_alarm_21 , COUNT(CASE alarm_num WHEN 210 THEN 1 END) Count_alarm_210 FROM alarm WHERE alarm_dtm LIKE '201510%' AND alarm_num IN (21, 210) AND ISNULL(SUBSTRING( alarm_message + 'Qty:0, CBN:' , CHARINDEX('Qty:', alarm_message + 'Qty:0, CBN:') + 4 , CHARINDEX('CBN:', alarm_message + 'Qty:0, CBN:') - CHARINDEX('Qty:', alarm_message + 'Qty:0, CBN:') - 6 ), 11) > 10 GROUP BY Fac_id ;