Python SQL 쿼리 문자열 형식


100

SQL 쿼리 문자열을 형식화하는 가장 좋은 방법을 찾으려고합니다. 내 응용 프로그램을 디버깅 할 때 모든 SQL 쿼리 문자열을 파일에 기록하고 싶습니다. 문자열의 형식이 올바르게 지정되어 있어야합니다.

옵션 1

def myquery():
    sql = "select field1, field2, field3, field4 from table where condition1=1 and condition2=2"
    con = mymodule.get_connection()
    ...
  • 이것은 SQL 문자열을 인쇄하는 데 좋습니다.
  • 문자열이 길고 표준 너비 인 80 자에 맞지 않는 경우 좋은 솔루션이 아닙니다.

옵션 2

def query():
    sql = """
        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2"""
    con = mymodule.get_connection()
    ...
  • 여기 코드는 명확하지만 SQL 쿼리 문자열을 인쇄하면 이러한 모든 성가신 공백이 나타납니다.

    u '\ n 테이블에서 field1, field2, field3, field4 \ n_ _ ___ 선택 \ n _ ___ where condition1 = 1 \ n _ ___ _and condition2 = 2'

참고 : 공백 _은 편집기에 의해 잘 리기 때문에 밑줄로 대체 했습니다.

옵션 3

def query():
    sql = """select field1, field2, field3, field4
from table
where condition1=1
and condition2=2"""
    con = mymodule.get_connection()
    ...
  • 이 옵션은 잘 표로 작성된 코드의 명확성을 깨뜨리기 때문에 마음에 들지 않습니다.

옵션 4

def query():
    sql = "select field1, field2, field3, field4 " \
          "from table " \
          "where condition1=1 " \
          "and condition2=2 "
    con = mymodule.get_connection()    
    ...
  • 이 옵션이 마음에 들지 않습니다. 각 줄에 모든 추가 입력이 있고 쿼리도 편집하기 어렵 기 때문입니다.

나에게 가장 좋은 해결책은 옵션 2 이지만 SQL 문자열을 인쇄 할 때 추가 공백이 마음에 들지 않습니다.

다른 옵션을 알고 있습니까?


이것은 Psycopg 사람들이 쿼리 문자열의 구성에 대한 순진한 접근 방식이라고 부르는 것입니다 (예 : 문자열 연결 사용 -initd.org/psycopg/docs/…) . 대신 쿼리 매개 변수를 사용하여 SQL 주입 공격을 피하고 Python 개체를 SQL 리터럴과 자동으로 변환합니다. stackoverflow.com/questions/3134691/...
매튜 코넬에게

이 질문은 실제로 SQL 쿼리에만 국한되지 않지만 일반적으로 Python에서 여러 줄 문자열을 형식화하는 데 적용됩니다. SQL 태그를 제거해야합니다.
cstork

답변:


134

그런 오래된 스레드에 게시 해 주셔서 죄송합니다.하지만 비단뱀 같은 '최고'에 대한 열정도 공유하는 사람으로서 저는 우리의 솔루션을 공유 할 것이라고 생각했습니다.

