블로그 이미지
Sunny's

calendar

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 27 28 29 30 31

Notice

2009. 11. 19. 15:40 MSSQL

Contents

1 시나리오
2 MSSQL 2005의 구현방법


1 시나리오 #

다음과 같은 테이블 있다.
image001.jpg

고객 yasicom은 bill 테이블과 같이 캐시가 충전되어 있다. 그리고 bill_policy테이블에는 어떤 요금부터 차감을 할 것인지에 대한 계산의 우선순위가 매겨져 있다. 또한 충전한 요금유형이 여러 개라면 먼저 충전된 캐시가 차감된다. 즉, 선입선출이다. 그러므로 다음과 같은 우선순위를 갖는다.

image002.jpg

만약 17,000원짜리 아이템을 구매하였다면, 다음과 같이 차감되어야 한다.

image003.jpg

  1. 1번째 Row: 500 - 17000 = -16500
  2. 2번째 Row: 2000 - 16500 = -14500
  3. 3번째 Row: 500 - 14500 = -14000
  4. 4번째 Row: 10000 - 14000 = -4000
  5. 5번째 Row: 5000 - 4000 = 1000

즉, 17,000원 짜리 아이템을 구매했으면 충전된 캐시가 다음과 같이 차감(업데이트)되어야 한다.

image004.jpg

2 MSSQL 2005의 구현방법 #


2000버전의 MSSQL Server는 이러한 구현을 하려면 매우 우울했었다. 많은 방법을 생각해보았지만 커서만한 솔루션이 없었으니까 말이다. 어찌되었던 2005 버전에서는 CTE를 사용하는 재귀쿼리를 이용하여 선입선출 및 차감 Row단위의 접근이 어느 정도 가능해졌다. 뭐 집합적으로 가능해졌다고 할 수도 있겠으나 집합적이다 라고 말하기도 조금 뭣하다. 어찌되었건 다음과 같이 구현해 보았다.

  1. USE tempdb
  2. go
  3.  
  4. IF OBJECT_ID('bill_policy') IS not null
  5. DROP TABLE bill_policy
  6. CREATE TABLE bill_policy(
  7. charge_type VARCHAR(20)
  8. , calc_rank TINYINT
  9. )
  10.  
  11. IF OBJECT_ID('bill') IS not null
  12. DROP TABLE bill
  13. CREATE TABLE bill(
  14. seq INT IDENTITY(1,1) PRIMARY KEY
  15. , cust_id VARCHAR(20)
  16. , cash INT
  17. , charge_type VARCHAR(20)
  18. , create_dt DATETIME
  19. )
  20.  
  21.  
  22. INSERT bill_policy VALUES('이벤트', 1)
  23. INSERT bill_policy VALUES('상품권', 2)
  24. INSERT bill_policy VALUES('실캐시', 3)
  25. INSERT bill_policy VALUES('포인트', 4)
  26.  
  27. INSERT bill VALUES('yasicom', 500, '이벤트', '20070811')
  28. INSERT bill VALUES('yasicom', 5000, '실캐시', '20070701')
  29. INSERT bill VALUES('yasicom', 10, '포인트', '20070811')
  30. INSERT bill VALUES('yasicom', 2000, '이벤트', '20070912')
  31. INSERT bill VALUES('yasicom', 500, '상품권', '20070830')
  32. INSERT bill VALUES('yasicom', 10000, '실캐시', '20070613')
  33.  
  34. SELECT * FROM bill
  35. SELECT * FROM bill_policy
  36.  
* IE에서 소스 복사시 줄바꿈 안됨. MS-Word 등에 붙여 넣으세요. 줄바꿈 되는 브라우저: Chrome, Opera, Safari

image004.jpg

  1. SELECT
  2. b.cust_id
  3. , b.cash
  4. , b.create_dt
  5. , a.charge_type
  6. , row_number() OVER(ORDER BY a.calc_rank, b.create_dt) calc_rank
  7. FROM bill_policy a INNER join bill b
  8. ON a.charge_type = b.charge_type
  9.  
