커서
커서는 테이블의 여러행을 쿼리한 후에, 쿼리의 결과인 행 집합을 한 행씩 처리하기 위한 방식
순서 : 파일을 연다 -> 처음 데이터를 읽는다. -> 파일의 끝까지 반복한다. -> 파일을 닫는다.
명령어
커서선언 DECLARE CURSOR
반복조건 선언 DECLARE CONTINUE HANDLER
(더이상 읽을 행이 없을 경우에, 실행할 내용 설정)
커서 열기 OPEN
커서에서 데이터 가져오기 FETCH
(LOOP ~ END LOOP 문으로 반복 구간 지정 )
데이터 처리
커서닫기
-- 예제 테이블 생성 및 데이타 생성
CREATE TABLE Member (
userid VARCHAR(20),
`point` INT
) ENGINE = InnoDB ROW_FORMAT = DEFAULT;
insert into Member (UserID ) VALUES ( 'User01') ;
insert into Member (UserID ) VALUES ( 'User02') ;
insert into Member (UserID ) VALUES ( 'User03') ;
insert into Member (UserID ) VALUES ( 'User04') ;
commit;
select * from Member;
drop PROCEDURE if EXISTS curdemo
delimiter $$
CREATE PROCEDURE curdemo()
BEGIN
DECLARE done boolean DEFAULT FALSE;
DECLARE vRowCount INT DEFAULT 0 ;
DECLARE vUserID varchar(20);
-- 커서로 만들 데이타 값들
DECLARE cur1 CURSOR FOR SELECT Userid FROM Member;
-- 커서가 마지막에 도착할 때의 상태값
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- 커서를 연다.
OPEN cur1;
-- Loop 가 돌아간다.
read_loop: LOOP
-- 커서로 만드어진 데이타를 돌린다.
FETCH cur1 INTO vUserID ;
SET vRowCount = vRowCount +1 ;
-- 커서가 마지막 로우면 Loop를 빠져나간다.
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
SELECT vRowCount ;
-- 커서를 닫는다.
CLOSE cur1;
END;
delimiter;
Call curdemo() ;
-- 예제 테이블 생성
CREATE TABLE PointHistory (
UserID VARCHAR(20),
PointDate DATE,
PointValue INT
) ENGINE = InnoDB ROW_FORMAT = DEFAULT;
-- 예제 데이타 생성
insert into PointHistory ( UserID ,PointDate ,PointValue)
VALUES ( 'User02' ,'2014-01-01' , 10 )
-- 이미 있는 프로시져 삭제한다. ..
DROP PROCEDURE IF EXISTS curdemo ;
delimiter $$
CREATE PROCEDURE curdemo()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE vRowCount INT DEFAULT 0 ;
DECLARE vUserID varchar(20);
DECLARE vPointValue int ;
DECLARE cur1 CURSOR FOR SELECT Userid FROM Member;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO vUserID ;
-- Not Found Handler 값 변화 살펴보자.
SELECT done ;
-- 포인트 테이블의 값을 읽어 온다.
SELECT PointValue into vPointValue FROM pointhistory
WHERE UserID = vUserID ;
-- 회원테이블의 포인트 값에 업데이트 한다.
UPDATE Member Set point = vPointValue WHERE UserID = vUserID ;
-- 커서가 몇번을 도는지 알아 본다.
SET vRowCount = vRowCount + 1 ;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
SELECT vRowCount ;
CLOSE cur1;
END;
delimiter;
Call curdemo() ;
-- 실행 결과 없다. → 우리가 원하는 바가 아니다. vRowCount 가 1이다. 1번 돌았다.
-- 3. 해결 방법
--
-- - 커서의 DECLARE CONTINUE HANDLER FOR NOT FOUND 는 커서의 집합이 없을 때이기도 하지만 커서안에서 다른 쿼리문의 집합이 없을 때도 True 을 반환한다.
-- - 그래서 Mysql 은 커서가 이상해 이런 얘기가 나오는 거다.
-- - MSSQL은 커서의 집합만을 비교하여 마지막 커서행인지 판단해 주는데 mysql 의 경우 커서뿐만 아니라 커서안의 select 의 집합도 NOT FOUND로 판단하고 있다.
-- - 그래서 커서안의 select 의 집합의 NOT FOUND와 Curosor 의 NOT FOUND을 구분하여 줄 필요가 있다.
DROP PROCEDURE IF EXISTS curdemo ;
delimiter $$
CREATE PROCEDURE curdemo()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE vRowCount INT DEFAULT 0 ;
DECLARE vUserID varchar(20);
DECLARE vPointValue int ;
DECLARE cur1 CURSOR FOR SELECT Userid FROM Member;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done =TRUE ;
OPEN cur1;
REPEAT
FETCH cur1 INTO vUserID ;
-- Not Found Handler 값 변화 살펴보자.
SELECT done ;
-- 커서가 마지막이 아니라면
IF NOT done THEN
SELECT PointValue into vPointValue FROM pointhistory
WHERE UserID = vUserID ;
UPDATE Member Set point = vPointValue WHERE UserID = vUserID ;
-- SELECT concat(vUserID, '', vPointValue) ;
SET vPointValue = 0 ;
-- 위의 select 가 조회 데이타가 없어서 not found 되어
-- fetch 문을 빠져나가는 걸 방지한다.
SET done = False ;
END IF;
UNTIL DONE END REPEAT;
CLOSE cur1;
END;
delimiter;
-- 위의 예제는 커서가 마지막행을 만나기 전에 SELECT 문에서 조회값이 없는 경우 Not Found 도 발생하는 걸 인위적으로 SET done = False 으로 해결 하고 있다.
Call curdemo()
출처: http://bizadmin.tistory.com/entry/MySQL-Fetch-Cursor-%EB%AC%B8-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 | 커서 커서는 테이블의 여러행을 쿼리한 후에, 쿼리의 결과인 행 집합을 한 행씩 처리하기 위한 방식 순서 : 파일을 연다 -> 처음 데이터를 읽는다. -> 파일의 끝까지 반복한다. -> 파일을 닫는다. 명령어 커서선언 DECLARE CURSOR 반복조건 선언 DECLARE CONTINUE HANDLER (더이상 읽을 행이 없을 경우에, 실행할 내용 설정) 커서 열기 OPEN 커서에서 데이터 가져오기 FETCH (LOOP ~ END LOOP 문으로 반복 구간 지정 ) 데이터 처리 커서닫기 -- 예제 테이블 생성 및 데이타 생성 CREATE TABLE Member ( userid VARCHAR(20), `point` INT ) ENGINE = InnoDB ROW_FORMAT = DEFAULT; insert into Member (UserID ) VALUES ( 'User01') ; insert into Member (UserID ) VALUES ( 'User02') ; insert into Member (UserID ) VALUES ( 'User03') ; insert into Member (UserID ) VALUES ( 'User04') ; commit; select * from Member; drop PROCEDURE if EXISTS curdemo delimiter $$ CREATE PROCEDURE curdemo() BEGIN DECLARE done boolean DEFAULT FALSE; DECLARE vRowCount INT DEFAULT 0 ; DECLARE vUserID varchar(20); -- 커서로 만들 데이타 값들 DECLARE cur1 CURSOR FOR SELECT Userid FROM Member; -- 커서가 마지막에 도착할 때의 상태값 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- 커서를 연다. OPEN cur1; -- Loop 가 돌아간다. read_loop: LOOP -- 커서로 만드어진 데이타를 돌린다. FETCH cur1 INTO vUserID ; SET vRowCount = vRowCount +1 ; -- 커서가 마지막 로우면 Loop를 빠져나간다. IF done THEN LEAVE read_loop; END IF; END LOOP; SELECT vRowCount ; -- 커서를 닫는다. CLOSE cur1; END; delimiter; Call curdemo() ; -- 예제 테이블 생성 CREATE TABLE PointHistory ( UserID VARCHAR(20), PointDate DATE, PointValue INT ) ENGINE = InnoDB ROW_FORMAT = DEFAULT; -- 예제 데이타 생성 insert into PointHistory ( UserID ,PointDate ,PointValue) VALUES ( 'User02' ,'2014-01-01' , 10 ) -- 이미 있는 프로시져 삭제한다. .. DROP PROCEDURE IF EXISTS curdemo ; delimiter $$ CREATE PROCEDURE curdemo() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE vRowCount INT DEFAULT 0 ; DECLARE vUserID varchar(20); DECLARE vPointValue int ; DECLARE cur1 CURSOR FOR SELECT Userid FROM Member; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN cur1; read_loop: LOOP FETCH cur1 INTO vUserID ; -- Not Found Handler 값 변화 살펴보자. SELECT done ; -- 포인트 테이블의 값을 읽어 온다. SELECT PointValue into vPointValue FROM pointhistory WHERE UserID = vUserID ; -- 회원테이블의 포인트 값에 업데이트 한다. UPDATE Member Set point = vPointValue WHERE UserID = vUserID ; -- 커서가 몇번을 도는지 알아 본다. SET vRowCount = vRowCount + 1 ; IF done THEN LEAVE read_loop; END IF; END LOOP; SELECT vRowCount ; CLOSE cur1; END; delimiter; Call curdemo() ; -- 실행 결과 없다. → 우리가 원하는 바가 아니다. vRowCount 가 1이다. 1번 돌았다. -- 3. 해결 방법 -- -- - 커서의 DECLARE CONTINUE HANDLER FOR NOT FOUND 는 커서의 집합이 없을 때이기도 하지만 커서안에서 다른 쿼리문의 집합이 없을 때도 True 을 반환한다. -- - 그래서 Mysql 은 커서가 이상해 이런 얘기가 나오는 거다. -- - MSSQL은 커서의 집합만을 비교하여 마지막 커서행인지 판단해 주는데 mysql 의 경우 커서뿐만 아니라 커서안의 select 의 집합도 NOT FOUND로 판단하고 있다. -- - 그래서 커서안의 select 의 집합의 NOT FOUND와 Curosor 의 NOT FOUND을 구분하여 줄 필요가 있다. DROP PROCEDURE IF EXISTS curdemo ; delimiter $$ CREATE PROCEDURE curdemo() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE vRowCount INT DEFAULT 0 ; DECLARE vUserID varchar(20); DECLARE vPointValue int ; DECLARE cur1 CURSOR FOR SELECT Userid FROM Member; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done =TRUE ; OPEN cur1; REPEAT FETCH cur1 INTO vUserID ; -- Not Found Handler 값 변화 살펴보자. SELECT done ; -- 커서가 마지막이 아니라면 IF NOT done THEN SELECT PointValue into vPointValue FROM pointhistory WHERE UserID = vUserID ; UPDATE Member Set point = vPointValue WHERE UserID = vUserID ; -- SELECT concat(vUserID, '', vPointValue) ; SET vPointValue = 0 ; -- 위의 select 가 조회 데이타가 없어서 not found 되어 -- fetch 문을 빠져나가는 걸 방지한다. SET done = False ; END IF; UNTIL DONE END REPEAT; CLOSE cur1; END; delimiter; -- 위의 예제는 커서가 마지막행을 만나기 전에 SELECT 문에서 조회값이 없는 경우 Not Found 도 발생하는 걸 인위적으로 SET done = False 으로 해결 하고 있다. Call curdemo() 출처: http://bizadmin.tistory.com/entry/MySQL-Fetch-Cursor-%EB%AC%B8-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95 | cs |
'데이터 베이스 > MySQL' 카테고리의 다른 글
<실습> 1.mysql 기본 (4) | 2017.05.10 |
---|---|
MYSQL 트리거&TRIGGER 예제&문제 17 (4) | 2016.12.20 |
MYSQL 뷰&VIEW 예제&문제 15 (4) | 2016.12.19 |
MYSQL 스토어드 함수 예제&문제 14 (4) | 2016.12.16 |
MYSQL procedure&프로시저 예제&문제 13 (2) | 2016.12.16 |
댓글