Microsoft MVP성태의 닷넷 이야기
스크립트: 34. 파이썬 - MySQLdb 기본 예제 코드 [링크 복사], [링크+제목 복사],
조회: 15470
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 6개 있습니다.)

파이썬 - MySQLdb 기본 예제 코드

파이썬에서 mysql을 접근하는 것은, 지난 글에서 다룬 sqlite3에 비해 더 쉽습니다. 왜냐하면 파일 기반이 아닌, 전반적인 관리를 모두 mysqld 서버 측에서 처리를 하기 때문에 클라이언트는 그저 전통적인 SQL 쿼리에 기반을 둬 DB 명령어에만 집중하면 되기 때문입니다.

전체적인 도움말은 다음의 문서에 잘 나오는데요,

MySQLdb User’s Guide
; https://mysqlclient.readthedocs.io/user_guide.html

간략히 정리해 보면, 우선 pip install을 해주고,

// $ sudo apt install default-libmysqlclient-dev pkg-config -y

c:\temp> pip install mysql-python

// 또는,

c:\temp> pip install mysqlclient

연결 개체를 다음과 같은 식으로 얻을 수 있습니다.

from MySQLdb import _mysql

con = _mysql.connect("localhost", "testusr", "...", "test")

# 또는 이렇게,

con = _mysql.connect(host="localhost", user="testusr", passwd="...", db="test")

이후, 쿼리와 resultset을 다루는 것은 다음과 같은 식으로 간단하게 정리할 수 있습니다.

con.query("INSERT INTO test(name, age, enable) VALUES('테스터', 23, 1)")

con.query("SELECT * FROM test;")

r = con.store_result()

# 또는, 
# r = con.use_result()

while True:
    record = r.fetch_row()
    if not record:
        break

    print(record)

con.query("DELETE FROM test WHERE enable=1 AND age=23")

con.close()




그런데, 위의 예제 코드는 공식 문서에서도 나오듯이,

If you want to write applications which are portable across databases, use MySQLdb, and avoid using this module directly. MySQLdb._mysql provides an interface which mostly implements the MySQL C API


MySQLdb._mysql은 C로 작성한 MySQL API에 대응하는 것으로, 가능한 "MySQLdb._mysql"이 아닌 MySQLdb을 사용하라고 합니다. 이렇게 되면 또 사용법이 달라지는데요, 하지만 Python에서 제공한 DB 인터페이스인 dbapi2를 따르도록 MySQLdb을 구현하고 있으므로 sqlite3의 예제 코드에서와 동일한 경험으로 사용할 수 있습니다.

import MySQLdb

con = MySQLdb.connect("localhost", "testusr", "...", "test", connect_timeout=3) # connect_timeout 단위: 초

cursor = con.cursor()

query = "DELETE FROM test"
cursor.execute(query)

for idx in range(1, 5):
    query = "INSERT INTO test(name, age, enable) VALUES('tester{0}', {1}, {2});".format(idx, idx, idx * 10)
    cursor.execute(query)

con.commit()

query = "SELECT * FROM test"
cursor.execute(query)

record_text = ""

all_rows = cursor.fetchall()
for row in all_rows:
    record_text += str(row)
    # field_name = row[0]
    # field_age = row[1]
    # field_enable = row[2]

# 또는 이렇게,
#
# while True:
#     record = cursor.fetchone()
#     if not record:
#         break
#
#     record_text += str(record)

cursor.close()

con.close()  # 이 코드가 없으면 이후 누적돼 connect 시에 MySQLdb._exceptions.OperationalError: (1040, 'Too many connections') 오류 발생

사실상 위의 코드는 sqlite3에서 사용한 코드와 connect 함수의 인자 설정 방식만 다를 뿐 완전히 같습니다. 참고로 with를 이용한 자원 해제도 가능합니다.

with MySQLdb.connect(...[생략]...) as conn:
    conn.encoding = 'utf8'

    with conn.cursor() as cursor:

    query = "DO SLEEP(10); SELECT * FROM mytable;"
    cursor.execute(query)




그나저나, 닷넷 개발자라면 row에 대해 이름으로 값을 얻고 싶을 텐데,

all_rows = cursor.fetchall()
for row in all_rows:
    field_name = row['name']
    field_age = row['age']
    field_enable = row['enable']

# 예외 발생 TypeError at ...
# tuple indices must be integers or slices, not str

실제로 해보면 TypeError 오류가 발생합니다. 왜냐하면, 이것을 위해서는 애당초 cursor에 dictionary 형식으로 row를 유지하도록 다음과 같이 내부 타입을 명시해야 하기 때문입니다.

cursor = con.cursor(MySQLdb.cursors.DictCursor)

# ... query 수행

all_rows = cursor.fetchall()
for row in all_rows:
    field_name = row['name']
    field_age = row['age']
    field_enable = row['enable']