* IE에서 소스 복사시 줄바꿈 안됨. MS-Word 등에 붙여 넣으세요. 줄바꿈 되는 브라우저: Chrome, Opera, Safari

image006.jpg

  1. --만약 1,7000원 짜리item을 구매한다면..
  2. DECLARE
  3. @price INT
  4. , @cust_id VARCHAR(20);
  5. SET @cust_id = 'yasicom'
  6. SET @price = 17000; --세미콜론(;)을 꼭 찍어줘야 한다. 그렇지 않으면 문법에러라고 뻘건 글씨봐야 한다.
  7.  
  8. IF (SELECT isnull(SUM(cash), 0) FROM bill WHERE cust_id = @cust_id) >= @price
  9. BEGIN
  10. WITH base(seq, cust_id, cash, create_dt, charge_type, calc_rank)
  11. AS
  12. (
  13. SELECT
  14. b.seq
  15. , b.cust_id
  16. , b.cash
  17. , b.create_dt
  18. , a.charge_type
  19. , row_number() OVER(ORDER BY a.calc_rank, b.create_dt) calc_rank
  20. FROM bill_policy a INNER join bill b
  21. ON a.charge_type = b.charge_type
  22. ),
  23. cte(seq, cust_id, cash, remain_cash, deduction_cash, create_dt, charge_type, calc_rank, LEVEL)
  24. AS
  25. (
  26. SELECT-Anchor member
  27. seq
  28. , cust_id
  29. , cash
  30. , CASE WHEN cash - @price <= 0 THEN 0 ELSE cash - @price END remain_cash
  31. , cash - @price deduction_cash
  32. , create_dt
  33. , charge_type
  34. , calc_rank
  35. , 1 AS LEVEL
  36. FROM base
  37. WHERE calc_rank = 1
  38. UNION all
  39. SELECT-Recusive member
  40. a.seq
  41. , a.cust_id
  42. , a.cash
  43. , CASE WHEN a.cash + b.deduction_cash <= 0 THEN 0 ELSE a.cash + b.deduction_cash END remain_cash
  44. , a.cash + b.deduction_cash deduction_cash
  45. , a.create_dt
  46. , a.charge_type
  47. , a.calc_rank
  48. , b.LEVEL + 1
  49. FROM base a INNER join cte b
  50. ON a.calc_rank = b.LEVEL + 1
  51. WHERE a.cash + b.deduction_cash < 0 --where 절은 어디까지 update 해야 하는지 구분하는 부분
  52. or a.cash > b.deduction_cash --where 절을 빼면 마지막 순서의 Row는 남은 캐시 합계가 된다.
  53. )
  54. UPDATE a
  55. SET a.cash = b.remain_cash
  56. FROM bill a INNER join cte b
  57. ON a.seq = b.seq;
  58. /*
  59. select
  60. seq 차감일련번호
  61. , cust_id 고객ID
  62. , cash 캐시
  63. , remain_cash 남은캐시
  64. , deduction_cash 차감해야할캐시
  65. , charge_type 요금유형
  66. , calc_rank 차감우선순위
  67. , create_dt 캐시충전일시
  68. from cte;
  69. */
  70. END ELSE
  71. SELECT '캐시가부족하니까충전해유~';
  72. go
  73.  
  74. SELECT * FROM bill;
  75.  
* IE에서 소스 복사시 줄바꿈 안됨. MS-Word 등에 붙여 넣으세요. 줄바꿈 되는 브라우저: Chrome, Opera, Safari
image007.jpg

출 처 : http://databaser.net/moniwiki/wiki.php/%EB%B9%8C%EB%A7%81%EC%9D%98%EC%B0%A8%EA%B0%90%EB%B0%8F%EC%84%A0%EC%9E%85%EC%84%A0%EC%B6%9C%EB%B0%A9%EB%B2%95
posted by Sunny's