해결책은 파이썬의 문자열 리터럴 연결 ( http://docs.python.org/)을 사용하여 SQL 문을 빌드하는 것입니다. )을 하는 것입니다. 이는 옵션 2와 옵션 4 사이의 어딘가에 자격을 부여 할 수 있습니다.

코드 샘플 :

sql = ("SELECT field1, field2, field3, field4 "
       "FROM table "
       "WHERE condition1=1 "
       "AND condition2=2;")

f- 문자열 과도 잘 작동합니다 .

fields = "field1, field2, field3, field4"
table = "table"
conditions = "condition1=1 AND condition2=2"

sql = (f"SELECT {fields} "
       f"FROM {table} "
       f"WHERE {conditions};")

장점 :

  1. 비단뱀의 '잘 표로 만들어진'형식을 유지하지만 불필요한 공백 문자를 추가하지 않습니다 (로깅을 오염).
  2. 옵션 4의 백 슬래시 연속 추악함을 피하여 문을 추가하기 어렵게 만듭니다 (공백 블라인드는 말할 것도 없음).
  3. 또한 VIM에서 문을 확장하는 것은 정말 간단합니다 (커서를 삽입 지점에 놓고 SHIFT-O 를 눌러 새 줄을 엽니 다).

1
이 인쇄 인 경우에, 나는 더 나은 대안이 mutiline와 문자열로 작성하는 것입니다 생각 """하고 사용하기 textwrap.dedent()출력하기 전에
slezica

나는 그 옵션을 가지고 놀았지만 로그 출력도 여러 줄로 만들었습니다. db chatty 앱을 추적 할 때 이로 인해 방대한 출력이 발생했습니다.
user590028 2014-08-13

1
이것은 오래된 스레드이지만 모범 사례로이 형식을 사용해 왔지만 더 긴 쿼리로 지루해집니다
Jabda

8
"sql query"SQL 문자열 (작은 따옴표를 표준으로 사용)을 엉망으로 만드는 것을 피하기 위해 항상 큰 따옴표 를 사용해야하지 않습니까?
tpvasconcelos

19

SQL이 제대로 출력되도록 작성하는 많은 방법을 분명히 고려했지만, 원하지 않는 방식으로 SQL을 작성하는 대신 디버그 로깅에 사용하는 'print'문을 변경하는 것은 어떻습니까? 위에서 좋아하는 옵션을 사용하여 다음과 같은 로깅 기능은 어떻습니까?

def debugLogSQL(sql):
     print ' '.join([line.strip() for line in sql.splitlines()]).strip()

sql = """
    select field1, field2, field3, field4
    from table"""
if debug:
    debugLogSQL(sql)

이렇게하면 줄이 원하는 길이보다 길 경우 로깅 된 문자열을 여러 줄로 분할하는 추가 논리를 추가하는 것도 간단합니다.


11

내가 본 가장 깨끗한 방법은 SQL 스타일 가이드 에서 영감을 얻었습니다 .

sql = """
    SELECT field1, field2, field3, field4
      FROM table
     WHERE condition1 = 1
       AND condition2 = 2;
"""

기본적으로 절을 시작하는 키워드는 오른쪽 정렬되어야하고 필드 이름 등은 왼쪽 정렬되어야합니다. 이것은 매우 깔끔해 보이며 디버그하기도 더 쉽습니다.


2
sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1={} "
       "and condition2={}").format(1, 2)

Output: 'select field1, field2, field3, field4 from table 
         where condition1=1 and condition2=2'

condition의 값이 문자열이어야하는 경우 다음과 같이 할 수 있습니다.

sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1='{0}' "
       "and condition2='{1}'").format('2016-10-12', '2017-10-12')

Output: "select field1, field2, field3, field4 from table where
         condition1='2016-10-12' and condition2='2017-10-12'"

5
절대 이러지 마세요. SQL 인젝션이라고하는데 정말 위험합니다. 거의 모든 Python 데이터베이스 라이브러리는 매개 변수 사용을위한 기능을 제공합니다. format()SQL 문자열을 사용하여 자신을 포착 하면 주요 코드 냄새입니다.
mattmc3

나는 우리가 그것을 사용할 수 없다고 생각한다. 당신은 그것을 사용하기 전에 매개 변수를 검증해야하고, 당신이 무엇을 전달하는지 알아야한다.
pangpang

유효성 검사는 where condition1=:field1값을 매개 변수로 사용 하고 전달 하는 것보다 훨씬 더 오류가 발생하기 쉽습니다 . 을 사용하는 경우 SQL .format()에 팝업하는 방법이 ';DROP TABLE Users있습니다. 매개 변수를 올바르게 사용하는 방법은 PEP-249를 참조하십시오. python.org/dev/peps/pep-0249/#paramstyle
mattmc3

0

포맷을 완전히 피하려면 프로 시저 를 사용하는 것이 좋은 해결책이라고 생각 합니다 .

프로 시저를 호출하면 이 프로 시저에 넣을 쿼리결과를 얻을 수 있습니다 . 실제로 프로 시저 내에서 여러 쿼리를 처리 할 수 있습니다 . 호출은 마지막 으로 호출 된 쿼리 만 반환 합니다 .

MYSQL

DROP PROCEDURE IF EXISTS example;
 DELIMITER //
 CREATE PROCEDURE example()
   BEGIN
   SELECT 2+222+2222+222+222+2222+2222 AS this_is_a_really_long_string_test;
   END //
 DELIMITER;

#calling the procedure gives you the result of whatever query you want to put in this procedure. You can actually process multiple queries within a procedure. The call just returns the last query result
 call example;

파이썬

sql =('call example;')

-1

필드 이름을 배열 "필드"에 넣은 다음 다음을 수행 할 수 있습니다.


sql = 'select %s from table where condition1=1 and condition2=2' % (
 ', '.join(fields))

당신의 조건 목록이 커질 경우, 당신은 '과'.join (조건)를 사용하여 동일한 기능을 수행 할 수 있습니다
jcomeau_ictx

솔루션을 사용하면 Option_4보다 쿼리를 편집하기가 훨씬 더 어렵고 읽기도 어려울 것입니다.
ssoler

@ssoler, 그것은 일을하는 방법에 달려 있습니다. 내 프로그램에서 변수를 거의 선언하지 않고 대신 문자열 배열을 사용하므로 위와 같은 메서드 는 적어도 나에게는 매우 유용 하고 유지 관리 할 수 있습니다.
jcomeau_ictx 2011 년

-1

옵션 2를 고수하는 것이 좋습니다 (항상 더 복잡한 쿼리에 사용하고 있습니다 SELECT * FROM table). 좋은 방법으로 인쇄하려면 항상 별도의 모듈을 사용할 수 있습니다 .


-1

한 줄 또는 두 줄에 맞는 짧은 쿼리의 경우 위의 상위 투표 솔루션에서 문자열 리터럴 솔루션을 사용합니다. 더 긴 쿼리의 경우 .sql파일 로 나눕니다. 그런 다음 래퍼 함수를 ​​사용하여 파일을로드하고 다음과 같이 스크립트를 실행합니다.

script_cache = {}
def execute_script(cursor,script,*args,**kwargs):
    if not script in script_cache:
        with open(script,'r') as s:
            script_cache[script] = s
    return cursor.execute(script_cache[script],*args,**kwargs)

물론 이것은 종종 클래스 내부에 있기 때문에 일반적 cursor으로 명시 적으로 전달할 필요가 없습니다 . 나는 또한 일반적으로를 사용 codecs.open()하지만 이것은 일반적인 아이디어를 얻습니다. 그런 다음 SQL 스크립트는 자체 구문 강조와 함께 자체 파일에 완전히 포함됩니다.


-2
sql = """\
select field1, field2, field3, field4
from table
where condition1=1
and condition2=2
"""

[주석에 대한 응답으로 편집]
메소드 내에 SQL 문자열이 있다고해서 "표로 작성"해야한다는 의미는 아닙니다.

>>> class Foo:
...     def fubar(self):
...         sql = """\
... select *
... from frobozz
... where zorkmids > 10
... ;"""
...         print sql
...
>>> Foo().fubar()
select *
from frobozz
where zorkmids > 10
;
>>>

IMO 이것은 Option_2와 동일합니다
ssoler

@ssoler : Option_2에는 모든 행 에 선행 공백이 있습니다. 귀하의 예제는 앞의 선행 공백을 생략합니다 select. 내 대답에는 선행 공백이 없습니다. 그들이 똑같다는 의견을 형성하게 된 이유는 무엇입니까?
John Machin 2011 년

메서드 안에 SQL 문자열을 넣으면 모든 줄 (Option_2)을 표로 만들어야합니다. 이에 대한 한 가지 가능한 해결책은 Option_3입니다.
ssoler 2011 년

@ssoler : 죄송합니다, 그 말을 이해하지 못합니다. 업데이트 된 답변을 봐주세요.
John Machin 2011 년

업데이트 된 대답은 내 Option_3입니다. 이 옵션은 표로 작성된 코드의 명확성을 깨뜨리기 때문에 마음에 들지 않습니다.
ssoler 2011 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.