주의해야 할 것은, 이렇게 바꾼 다음부터는 숫자 인덱스 값을 전달할 수 없다는 것입니다.

cursor = con.cursor(MySQLdb.cursors.DictCursor)

# ... query 수행

all_rows = cursor.fetchall()
for row in all_rows:
    field_name = row[0] # 예외 발생 KeyError at ...
    field_age = row[1]
    field_enable = row[2]




또 하나 재미있는 것은 한글 처리입니다. MySQL 측의 encoding 설정이 utf8mb4로 되어 있고,

mysql> status;
--------------
mysql  Ver 8.0.26 for Win64 on x86_64 (MySQL Community Server - GPL)

Connection id:          30
Current database:
Current user:           root@localhost
SSL:                    Cipher in use is TLS_AES_256_GCM_SHA384
Using delimiter:        ;
Server version:         8.0.26 MySQL Community Server - GPL
Protocol version:       10
Connection:             localhost via TCP/IP
Server characterset:    utf8mb4
Db     characterset:    utf8mb4
Client characterset:    utf8mb4
Conn.  characterset:    utf8mb4
TCP port:               3306
Binary data as:         Hexadecimal
Uptime:                 2 hours 4 min 16 sec

Threads: 4  Questions: 108  Slow queries: 0  Opens: 185  Flush tables: 3  Open tables: 104  Queries per second avg: 0.014
--------------

MySQL Workbench를 이용해 "name" 필드에 직접 한글을 입력한 후 SELECT를 했더니 한글이 깨져 나옵니다.

(218, '??5', 3, '32')

혹은 Workbench를 이용하지 않고 쿼리에 한글을 포함하면,

for idx in range(1, 5):
    query = "INSERT INTO test(name, age, enable) VALUES('테스터{0}', {1}, {2});".format(idx, idx, idx * 10)
    cursor.execute(query)

# 예외 발생
# UnicodeEncodeError at /bbs/mysqlclient_wrapper
# 'charmap' codec can't encode characters in position 44-46: character maps to <undefined>

UnicodeEncodeError 오류가 발생합니다. 파이썬의 기본 인코딩이 Unicode이고 MySQL은 utf-8로 설정되어 있으므로 이럴 때는 어느 한쪽의 인코딩을 맞춰주면 됩니다. 물론 MySQL 데이터베이스 측을 바꿔도 되겠지만 대개의 경우 현업에서 그런 요구는 무리죠. ^^ 따라서 남은 방법은, 연결 문자열에 charset을 설정하는 식으로 해결할 수 있습니다.

con = MySQLdb.connect("localhost", "testusr", "...", "test", charset='utf8')

제 경우에 MySQL의 encoding이 utf8mb4로 되어 있기 때문에 utf8mb4로 바꿔도 무방합니다. 둘 간의 차이를 검색해 보면,

[MariaDB] Setting utf8mb4 Character Set
; https://medium.com/oldbeedev/mysql-utf8mb4-character-set-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-da7624958624

utf-8은 원래 모든 유니코드 문자를 인코딩할 수 있지만 MySQL의 경우에는 특별히 3바이트 이내로 제한을 했다고 합니다. 즉, MySQL에서 utf8은 제한적인 utf-8 형식에 해당하는 것으로 원래는 이름을 다른 걸로 썼어야 했을 것입니다. 어쨌든 나중에는 이러한 제한을 4바이트까지 풀어야 하는 요구 사항이 나왔을 것이고 이로 인해 원래의 utf-8에 해당하는 utf8mb4가 나온 것입니다. 정리하면, 만약 예전의 utf8을 utf8mb3으로 작명했었다면 새로운 utf8mb4를 그냥 utf8로 통일할 수 있었을 것입니다.

실제로 다음의 문서를 보면 utf8mb3라는 이름을 사용하고 있습니다. ^^

10.9.1 The utf8mb4 Character Set (4-Byte UTF-8 Unicode Encoding)
; https://dev.mysql.com/doc/refman/5.7/en/charset-unicode-utf8mb4.html



설치 시 이렇게 오류가 발생한다면?

$ pip install mysqlclient==2.0.3
Collecting mysqlclient==2.0.3
  Downloading mysqlclient-2.0.3.tar.gz (88 kB)
     |████████████████████████████████| 88 kB 2.2 MB/s
    ERROR: Command errored out with exit status 1:
     command: /usr/bin/python3 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-zltsyles/mysqlclient/setup.py'"'"'; __file__='"'"'/tmp/pip-install-zltsyles/mysqlclient/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-install-zltsyles/mysqlclient/pip-egg-info
         cwd: /tmp/pip-install-zltsyles/mysqlclient/
    Complete output (15 lines):
    /bin/sh: 1: mysql_config: not found
    /bin/sh: 1: mariadb_config: not found
    /bin/sh: 1: mysql_config: not found
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-install-zltsyles/mysqlclient/setup.py", line 15, in <module>
        metadata, options = get_config()
      File "/tmp/pip-install-zltsyles/mysqlclient/setup_posix.py", line 70, in get_config
        libs = mysql_config("libs")
      File "/tmp/pip-install-zltsyles/mysqlclient/setup_posix.py", line 31, in mysql_config
        raise OSError("{} not found".format(_mysql_config_path))
    OSError: mysql_config not found
    mysql_config --version
    mariadb_config --version
    mysql_config --libs
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

