처음만나는 에러
python의 pymysql 라이브러리를 이용해서 MySQL 데이터베이스 내 특정 테이블에 INSERT를 할 때,
아래 예시와 같이, SET~; 문과 INSERT ~ SELECT; [테이블 속성1, 테이블 속성2, ...] 문 2개를 한 번에 실행해서 INSERT를 하는 경우가 있다.
# 데이터베이스 INSERT 쿼리 : GolfMember 테이블에 있는 멤버의 id와 name을 Member 테이블에 추가 (중복시 name 업데이트)
last_id_query = '''
SET @last_id := (SELECT MAX(id) FROM Member); -- Member 테이블의 마지막 pk 값을 변수로 저장
INSERT INTO Member (`id`, `name`)
SELECT @last_id:=@last_id+1 AS `id`, `name` -- 마지막 id 값 이후는 +1씩, name 값을 select
FROM GolfMember G
ON DUPLICATE KEY UPDATE -- 중복 발생 시 Member 테이블의 name을 GolfMember 테이블의 name으로 update
name = G.name;
'''
# 데이터베이스 커넥션 생성
conn = pymysql.connect(host=DB_HOST, # 데이터베이스의 호스트 주소
user=DB_SUPER_USER, # 데이터베이스 계정
password=DB_SUPER_PW, # 데이터베이스 비밀번호
port=PORT_NUM, # 포트번호
db=DB_NAME # 데이터베이스 이름
)
# 데이터베이스에 INSERT 쿼리 실행
with conn.cursor(pymysql.cursors.DictCursor) as curs:
fetch_count = curs.execute(last_id_query)
conn.commit()
위와 같은 상황에서, pymysql은 에러를 내보내는데, 그 에러는 INSERT ~ SELECT; 문에서 발생하는 에러다.
분명, mysql workbench에서는 저 쿼리가 실행이 됐는데, pymysql에서만 실행이 안된다.
이유와 해결 방법
그 이유는 단순하게도 pymysql에서 .execute() 메서드를 호출하면,
쿼리문 안의 세미콜론(;)을 하나만 인식해서 SET ~; 쿼리만 실행하고 끝내기 때문에 발생하는 에러다.
@last_id 변수는 SET ~; 쿼리를 한 번 실행하면 데이터베이스 내 지역변수로 저장하기 때문에 커넥션 세션이 닫혀도 계속 유지된다.
따라서 해결법은 .execute() 메서드를 각각의 쿼리마다 호출해주면 된다.
해결 코드 전문
first_query = 'SET @last_id := (SELECT MAX(id) FROM Member);' -- Member 테이블의 마지막 pk 값을 변수로 저장
insert_query = '''
INSERT INTO Member (`id`, `name`)
SELECT @last_id:=@last_id+1 AS `id`, `name` -- 마지막 id 값 이후는 +1씩, name 값을 select
FROM GolfMember G
ON DUPLICATE KEY UPDATE -- 중복 발생 시 Member 테이블의 name을 GolfMember 테이블의 name으로 update
name = G.name;
'''
with conn.cursor(pymysql.cursors.DictCursor) as curs:
fetch_count += curs.execute(first_query)
fetch_count += curs.execute(insert_query)
conn.commit()
'Python > Python Distilled' 카테고리의 다른 글
[Python] SMTP, email, pandas 라이브러리 활용한 이메일 자동화 (RPA) (한글 깨짐 해결) (0) | 2023.02.06 |
---|---|
[python] 대입 연산자 (:=) (1) | 2022.10.04 |
[python] 이스케이프 표현식(escaped expression) (0) | 2022.09.12 |