파이썬에서 SQL Formatting 하는 법을 자꾸 잊어버립니다 (ORM 최고야)
💡 코딩 스타일은 항상 민감한 문제이므로 '', "", ''', """ 에 대해서
이것이 옳다! 라는 소모적인 논쟁은 정중히 사양하겠습니다 🙇♂️
(개인적으로는 "He's a man" 같은 문장을 위해, double quote 가 좋다고 생각합니다)
파이썬에서 긴 문자열을 여러줄에 걸쳐 작성하는 방법이 몇 가지 있습니다
얘기를 시작하기 전에, SQL 문자열은 double quote 로 감싸는 방법을 권장합니다
ANSI QUOTES
모드가 켜져 있는 상태에 한정WHERE username = 'scott'
과 같이 표현해야 하므로, "" 로 감싸는 것이 더 바람직 합니다string format 방법은, 기가 막힌 설명이 있기에 제가 다시 정리하지는 않겠습니다.
%(printf style)
, .format()
, f-string
에 대한 내용입니다
교통정리를 마쳤으니 Python 에서 SQL statement 을 알아봅니다
SQL statement 를 작성하는 방법은 세 가지 정도가 있습니다
(여기 쓰지 않은 다른 방법도 얼마든지 가능합니다. 문자열만 만들면 되니까요)
소괄호를 사용하여, 여러 줄에 걸쳐 긴 문자열을 완성할 수 있습니다
>>> sql = (
... "SELECT * "
... "FROM user "
... "WHERE username = 'scott'"
... )
>>> print(sql)
SELECT * FROM user WHERE username = 'scott'
아쉬운 점은, SELECT
, FROM
끝에 공백을 넣어줘야 한다는 점 입니다
그렇지 않으면 SELECT *FROM userWHERE username = 'scott'
이렇게 됩니다
(ProgrammingError
가 발생합니다)
>>> sql = "SELECT * " \
... "FROM user " \
... "WHERE username = 'scott'"
>>> print(sql)
SELECT * FROM user WHERE username = 'scott'
소괄호로 묶는 방법과 유사합니다만 나중에 문자열을 더 추가하면서
문자열 맨 마지막의 escaping character 를 잊어버릴 수 있으니 소괄호로 묶는게 더 나을듯 합니다
(저만의 걱정일수 있으니 항상 스타일은 자유롭게... ㅇㅅㅇ)
마찬가지로, 맨 끝에 공백을 넣어줘야 하는 점이 아쉽습니다
docstring 을 작성할 때 많이 사용되지만, 문자열로 지정할 수 있습니다
triple quotes 스타일에서, 첫 문장을 붙여서 또는 줄 바꿈 하여 시작할지에 대한 고민은
오픈소스의 docstring 작성 방법이 각각 다르니, '', "" 의 문제처럼 일관성만 맞추면 된다 생각합니다
>>> sql = """
... SELECT *
... FROM user
... WHERE username = 'scott'
... """
>>> print(sql)
SELECT *
FROM user
WHERE username = 'scott'
print(sql)
을 하게 되면, 첫 줄은 줄 바꿈(\n), 이후에는 indentation(\t) 이 유지됩니다
위 두 가지 스타일과 다르게, 별도의 공백 처리가 필요하지 않다는 점이 좋아 보입니다
아래의 paramstyle 을 설명하면서는 triple quote 형태를 사용하도록 하겠습니다
저는 보통 이렇게 사용하기 때문이죠 🐶
💡 SQL paramstyle 에 맞춰 parameterize 를 해야 하는 이유는, SQL injection 을 막기 위함 입니다
더불어, paramstyle 이라는 단어는 PEP-0249#paramstyle 부분에서 차용했습니다.
처음부터 완성된 문자열을 만들어서 실행할 수도 있지만
보통은 변수를 받아, 그 값을 SQL 문에 할당하여 실행하곤 합니다
def get_user(user_id: int):
sql = '''
SELECT *
FROM user
WHERE id = %s
'''
cursor.execute(sql, user_id)
return cursor.fetchone()
지금은 바로 저 id = %s
를 어떻게 만들 것인가에 대한 내용을 정리하려고 합니다
매 번 작성할 때마다 헷갈리기 때문이죠 😭
PEP-0249 에는 아래와 같이 다양한 방법으로 작성할 수 있다고 설명합니다
paramstyle | Meaning |
---|---|
qmark | Question mark style, e.g. ...WHERE name=? |
numeric | Numeric, positional style, e.g. ...WHERE name=:1 |
named | Named style, e.g. ...WHERE name=:name |
format | ANSI C printf format codes, e.g. ...WHERE name=%s |
pyformat | Python extended format codes, e.g. ...WHERE name=%(name)s |
그리고 Python 에서 사용할 수 있는 각각의 DB API 에 따라
권장하는 방식을 직접 확인할 수 있습니다 (https://wiki.python.org/moin/DbApiFaq)
# MySQL
>>> pymysql.paramstyle
'pyformat'
>>> MySQLdb.paramstyle
'format'
>>> mysql.connector.paramstyle
'pyformat'
# Postgresql
>>> psycopg2.paramstyle
'pyformat'
# MS-SQL
>>> pymssql.paramstyle
'pyformat'
# Oracle
>>> cx_Oracle.paramstyle
'named'
# Sqlite3
>>> sqlite3.paramstyle
'qmark'
# Infomix
>>> IfxPy.paramstyle
'qmark'
다 찾아보느라 힘들었습니다... 대표적인 것만 추려봤어요
는 미친 이런 사이트가 있네요 Python-wiki#DbApiModuleComparison
(대체 뭘 한거지...)
사용하는 DB API 에 따라서, 필요한 paramstyle 을 적용하면 됩니다
대표적으로 많이 사용하는 pymysql 을 이용하여 pyformat 을
어떻게 올바르게 사용할 수 있는지 알아보겠습니다
insert_stmt = (
"INSERT INTO employees (emp_no, first_name, last_name, hire_date) "
"VALUES (%s, %s, %s, %s)"
)
data = (2, 'Jane', 'Doe', datetime.date(2012, 3, 23))
cursor.execute(insert_stmt, data)
select_stmt = "SELECT * FROM employees WHERE emp_no = %(emp_no)s"
cursor.execute(select_stmt, {'emp_no': 2 })
dev.mysql.com 에서 안내하는 Python Developer Guide 예제 입니다
insert_stmt
에서 처럼, format 스타일을 사용할 수도 있고,
select_stmt
에서 처럼, pyformat 스타일을 사용할 수도 있습니다
format 스타일은, tuple(또는 list)로 값을 전달하고
pyforamt 스타일은, dict 형태의 값으로 전달 되어야 합니다
pyformat 에 tuple(또는 list) 스타일로 값을 전달하면
TypeError: format requires a mapping
와 같은 에러가 발생합니다
반대로, format 에 dict 형태로 값을 전달하면
dict 값이 문자열로 치환 되면서 pymysql.err.ProgrammingError
에러가 발생합니다
다시 한 번, SQLAlchemy, Django ORM 에 고마움을 느낍니다 (?)
각 DB API 별로 지원하는 paramstyle 이 있다는 걸 알아낸 것만으로도 큰 도움이 되었습니다
다음에 기회가 된다면, 여러 줄의 쿼리문을 실행하는 방법이나
한 번에 여럿을 insert 하는 방법도 정리해 보려고 합니다... 언젠가
긴 글 읽어주셔서 감사합니다. 지적은 언제나 환영입니다
저는 with triple (single) quotes 이 좋아보이네요! ㅋㅋ