아래 예는 tiny_warehouse 웨어하우스, testdb 데이터베이스 및 testschema 스키마를 생성하는 방법을 보여줍니다. 스키마를 생성할 때는 반드시 스키마를 생성할 데이터베이스의 이름을 지정하거나 스키마를 생성할 데이터베이스에 이미 연결되어 있어야 함에 유의하십시오. 아래 예에서는 USEDATABASE 명령을 실행한 후 CREATESCHEMA 명령을 실행하여 올바른 데이터베이스에 스키마가 생성되도록 합니다.
conn.cursor().execute("CREATE WAREHOUSE IF NOT EXISTS tiny_warehouse_mg")conn.cursor().execute("CREATE DATABASE IF NOT EXISTS testdb_mg")conn.cursor().execute("USE DATABASE testdb_mg")conn.cursor().execute("CREATE SCHEMA IF NOT EXISTS testschema_mg")
외부 위치(즉, S3 버킷)에 이미 스테이징된 파일에서 테이블로 데이터를 로드하려면 COPY INTO <테이블> 명령을 사용합니다.
예:
# Copying Datacon.cursor().execute("""COPY INTO testtable FROM s3://<s3_bucket>/data/ STORAGE_INTEGRATION = myint FILE_FORMAT=(field_delimiter=',')""".format(aws_access_key_id=AWS_ACCESS_KEY_ID,aws_secret_access_key=AWS_SECRET_ACCESS_KEY))
여기서
s3://<s3_버킷>/data/ 은 S3 버킷의 이름을 지정합니다.
버킷의 파일에는 data 접두사가 사용됩니다.
버킷은 계정 관리자(즉, ACCOUNTADMIN 역할의 사용자) 또는 전역 CREATE INTEGRATION 권한이 있는 역할이 CREATE STORAGE INTEGRATION 을 사용하여 생성한 저장소 통합으로 액세스됩니다. 저장소 통합을 사용하면 사용자는 개인 저장소 위치에 액세스하기 위한 자격 증명을 입력하지 않아도 됩니다.
참고
이 예에서는 format() 함수를 사용하여 문을 구성합니다. 환경에 SQL 삽입 공격의 위험이 있는 경우에는 format() 함수를 사용하는 대신 값을 바인딩하는 것이 좋을 수 있습니다.
동기식 쿼리의 경우 진행 중인 모든 동기식 쿼리는 매개 변수 값에 관계없이 즉시 중단됩니다.
비동기 쿼리의 경우:
ABORT_DETACHED_QUERY가 FALSE 로 설정된 경우 진행 중인 비동기 쿼리가 정상적으로 종료될 때까지 계속 실행됩니다.
ABORT_DETACHED_QUERY가 TRUE 로 설정된 경우 5분 후에도 클라이언트 연결이 다시 설정되지 않으면 Snowflake는 진행 중인 모든 비동기 쿼리를 자동으로 중단합니다.
cursor.query_result(queryId) 를 호출하여 5분이 지나면 비동기 쿼리가 중단되는 것을 방지할 수 있습니다. 이 호출은 쿼리가 아직 실행 중이므로 실제 쿼리 결과를 검색하지는 않지만 쿼리가 취소되는 것을 방지합니다. query_result 호출은 동기 작업이므로 특정 사용 사례에 적합할 수도 있고 적합하지 않을 수도 있습니다.
이 기능을 사용하면 각 쿼리가 완료될 때까지 기다릴 필요 없이 여러 쿼리를 병렬로 제출할 수 있습니다. 또한, 동일한 세션 동안 동기 및 비동기 쿼리의 조합을 실행할 수도 있습니다.
참고
단일 쿼리에서 여러 개의 문을 실행하려면 세션에서 유효한 웨어하우스를 사용할 수 있어야 합니다.
마지막으로, 한 연결에서 비동기 쿼리를 제출하고 다른 연결에서 결과를 확인할 수 있습니다. 예를 들어, 사용자는 애플리케이션에서 장기 실행 쿼리를 시작하고 애플리케이션을 종료하며 나중에 결과를 확인할 수 있도록 애플리케이션을 다시 시작할 수 있습니다.
드라이버의 비즈니스 논리 계층 구조 및 ABORT_DETACHED_QUERY 매개 변수의 상호 작용에 대해 더 잘 이해하려면 다음 순서도를 참조하세요.
:codenowrap:`cursor.execute_async(query)`가 사용되는 경우 Snowflake Python 드라이버는 비동기적으로 제출된 쿼리를 자동으로 추적합니다. 연결이 :codenowrap:`connection.close()`로 명시적으로 종료된 경우 또는 컨텍스트 관리자가 :codenowrap:`connect()…`로 사용되는 경우, 비동기 쿼리 목록을 검사하고 그중 하나라도 여전히 실행 중이면 Snowflake 측 세션이 삭제되지 않습니다.
동일한 연결 내에서 실행 중인 비동기 쿼리가 없는 경우 연결에 속한 Snowflake 세션은 ``connection.close()``가 호출될 때 로그아웃되며, 이는 동일한 세션에서 실행 중인 다른 모든 쿼리를 암시적으로 취소합니다.
가장 좋은 방법은 장기 실행 중인 모든 비동기 작업(특히 연결이 닫힌 후에도 계속되도록 의도된 작업)을 별도의 연결로 분리하는 것입니다.
server_session_keep_alive`(기본값::codenowrap:`False) 연결 매개 변수를 사용하여 이 자동 동작을 재정의할 수 있습니다. 기본적으로 Snowflake 세션은 비동기 쿼리가 실행 중이 아닐 때 :codenowrap:`connection.close()`를 호출하는 경우에만 로그아웃됩니다. 기본 동작에서는 동기화 쿼리를 고려하거나 추적하지 않습니다.
:codenowrap:`server_session_keep_alive=True`인 경우, :codenowrap:`connection.close()`는 쿼리의 상태와 관계없이 Snowflake 세션을 로그아웃하지 않습니다. 장기 실행 비동기 쿼리를 실행하도록 설계된 연결의 경우 이 설정을 활성화하면 CPU 오버헤드를 줄이고 연결 종료 프로세스를 가속화합니다.
중요
이 매개 변수를 활성화하면 예기치 않은 청구 가능한 효과가 발생할 수 있습니다(예: :ref:`STATEMENT_TIMEOUT_IN_SECONDS<label-statement_timeout_in_seconds>`의 구성된 값까지 쿼리가 계속 실행될 수 있음). Snowflake는 :codenowrap:`server_session_keep_alive`의 값을 기본값에서 변경해야 하는지 신중하게 결정하고, 가능하면 프로덕션에서 변경 사항을 구현하기 전에 비프로덕션 환경에서 변경 사항을 철저히 테스트합니다.
conn=snowflake.connector.connect(...)cur=conn.cursor()# Submit an asynchronous query for execution.cur.execute_async('select count(*) from table(generator(timeLimit => 25))')
importtime...# Execute a long-running query asynchronously.cur.execute_async('select count(*) from table(generator(timeLimit => 25))')...# Wait for the query to finish running.query_id=cur.sfqidwhileconn.is_still_running(conn.get_query_status(query_id)):time.sleep(1)
다음 예에서는 쿼리 결과에 오류가 있는 경우 오류가 발생합니다.
fromsnowflake.connectorimportProgrammingErrorimporttime...# Wait for the query to finish running and raise an error# if a problem occurred with the execution of the query.try:query_id=cur.sfqidwhileconn.is_still_running(conn.get_query_status_throw_if_error(query_id)):time.sleep(1)exceptProgrammingErroraserr:print('Programming Error: {0}'.format(err))
예를 들어, 테이블 생성 및 데이터 삽입하기 에서 이전에 생성한 testtable 테이블에서 “col1” 및 “col2” 열을 가져오려면 다음과 유사한 코드를 사용합니다.
cur=conn.cursor()try:cur.execute("SELECT col1, col2 FROM test_table ORDER BY col1")for(col1,col2)incur:print('{0}, {1}'.format(col1,col2))finally:cur.close()
또는 Python용 Snowflake 커넥터가 편리한 바로 가기를 제공합니다.
for(col1,col2)incon.cursor().execute("SELECT col1, col2 FROM testtable"):print('{0}, {1}'.format(col1,col2))
단일 결과(즉, 단일 행)를 가져오려면 fetchone 메서드를 사용합니다.
col1,col2=con.cursor().execute("SELECT col1, col2 FROM testtable").fetchone()print('{0}, {1}'.format(col1,col2))
지정된 행의 개수를 한 번에 가져오려면 행의 개수와 함께 fetchmany 메서드를 사용합니다.
cur=con.cursor().execute("SELECT col1, col2 FROM testtable")ret=cur.fetchmany(3)print(ret)whilelen(ret)>0:ret=cur.fetchmany(3)print(ret)
참고
결과 세트가 너무 커 메모리에 적합하지 않은 경우에는 fetchone 또는 fetchmany 를 사용합니다.
모든 결과를 한 번에 가져오려면:
results=con.cursor().execute("SELECT col1, col2 FROM testtable").fetchall()forrecinresults:print('%s, %s'%(rec[0],rec[1]))
쿼리에 대한 시간 초과를 설정하려면 “begin” 명령을 실행하고 쿼리에 시간 초과 매개 변수를 포함합니다. 쿼리가 매개 변수 값의 길이를 초과하면 오류가 발생하고 롤백이 수행됩니다.
다음 코드에서 604 오류는 쿼리가 취소되었음을 의미합니다. 시간 초과 매개 변수를 통해 Timer() 가 시작되고 쿼리가 지정된 시간 내에 완료되지 않으면 취소됩니다.
conn.cursor().execute("create or replace table testtbl(a int, b string)")conn.cursor().execute("begin")try:conn.cursor().execute("insert into testtbl(a,b) values(3, 'test3'), (4,'test4')",timeout=10)# long queryexceptProgrammingErrorase:ife.errno==604:print("timeout")conn.cursor().execute("rollback")else:raiseeelse:conn.cursor().execute("commit")
열 이름을 기준으로 값을 가져오려면, DictCursor 타입의 cursor 오브젝트를 생성합니다.
예:
# Querying data by DictCursorfromsnowflake.connectorimportDictCursorcur=con.cursor(DictCursor)try:cur.execute("SELECT col1, col2 FROM testtable")forrecincur:print('{0}, {1}'.format(rec['COL1'],rec['COL2']))finally:cur.close()
fromsnowflake.connectorimportProgrammingErrorimporttimeconn=snowflake.connector.connect(...)cur=conn.cursor()# Submit an asynchronous query for execution.cur.execute_async('select count(*) from table(generator(timeLimit => 25))')# Retrieve the results.cur.get_results_from_sfqid(query_id)results=cur.fetchall()print(f'{results[0]}')
다음 예에서는 1개의 연결에서 비동기 쿼리를 제출하고 다른 연결에서 결과를 검색합니다.
fromsnowflake.connectorimportProgrammingErrorimporttimeconn=snowflake.connector.connect(...)cur=conn.cursor()# Submit an asynchronous query for execution.cur.execute_async('select count(*) from table(generator(timeLimit => 25))')# Get the query ID for the asynchronous query.query_id=cur.sfqid# Close the cursor and the connection.cur.close()conn.close()# Open a new connection.new_conn=snowflake.connector.connect(...)# Create a new cursor.new_cur=new_conn.cursor()# Retrieve the results.new_cur.get_results_from_sfqid(query_id)results=new_cur.fetchall()print(f'{results[0]}')
쿼리 성능을 향상하려면, snowflake.connector.converter_null 모듈의 SnowflakeNoConverterToPython 클래스를 사용하여 Snowflake 내부 데이터 타입에서 네이티브 Python 데이터 타입으로의 데이터 변환을 우회합니다. 예:
fromsnowflake.connector.converter_nullimportSnowflakeNoConverterToPythoncon=snowflake.connector.connect(...converter_class=SnowflakeNoConverterToPython)forrecincon.cursor().execute("SELECT * FROM large_table"):# rec includes raw Snowflake data
결과적으로, 모든 데이터는 문자열 형식으로 표시되어 애플리케이션이 네이티브 Python 데이터 타입으로 변환을 수행합니다. 예를 들어, TIMESTAMP_NTZ 및 TIMESTAMP_LTZ 데이터는 문자열 형식으로 표시되는 Epoch 시간이며 TIMESTAMP_TZ 데이터는 Epoch 시간 다음에 공백이 오고 그 다음에 UTC에 대한 오프셋(분)이 표시되는 문자열 형식입니다.
바인딩 데이터에는 영향을 주지 않으며, 여전히 Python 네이티브 데이터를 바인딩하여 업데이트에서 사용할 수 있습니다.
Snowflake Connector for Python 버전 3.14.0는 GET 명령으로 Snowflake 스테이지의 파일을 다운로드할 때 커넥터가 파일 권한을 설정하는 방법을 지정하는 unsafe_file_write 연결 매개 변수를 도입했습니다. 이러한 파일은 항상 Python 프로세스를 실행하는 동일한 사용자가 소유합니다.
기본적으로 unsafe_file_write 매개 변수는 False`로설정되어더욱안전하고엄격한codenowrap:`600 파일 권한을 제공합니다. 즉, 소유자에게만 다운로드한 파일의 읽기/쓰기 권한이 있습니다. 다른 그룹과 사용자에게는 GET 명령으로 다운로드한 파일에 대한 권한이 없습니다.
조직에서 파일에 대해 덜 제한적인 파일 권한이 필요한 경우 unsafe_file_write 매개 변수를 True 로 설정할 수 있습니다. 이 매개 변수를 활성화하면 스테이지에서 다운로드한 파일에 대한 파일 권한이 644 로 설정되어 소유자는 파일을 읽고 쓸 수 있지만 다른 사람은 읽기만 할 수 있습니다. 예를 들어 다운로드한 파일을 읽고 처리할 수 있어야 하는 다른 시스템 사용자 아래에서 실행되는 일부 ETL 도구의 경우 이 설정이 필요할 수 있습니다.
어떤 값을 사용해야 할지 잘 모르겠다면 조직의 해당 애플리케이션 보안 정책을 담당하는 팀에 문의하십시오.
pyformat 바인딩 및 format 바인딩 모두 서버측이 아닌 클라이언트측에서 데이터를 바인딩합니다.
기본적으로, Python용 Snowflake 커넥터는 pyformat 및 format 모두를 지원하므로, 사용자는 %(name)s 또는 %s 를 자리 표시자로 사용할 수 있습니다. 예:
%(name)s 를 자리 표시자로 사용:
conn.cursor().execute("INSERT INTO test_table(col1, col2) ""VALUES(%(col1)s, %(col2)s)",{'col1':789,'col2':'test string3',})
%s 를 자리 표시자로 사용:
con.cursor().execute("INSERT INTO testtable(col1, col2) ""VALUES(%s, %s)",(789,'test string3'))
pyformat 및 format 을 사용하면 목록 오브젝트를 사용하여 IN 연산자를 위해 데이터를 바인딩할 수 있습니다.
# Binding data for IN operatorcon.cursor().execute("SELECT col1, col2 FROM testtable"" WHERE col2 IN (%s)",(['test string1','test string3'],))
퍼센트 문자(“%”)는 SQL LIKE용 와일드카드 문자 및 Python용 형식 바인딩 문자로 사용할 수 있습니다. 형식 바인딩을 사용하고 SQL 명령에 퍼센트 문자가 포함된 경우에는 퍼센트 문자를 이스케이프해야 할 수 있습니다. 예를 들어, SQL 문이 다음과 같은 경우:
SELECTcol1,col2FROMtest_tableWHEREcol2ILIKE'%York'LIMIT1;-- Find York, New York, etc.
Python 코드는 다음과 같아야 합니다(원본 퍼센트 기호를 이스케이프하려면 추가 퍼센트 기호에 유의).
sql_command="select col1, col2 from test_table "sql_command+=" where col2 like '%%York' limit %(lim)s"parameter_dictionary={'lim':1}cur.execute(sql_command,parameter_dictionary)
qmark 바인딩 및 numeric 바인딩은 클라이언트측이 아닌 서버측에서 데이터를 바인딩합니다.
qmark 바인딩의 경우, 물음표 문자(?)를 사용하여 문자열에서 변수 값을 삽입할 위치를 나타냅니다.
numeric 바인딩의 경우, 콜론(:) 뒤에 숫자를 사용하여 해당 위치에 대체될 변수의 위치를 나타냅니다. 예를 들어, :2 는 두 번째 변수를 지정합니다.
숫자 바인딩을 사용하여 동일한 쿼리에서 동일한 값을 두 번 이상 바인딩합니다. 예를 들어, 두 번 이상 사용할 Long VARCHAR 또는 BINARY 또는 반정형 값이 있는 경우 numeric 바인딩을 사용하면 서버에 값을 한 번 전송하고 여러 번 사용할 수 있습니다.
qmark 또는 numeric 스타일 바인딩을 사용하려면 다음 중 하나를 실행하거나 connect() 를 호출할 때 연결 매개 변수의 일부로 paramstyle 을 설정하면 됩니다.
snowflake.connector.paramstyle='qmark'
snowflake.connector.paramstyle='numeric'
paramstyle 을 qmark 또는 numeric 을 설정한 경우에는, ? 또는 :N (여기서 N 을 숫자로 대체)을 각각 자리 표시자로 사용해야 합니다.
예:
? 를 자리 표시자로 사용:
fromsnowflake.connectorimportconnectconnection_parameters={'account':'xxxxx','user':'xxxx','password':'xxxxxx',"host":"xxxxxx","port":443,'protocol':'https','warehouse':'xxx','database':'xxx','schema':'xxx','paramstyle':'qmark'# note paramstyle setting here at connection level}con=connect(**connection_parameters)con.cursor().execute("INSERT INTO testtable2(col1,col2,col3) ""VALUES(?,?,?)",(987,'test string4',("TIMESTAMP_LTZ",datetime.now())))
:N 을 자리 표시자로 사용:
importsnowflake.connectorsnowflake.connector.paramstyle='numeric'con=snowflake.connector.connect(...)con.cursor().execute("INSERT INTO testtable(col1, col2) ""VALUES(:1, :2)",(789,'test string3'))
qmark 또는 numeric 바인딩을 사용하여 데이터를 Snowflake TIMESTAMP 데이터 타입으로 바인딩하는 경우, 바인딩 변수를 Snowflake 타임스탬프 데이터 타입(TIMESTAMP_LTZ 또는 TIMESTAMP_TZ) 및 값을 지정하는 튜플로 설정합니다. 예:
importsnowflake.connectorsnowflake.connector.paramstyle='qmark'con=snowflake.connector.connect(...)con.cursor().execute("CREATE OR REPLACE TABLE testtable2 ("" col1 int, "" col2 string, "" col3 timestamp_ltz"")")con.cursor().execute("INSERT INTO testtable2(col1,col2,col3) ""VALUES(?,?,?)",(987,'test string4',("TIMESTAMP_LTZ",datetime.now())))
클라이언트측 바인딩과 달리, 서버측 바인딩에는 열에 대한 Snowflake 데이터 타입이 필요합니다. 가장 일반적인 Python 데이터 타입에는 이미 Snowflake 데이터 타입에 대한 암시적 매핑(예: int 이 FIXED 로 매핑됨)이 있습니다. 그러나 Python datetime 데이터는 여러 Snowflake 데이터 타입 중 1개(TIMESTAMP_NTZ, TIMESTAMP_LTZ 또는 TIMESTAMP_TZ)에 바인딩될 수 있으며 기본 매핑은 TIMESTAMP_NTZ 이므로 사용자가 사용할 Snowflake 데이터 타입을 지정해야 합니다.
애플리케이션 코드에서는 단일 일괄 처리에 여러 행을 삽입할 수 있습니다. 이 작업을 수행하려면 INSERT 문에서 값에 대한 매개 변수를 사용하십시오. 예를 들어, 다음 문에서는 INSERT 문에서 qmark 바인딩을 위해 자리 표시자를 사용합니다.
insertintogrocery(item,quantity)values(?,?)
그리고 삽입할 데이터를 지정하려면 시퀀스의 시퀀스(예: 튜플 목록)인 변수를 정의합니다.
rows_to_insert=[('milk',2),('apple',3),('egg',2)]
위의 예에서와 같이, 목록의 각 항목은 삽입할 행에 대한 열 값이 포함된 튜플입니다.
바인딩을 실행하려면 executemany() 메서드를 호출하여 변수를 두 번째 인자로 전달합니다. 예:
conn=snowflake.connector.connect(...)rows_to_insert=[('milk',2),('apple',3),('egg',2)]conn.cursor().executemany("insert into grocery (item, quantity) values (?, ?)",rows_to_insert)
서버측에서 데이터를 바인딩 (즉, qmark 또는 numeric 바인딩)하는 경우, 커넥터는 바인딩을 통해 일괄 삽입의 성능을 최적화할 수 있습니다.
이러한 방식을 사용하여 값을 대량으로 삽입하는 경우 드라이버는 수집을 위한 임시 스테이지로 데이터를 스트리밍하여(로컬 시스템에 파일을 생성하지 않음) 성능을 향상할 수 있습니다. 값의 개수가 임계값을 초과하는 경우 드라이버는 자동으로 이 작업을 수행합니다.
또한, 세션의 현재 데이터베이스 및 스키마를 설정해야 합니다. 이러한 값이 설정되지 않은 경우에는 드라이버가 실행하는 CREATE TEMPORARY STAGE 명령에서 다음 오류가 발생하며 실패할 수 있습니다.
CREATE TEMPORARY STAGE SYSTEM$BIND file_format=(type=csv field_optionally_enclosed_by='"')
Cannot perform CREATE STAGE. This session does not have a current schema. Call 'USE SCHEMA', or use a qualified name.
참고
Snowflake 데이터베이스에 데이터를 로드하는 대체 방법(COPY 명령을 사용한 대량 로드 등)과 관련해서는 Snowflake에 데이터 로드하기 를 참조하십시오.
결과 세트에서 각 열에 대한 메타데이터(예: 각 열의 이름, 타입, 전체 자릿수, 소수 자릿수 등)를 검색하려면 다음 방식 중 하나를 사용하십시오.
쿼리를 실행하기 위해 execute() 메서드를 호출한 후 메타데이터에 액세스하려면 Cursor 오브젝트의 describe 속성을 사용합니다.
쿼리를 실행할 필요 없이 메타데이터에 액세스하려면 describe() 메서드를 호출합니다.
describe 메서드는 Python용 Snowflake 커넥터 2.4.6 이상 버전에서 사용할 수 있습니다.
description 속성은 다음 값 중 1개로 설정됩니다.
2.4.5 이하 버전: 튜플의 목록.
2.4.6 이상 버전:ResultMetadata 오브젝트의 목록. (describe 메서드도 이 목록을 반환합니다.)
각 튜플 및 ResultMetadata 오브젝트에는 열에 대한 메타데이터(열 이름, 데이터 타입 등)가 포함되어 있습니다. 메타데이터에는 인덱스를 사용하여 또는 2.4.6 이상 버전의 경우 ResultMetadata 속성을 사용하여 액세스할 수 있습니다.
다음 예는 반환된 튜플 및 ResultMetadata 오브젝트에서 메타데이터에 액세스하는 방법을 보여줍니다.
예: 색인을 사용하여 열 이름 메타데이터 가져오기(2.4.5 및 이전 버전):
다음 예에서는 description 속성을 사용하여 쿼리를 실행한 후 열 이름 목록을 검색합니다. 이 속성은 튜플의 목록으로, 이 예에서는 각 튜플의 첫 번째 값에서 열 이름에 액세스합니다.
cur=conn.cursor()cur.execute("SELECT * FROM test_table")print(','.join([col[0]forcolincur.description]))
예: 속성을 사용하여 열 이름 메타데이터 가져오기(2.4.6 이상 버전):
다음 예에서는 description 속성을 사용하여 쿼리를 실행한 후 열 이름 목록을 검색합니다. 이 속성은 ResultMetaData 오브젝트의 목록으로, 이 예에서는 각 ResultMetadata 오브젝트의 name 속성에서 열 이름에 액세스합니다.
cur=conn.cursor()cur.execute("SELECT * FROM test_table")print(','.join([col.nameforcolincur.description]))
예: 쿼리를 실행하지 않고 열 이름 메타데이터 가져오기(2.4.6 이상 버전):
다음 예에서는 describe 메서드를 사용하여 쿼리를 실행하지 않고 열 이름 목록을 검색합니다. describe() 메서드는 ResultMetaData 오브젝트의 목록을 반환하는데, 이 예에서는 각 ResultMetadata 오브젝트의 name 속성에서 열 이름에 액세스합니다.
cur=conn.cursor()result_metadata_list=cur.describe("SELECT * FROM test_table")print(','.join([col.nameforcolinresult_metadata_list]))
이를 통해 수집된 클라이언트 메트릭을 서버로 제출하고 세션을 삭제할 수 있습니다. 또한, try-finally 를 사용하면 중간에 예외가 발생하는 경우에도 연결이 종료되지 않도록 할 수 있습니다.
# Connecting to Snowflakecon=snowflake.connector.connect(...)try:# Running queriescon.cursor().execute(...)...finally:# Closing the connectioncon.close()
조심
닫히지 않은 연결이 여러 개 있으면 시스템 리소스가 고갈되어 결국 애플리케이션 충돌이 발생할 수 있습니다.
Python용 Snowflake 커넥터는 필요한 경우 리소스를 할당 및 해제하는 컨텍스트 관리자를 지원합니다. 컨텍스트 관리자는 autocommit 이 비활성화된 경우 문의 상태에 따라 트랜잭션을 커밋 또는 롤백하는 데 유용합니다.
# Connecting to Snowflake using the context managerwithsnowflake.connector.connect(user=USER,password=PASSWORD,account=ACCOUNT,autocommit=False,)ascon:con.cursor().execute("INSERT INTO a VALUES(1, 'test1')")con.cursor().execute("INSERT INTO a VALUES(2, 'test2')")con.cursor().execute("INSERT INTO a VALUES(not numeric value, 'test3')")# fail
위의 예에서, 세 번째 문이 실패하면 컨텍스트 관리자가 트랜잭션의 변경 사항을 롤백하고 연결을 끊습니다. 모든 문이 성공하면 컨텍스트 관리자가 변경 사항을 커밋하고 연결을 끊습니다.
try 및 except 블록에 해당하는 코드는 다음과 같습니다.
# Connecting to Snowflake using try and except blockscon=snowflake.connector.connect(user=USER,password=PASSWORD,account=ACCOUNT,autocommit=False)try:con.cursor().execute("INSERT INTO a VALUES(1, 'test1')")con.cursor().execute("INSERT INTO a VALUES(2, 'test2')")con.cursor().execute("INSERT INTO a VALUES(not numeric value, 'test3')")# failcon.commit()exceptExceptionase:con.rollback()raiseefinally:con.close()
VECTOR 데이터 타입 에 대한 지원은 버전 3.6.0의 Snowflake Python Connector에 도입되었습니다. VECTOR 데이터 타입을 벡터 유사성 함수 와 함께 사용하여 벡터 검색 또는 RAG (retrieval-augmented-generation)를 기반으로 하는 애플리케이션을 구현할 수 있습니다.
다음 코드 예제는 Python 커넥터를 사용하여 VECTOR 열이 있는 테이블을 생성하고 VECTOR_INNER_PRODUCT 함수를 호출하는 방법을 보여줍니다.
importsnowflake.connectorconn=...# Set up connectioncur=conn.cursor()# Create a table and insert some vectorscur.execute("CREATE OR REPLACE TABLE vectors (a VECTOR(FLOAT, 3), b VECTOR(FLOAT, 3))")values=[([1.1,2.2,3],[1,1,1]),([1,2.2,3],[4,6,8])]forrowinvalues:cur.execute(f""" INSERT INTO vectors(a, b) SELECT {row[0]}::VECTOR(FLOAT,3), {row[1]}::VECTOR(FLOAT,3) """)# Compute the pairwise inner product between columns a and bcur.execute("SELECT VECTOR_INNER_PRODUCT(a, b) FROM vectors")print(cur.fetchall())
[(6.30...,), (41.2...,)]
다음 코드 예제에서는 Python Connector를 사용하여 [1,2,3] 에 가장 가까운 벡터를 찾기 위해 VECTOR_COSINE_SIMILARITY 를 호출하는 방법을 보여줍니다.
cur.execute(f""" SELECT a, VECTOR_COSINE_SIMILARITY(a, {[1,2,3]}::VECTOR(FLOAT, 3)) AS similarity FROM vectors ORDER BY similarity DESC LIMIT 1;""")print(cur.fetchall())
Python용 Snowflake 커넥터는 표준 Python logging 모듈을 사용하여 일정 간격으로 상태를 기록함으로써 애플리케이션이 백그라운드로 실행되는 활동을 추적할 수 있도록 해줍니다. 로깅을 활성화하는 가장 간단한 방법은 애플리케이션을 시작할 때 logging.basicConfig() 를 호출하는 것입니다.
예를 들어, 로깅 수준을 INFO 로 설정하고 /tmp/snowflake_python_connector.log 파일에 로그를 저장하려면:
보다 포괄적인 로깅은 다음과 같이 로깅 수준을 DEBUG 로 설정하여 활성화할 수 있습니다.
# Logging including the timestamp, thread and the source code locationimportloggingforlogger_namein['snowflake.connector','botocore','boto3']:logger=logging.getLogger(logger_name)logger.setLevel(logging.DEBUG)ch=logging.FileHandler('/tmp/python_connector.log')ch.setLevel(logging.DEBUG)ch.setFormatter(logging.Formatter('%(asctime)s - %(threadName)s%(filename)s:%(lineno)d - %(funcName)s() - %(levelname)s - %(message)s'))logger.addHandler(ch)
선택 사항이지만 권장되는 SecretDetector 포맷터 클래스를 사용하면 알려진 민감한 정보 세트를 마스킹한 후 Snowflake Python Connector 로그 파일에 작성할 수 있습니다. SecretDetector를 사용하려면, 다음과 유사한 코드를 사용합니다.
# Logging including the timestamp, thread and the source code locationimportloggingfromsnowflake.connector.secret_detectorimportSecretDetectorforlogger_namein['snowflake.connector','botocore','boto3']:logger=logging.getLogger(logger_name)logger.setLevel(logging.DEBUG)ch=logging.FileHandler('/tmp/python_connector.log')ch.setLevel(logging.DEBUG)ch.setFormatter(SecretDetector('%(asctime)s - %(threadName)s%(filename)s:%(lineno)d - %(funcName)s() - %(levelname)s - %(message)s'))logger.addHandler(ch)
참고
botocore 및 boto3 는 Python용 AWS(Amazon Web Services) SDK를 통해 사용할 수 있습니다.
연결 정보가 포함된 명령줄 인자(예: “–warehouse MyWarehouse”) 읽기.
서버에 연결하기.
웨어하우스, 데이터베이스 및 스키마 만들기 및 사용하기.
사용 완료 시 스키마, 데이터베이스 및 웨어하우스 삭제하기.
importloggingimportosimportsys# -- (> ---------------------- SECTION=import_connector ---------------------importsnowflake.connector# -- <) ---------------------------- END_SECTION ----------------------------classpython_veritas_base:""" PURPOSE: This is the Base/Parent class for programs that use the Snowflake Connector for Python. This class is intended primarily for: * Sample programs, e.g. in the documentation. * Tests. """def__init__(self,p_log_file_name=None):""" PURPOSE: This does any required initialization steps, which in this class is basically just turning on logging. """file_name=p_log_file_nameiffile_nameisNone:file_name='/tmp/snowflake_python_connector.log'# -- (> ---------- SECTION=begin_logging -----------------------------logging.basicConfig(filename=file_name,level=logging.INFO)# -- <) ---------- END_SECTION ---------------------------------------# -- (> ---------------------------- SECTION=main ------------------------defmain(self,argv):""" PURPOSE: Most tests follow the same basic pattern in this main() method: * Create a connection. * Set up, e.g. use (or create and use) the warehouse, database, and schema. * Run the queries (or do the other tasks, e.g. load data). * Clean up. In this test/demo, we drop the warehouse, database, and schema. In a customer scenario, you'd typically clean up temporary tables, etc., but wouldn't drop your database. * Close the connection. """# Read the connection parameters (e.g. user ID) from the command line# and environment variables, then connect to Snowflake.connection=self.create_connection(argv)# Set up anything we need (e.g. a separate schema for the test/demo).self.set_up(connection)# Do the "real work", for example, create a table, insert rows, SELECT# from the table, etc.self.do_the_real_work(connection)# Clean up. In this case, we drop the temporary warehouse, database, and# schema.self.clean_up(connection)print("\nClosing connection...")# -- (> ------------------- SECTION=close_connection -----------------connection.close()# -- <) ---------------------------- END_SECTION ---------------------# -- <) ---------------------------- END_SECTION=main --------------------defargs_to_properties(self,args):""" PURPOSE: Read the command-line arguments and store them in a dictionary. Command-line arguments should come in pairs, e.g.: "--user MyUser" INPUTS: The command line arguments (sys.argv). RETURNS: Returns the dictionary. DESIRABLE ENHANCEMENTS: Improve error detection and handling. """connection_parameters={}i=1whilei<len(args)-1:property_name=args[i]# Strip off the leading "--" from the tag, e.g. from "--user".property_name=property_name[2:]property_value=args[i+1]connection_parameters[property_name]=property_valuei+=2returnconnection_parametersdefcreate_connection(self,argv):""" PURPOSE: This gets account identifier and login information from the environment variables and command-line parameters, connects to the server, and returns the connection object. INPUTS: argv: This is usually sys.argv, which contains the command-line parameters. It could be an equivalent substitute if you get the parameter information from another source. RETURNS: A connection. """# Get account identifier and login information from environment variables and command-line parameters.# For information about account identifiers, see# https://docs.snowflake.com/en/user-guide/admin-account-identifier.html .# -- (> ----------------------- SECTION=set_login_info ---------------# Get the password from an appropriate environment variable, if# available.PASSWORD=os.getenv('SNOWSQL_PWD')# Get the other login info etc. from the command line.iflen(argv)<11:msg="ERROR: Please pass the following command-line parameters:\n"msg+="--warehouse <warehouse> --database <db> --schema <schema> "msg+="--user <user> --account <account_identifier> "print(msg)sys.exit(-1)else:connection_parameters=self.args_to_properties(argv)USER=connection_parameters["user"]ACCOUNT=connection_parameters["account"]WAREHOUSE=connection_parameters["warehouse"]DATABASE=connection_parameters["database"]SCHEMA=connection_parameters["schema"]# Optional: for internal testing only.try:PORT=connection_parameters["port"]except:PORT=""try:PROTOCOL=connection_parameters["protocol"]except:PROTOCOL=""# If the password is set by both command line and env var, the# command-line value takes precedence over (is written over) the# env var value.# If the password wasn't set either in the environment var or on# the command line...ifPASSWORDisNoneorPASSWORD=='':print("ERROR: Set password, e.g. with SNOWSQL_PWD environment variable")sys.exit(-2)# -- <) ---------------------------- END_SECTION ---------------------# Optional diagnostic:#print("USER:", USER)#print("ACCOUNT:", ACCOUNT)#print("WAREHOUSE:", WAREHOUSE)#print("DATABASE:", DATABASE)#print("SCHEMA:", SCHEMA)#print("PASSWORD:", PASSWORD)#print("PROTOCOL:" "'" + PROTOCOL + "'")#print("PORT:" + "'" + PORT + "'")print("Connecting...")# If the PORT is set but the protocol is not, we ignore the PORT (bug!!).ifPROTOCOLisNoneorPROTOCOL==""orPORTisNoneorPORT=="":# -- (> ------------------- SECTION=connect_to_snowflake ---------conn=snowflake.connector.connect(user=USER,password=PASSWORD,account=ACCOUNT,warehouse=WAREHOUSE,database=DATABASE,schema=SCHEMA)# -- <) ---------------------------- END_SECTION -----------------else:conn=snowflake.connector.connect(user=USER,password=PASSWORD,account=ACCOUNT,warehouse=WAREHOUSE,database=DATABASE,schema=SCHEMA,# Optional: for internal testing only.protocol=PROTOCOL,port=PORT)returnconndefset_up(self,connection):""" PURPOSE: Set up to run a test. You can override this method with one appropriate to your test/demo. """# Create a temporary warehouse, database, and schema.self.create_warehouse_database_and_schema(connection)defdo_the_real_work(self,conn):""" PURPOSE: Your sub-class should override this to include the code required for your documentation sample or your test case. This default method does a very simple self-test that shows that the connection was successful. """# Create a cursor for this connection.cursor1=conn.cursor()# This is an example of an SQL statement we might want to run.command="SELECT PI()"# Run the statement.cursor1.execute(command)# Get the results (should be only one):forrowincursor1:print(row[0])# Close this cursor.cursor1.close()defclean_up(self,connection):""" PURPOSE: Clean up after a test. You can override this method with one appropriate to your test/demo. """# Create a temporary warehouse, database, and schema.self.drop_warehouse_database_and_schema(connection)defcreate_warehouse_database_and_schema(self,conn):""" PURPOSE: Create the temporary schema, database, and warehouse that we use for most tests/demos. """# Create a database, schema, and warehouse if they don't already exist.print("\nCreating warehouse, database, schema...")# -- (> ------------- SECTION=create_warehouse_database_schema -------conn.cursor().execute("CREATE WAREHOUSE IF NOT EXISTS tiny_warehouse_mg")conn.cursor().execute("CREATE DATABASE IF NOT EXISTS testdb_mg")conn.cursor().execute("USE DATABASE testdb_mg")conn.cursor().execute("CREATE SCHEMA IF NOT EXISTS testschema_mg")# -- <) ---------------------------- END_SECTION ---------------------# -- (> --------------- SECTION=use_warehouse_database_schema --------conn.cursor().execute("USE WAREHOUSE tiny_warehouse_mg")conn.cursor().execute("USE DATABASE testdb_mg")conn.cursor().execute("USE SCHEMA testdb_mg.testschema_mg")# -- <) ---------------------------- END_SECTION ---------------------defdrop_warehouse_database_and_schema(self,conn):""" PURPOSE: Drop the temporary schema, database, and warehouse that we create for most tests/demos. """# -- (> ------------- SECTION=drop_warehouse_database_schema ---------conn.cursor().execute("DROP SCHEMA IF EXISTS testschema_mg")conn.cursor().execute("DROP DATABASE IF EXISTS testdb_mg")conn.cursor().execute("DROP WAREHOUSE IF EXISTS tiny_warehouse_mg")# -- <) ---------------------------- END_SECTION ---------------------# ----------------------------------------------------------------------------if__name__=='__main__':pvb=python_veritas_base()pvb.main(sys.argv)
코드 샘플의 두 번째 부품에서는 테이블을 생성하고 테이블에 행을 삽입하는 등의 작업을 수행합니다.
importsys# -- (> ---------------------- SECTION=import_connector ---------------------importsnowflake.connector# -- <) ---------------------------- END_SECTION ----------------------------# Import the base class that contains methods used in many tests and code # examples.frompython_veritas_baseimportpython_veritas_baseclasspython_connector_example(python_veritas_base):""" PURPOSE: This is a simple example program that shows how to use the Snowflake Python Connector to create and query a table. """def__init__(self):passdefdo_the_real_work(self,conn):""" INPUTS: conn is a Connection object returned from snowflake.connector.connect(). """print("\nCreating table test_table...")# -- (> ----------------------- SECTION=create_table ---------------------conn.cursor().execute("CREATE OR REPLACE TABLE ""test_table(col1 integer, col2 string)")conn.cursor().execute("INSERT INTO test_table(col1, col2) VALUES "+" (123, 'test string1'), "+" (456, 'test string2')")# -- <) ---------------------------- END_SECTION -------------------------print("\nSelecting from test_table...")# -- (> ----------------------- SECTION=querying_data --------------------cur=conn.cursor()try:cur.execute("SELECT col1, col2 FROM test_table ORDER BY col1")for(col1,col2)incur:print('{0}, {1}'.format(col1,col2))finally:cur.close()# -- <) ---------------------------- END_SECTION -------------------------# ============================================================================if__name__=='__main__':test_case=python_connector_example()test_case.main(sys.argv)
이 샘플을 실행하려면 다음을 수행해야 합니다.
코드의 첫 번째 부분을 “python_veritas_base.py” 파일로 복사합니다.
코드의 두 번째 부분을 “python_connector_example.py” 파일로 복사합니다.
SNOWSQL_PWD 환경 변수를 비밀번호에 설정합니다. 예:
export SNOWSQL_PWD='MyPassword'
다음과 유사한 명령줄을 사용하여 프로그램을 실행합니다(사용자 및 계정 정보를 사용자 및 계정 정보로 바꿔야 함).
경고
이 작업을 수행하면 프로그램의 마지막에 웨어하우스, 데이터베이스 및 스키마가 삭제됩니다! 손실될 수 있으므로 기존 데이터베이스의 이름을 사용하지 마십시오.
계정 및 로그인 정보를 설정한 섹션에서 Snowflake 로그인 정보(이름, 비밀번호 등)와 일치하도록 변수를 바꿨는지 확인하십시오.
이 예에서는 format() 함수를 사용하여 문을 구성합니다. 환경에 SQL 삽입 공격의 위험이 있는 경우에는 format() 함수를 사용하는 대신 값을 바인딩하는 것이 좋을 수 있습니다.
#!/usr/bin/env python## Snowflake Connector for Python Sample Program## Loggingimportlogginglogging.basicConfig(filename='/tmp/snowflake_python_connector.log',level=logging.INFO)importsnowflake.connector# Set ACCOUNT to your account identifier.# See https://docs.snowflake.com/en/user-guide/gen-conn-config.ACCOUNT='<my_organization>-<my_account>'# Set your login information.USER='<login_name>'PASSWORD='<password>'importos# Only required if you copy data from your S3 bucketAWS_ACCESS_KEY_ID=os.getenv('AWS_ACCESS_KEY_ID')AWS_SECRET_ACCESS_KEY=os.getenv('AWS_SECRET_ACCESS_KEY')# Connecting to Snowflakecon=snowflake.connector.connect(user=USER,password=PASSWORD,account=ACCOUNT,)# Creating a database, schema, and warehouse if none existscon.cursor().execute("CREATE WAREHOUSE IF NOT EXISTS tiny_warehouse")con.cursor().execute("CREATE DATABASE IF NOT EXISTS testdb")con.cursor().execute("USE DATABASE testdb")con.cursor().execute("CREATE SCHEMA IF NOT EXISTS testschema")# Using the database, schema and warehousecon.cursor().execute("USE WAREHOUSE tiny_warehouse")con.cursor().execute("USE SCHEMA testdb.testschema")# Creating a table and inserting datacon.cursor().execute("CREATE OR REPLACE TABLE ""testtable(col1 integer, col2 string)")con.cursor().execute("INSERT INTO testtable(col1, col2) ""VALUES(123, 'test string1'),(456, 'test string2')")# Copying data from internal stage (for testtable table)con.cursor().execute("PUT file:///tmp/data0/file* @%testtable")con.cursor().execute("COPY INTO testtable")# Copying data from external stage (S3 bucket -# replace <s3_bucket> with the name of your bucket)con.cursor().execute("""COPY INTO testtable FROM s3://<s3_bucket>/data/ STORAGE_INTEGRATION = myint FILE_FORMAT=(field_delimiter=',')""".format(aws_access_key_id=AWS_ACCESS_KEY_ID,aws_secret_access_key=AWS_SECRET_ACCESS_KEY))# Querying datacur=con.cursor()try:cur.execute("SELECT col1, col2 FROM testtable")for(col1,col2)incur:print('{0}, {1}'.format(col1,col2))finally:cur.close()# Binding datacon.cursor().execute("INSERT INTO testtable(col1, col2) ""VALUES(%(col1)s, %(col2)s)",{'col1':789,'col2':'test string3',})# Retrieving column namescur=con.cursor()cur.execute("SELECT * FROM testtable")print(','.join([col[0]forcolincur.description]))# Catching syntax errorscur=con.cursor()try:cur.execute("SELECT * FROM testtable")exceptsnowflake.connector.errors.ProgrammingErrorase:# default error messageprint(e)# user error messageprint('Error {0} ({1}): {2} ({3})'.format(e.errno,e.sqlstate,e.msg,e.sfqid))finally:cur.close()# Retrieving the Snowflake query IDcur=con.cursor()cur.execute("SELECT * FROM testtable")print(cur.sfqid)# Closing the connectioncon.close()