"default-libmysqlclient-dev" 구성요소를 설치하면 됩니다.

$ sudo apt install default-libmysqlclient-dev -y




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 5/17/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 106  107  108  109  110  111  112  113  114  115  [116]  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11025정성태8/12/201622337개발 환경 구성: 294. .NET Core 프로젝트에서 "Copy to Output Directory" 처리 [1]
11024정성태8/12/201621647오류 유형: 350. "nProtect GameMon" 실행 중에는 Visual Studio 디버깅이 안됩니다! [1]
11023정성태8/10/201623118개발 환경 구성: 293. Azure 구독 후 PaaS 서비스 만들어 보기
11022정성태8/10/201623800개발 환경 구성: 292. Azure Cloud Service 배포시 사용자 정의 작업을 추가하는 방법
11021정성태8/10/201620871오류 유형: 349. System.Runtime.Remoting.RemotingException - Type '..., ..., Version=..., Culture=neutral, PublicKeyToken=null' is not registered for activation [2]
11020정성태8/10/201623592VC++: 98. 원본과 대상 버퍼가 같은 경우 memcpy, wmemcpy 주의점
11019정성태8/10/201640265기타: 60. 도서: 시작하세요! C# 6.0 프로그래밍: 기본 문법부터 실전 예제까지 (2쇄 정오표)
11018정성태8/9/201624735.NET Framework: 600. 단일 메서드 내에서의 할당으로 알아보는 자바와 닷넷의 GC 차이점 [1]
11017정성태8/9/201626787웹: 33. HTTP 쿠키에 한글 값을 설정하는 방법
11016정성태8/7/201623989개발 환경 구성: 291. Windows Server Containers 소개
11015정성태8/7/201622260오류 유형: 348. Windows Server 2016 TP5에서 Windows Containers의 docker run 실행 시 encountered an error during Start failed in Win32
11014정성태8/6/201623049오류 유형: 347. Hyper-V Virtual Machine Management service Account does not have permission to open attachment
11013정성태8/6/201633800개발 환경 구성: 290. Windows 10에서 경험해 보는 Windows Containers와 docker [4]
11012정성태8/6/201623843오류 유형: 346. Windows 10에서 Windows Containers의 docker run 실행 시 encountered an error during CreateContainer failed in Win32 발생
11011정성태8/6/201625455기타: 59. outlook.live.com 메일 서비스의 아웃룩 POP3 설정하는 방법
11010정성태8/6/201622873기타: 58. Outlook에 설정한 SMTP/POP3(예:천리안 메일) 계정 암호를 잊어버린 경우
11009정성태8/3/201628061개발 환경 구성: 289. 2016-08-02부터 시작된 윈도우 10 1주년 업데이트에서 Bash Shell 사용 [8]
11008정성태8/1/201621842오류 유형: 345. 2의 30승 이상의 원소를 갖는 경우 버그가 발생하는 이진 검색(Binary Search) 코드
11007정성태8/1/201623559오류 유형: 344. RDP ActiveX 컨트롤로 특정 PC에 연결할 수 없을 때, 오류 상황을 해결하기 위한 팁파일 다운로드1
11006정성태7/22/201626572개발 환경 구성: 288. SSL 인증서를 Azure Cloud Service에 적용하는 방법
11005정성태7/22/201625209개발 환경 구성: 287. Let's Encrypt 인증서 업데이트 주기: 90일
11004정성태7/22/201620065오류 유형: 343. Invalid service definition or service configuration. Please see the Error List for more details.
11003정성태7/20/201627337VS.NET IDE: 110. Visual Studio 2015에서 .NET Core 응용 프로그램 개발 [1]
11002정성태7/20/201620830개발 환경 구성: 286. Microsoft Azure 서비스의 구독은 반드시 IE로!
11001정성태7/19/201631858.NET Framework: 599. .NET Core/SDK 설치 및 기본 사용법 [6]
11000정성태7/16/201620597오류 유형: 342. Microsoft Visual Studio 2010 Tools for Office Runtime (x86 and x64) 설치 시 오류
... 106  107  108  109  110  111  112  113  114  115  [116]  117  118  119  120  ...