Python Connector ์‚ฌ์šฉํ•˜๊ธฐยถ

์ด ํ•ญ๋ชฉ์—์„œ๋Š” Snowflake ์ปค๋„ฅํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ํ…Œ์ด๋ธ” ์ƒ์„ฑ, ์›จ์–ดํ•˜์šฐ์Šค ์ƒ์„ฑ, ๋ฐ์ดํ„ฐ ์‚ฝ์ž…/๋กœ๋“œ ๋ฐ ์ฟผ๋ฆฌ ๋“ฑ์˜ ํ‘œ์ค€ Snowflake ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ฃผ๋Š” ์ผ๋ จ์˜ ์˜ˆ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด ํ•ญ๋ชฉ์˜ ๋งˆ์ง€๋ง‰์— ์ œ๊ณต๋˜๋Š” ์ƒ˜ํ”Œ ์ฝ”๋“œ๋Š” ์˜ˆ์‹œ๋ฅผ ์ž‘๋™ํ•˜๋Š” ๋‹จ์ผ Python ํ”„๋กœ๊ทธ๋žจ์— ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

์ด์ œ Snowflake๋Š” SQL์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์Šคํ‚ค๋งˆ, ํ…Œ์ด๋ธ”, ์ž‘์—…, ์›จ์–ดํ•˜์šฐ์Šค๋ฅผ ํฌํ•จํ•œ ํ•ต์‹ฌ Snowflake ๋ฆฌ์†Œ์Šค๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ผ๊ธ‰ Python API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Snowflake Python APIs: Python์œผ๋กœ Snowflake ์˜ค๋ธŒ์ ํŠธ ๊ด€๋ฆฌํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ด ํ•ญ๋ชฉ์˜ ๋‚ด์šฉ:

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์Šคํ‚ค๋งˆ ๋ฐ ์›จ์–ดํ•˜์šฐ์Šค ๋งŒ๋“ค๊ธฐยถ

๋กœ๊ทธ์ธํ•œ ํ›„, ์•„์ง ์—†๋Š” ๊ฒฝ์šฐ CREATE DATABASE, CREATE SCHEMA ๋ฐ CREATE WAREHOUSE ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์Šคํ‚ค๋งˆ ๋ฐ ์›จ์–ดํ•˜์šฐ์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ์˜ˆ๋Š” tiny_warehouse ์›จ์–ดํ•˜์šฐ์Šค, testdb ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ testschema ์Šคํ‚ค๋งˆ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์Šคํ‚ค๋งˆ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ์Šคํ‚ค๋งˆ๋ฅผ ์ƒ์„ฑํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•˜๊ฑฐ๋‚˜ ์Šคํ‚ค๋งˆ๋ฅผ ์ƒ์„ฑํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ด๋ฏธ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์–ด์•ผ ํ•จ์— ์œ ์˜ํ•˜์‹ญ์‹œ์˜ค. ์•„๋ž˜ ์˜ˆ์—์„œ๋Š” USE DATABASE ๋ช…๋ น์„ ์‹คํ–‰ํ•œ ํ›„ CREATE 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")
Copy

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์Šคํ‚ค๋งˆ ๋ฐ ์›จ์–ดํ•˜์šฐ์Šค ์‚ฌ์šฉํ•˜๊ธฐยถ

ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ์Šคํ‚ค๋งˆ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, DML ๋ฌธ ๋ฐ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณตํ•  ์›จ์–ดํ•˜์šฐ์Šค๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, testdb ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, testschema ์Šคํ‚ค๋งˆ ๋ฐ tiny_warehouse ์›จ์–ดํ•˜์šฐ์Šค(์ด์ „์— ์ƒ์„ฑํ•œ)๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด:

conn.cursor().execute("USE WAREHOUSE tiny_warehouse_mg")
conn.cursor().execute("USE DATABASE testdb_mg")
conn.cursor().execute("USE SCHEMA testdb_mg.testschema_mg")
Copy

ํ…Œ์ด๋ธ” ์ƒ์„ฑ ๋ฐ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…ํ•˜๊ธฐยถ

CREATE TABLE ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๊ณ  INSERT ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ…Œ์ด๋ธ”์— ์ฑ„์›๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ด๋ฆ„์ด testtable ์ธ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๊ณ  ์ด ํ…Œ์ด๋ธ”์— ํ–‰์„ 2๊ฐœ ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค.

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')")
Copy

๋ฐ์ดํ„ฐ ๋กœ๋”ฉํ•˜๊ธฐยถ

๊ฐœ๋ณ„ INSERT ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ๋Œ€์‹ , ๋‚ด๋ถ€ ๋˜๋Š” ์™ธ๋ถ€ ์œ„์น˜์— ์Šคํ…Œ์ด์ง•๋œ ํŒŒ์ผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๊ด„์ ์œผ๋กœ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๋ถ€ ์œ„์น˜์—์„œ ๋ฐ์ดํ„ฐ ๋ณต์‚ฌํ•˜๊ธฐยถ

ํ˜ธ์ŠคํŠธ ์ปดํ“จํ„ฐ์˜ ํŒŒ์ผ์—์„œ ํ…Œ์ด๋ธ”๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋ ค๋ฉด, ์šฐ์„  PUT ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด๋ถ€ ์œ„์น˜์˜ ํŒŒ์ผ์„ ์Šคํ…Œ์ด์ง•ํ•œ ํ›„ COPY INTO <ํ…Œ์ด๋ธ”> ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ…Œ์ด๋ธ”๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ:

# Putting Data
con.cursor().execute("PUT file:///tmp/data/file* @%testtable")
con.cursor().execute("COPY INTO testtable")
Copy

์—ฌ๊ธฐ์„œ CSV ๋ฐ์ดํ„ฐ๋Š” Linux ๋˜๋Š” macOS ํ™˜๊ฒฝ์˜ ๋กœ์ปฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์ธ /tmp/data ์— ์ €์žฅ๋˜๋ฉฐ ์ด ๋””๋ ‰ํ„ฐ๋ฆฌ์—๋Š” ์ด๋ฆ„์ด file0, file1, โ€ฆ file100 ์ธ ํŒŒ์ผ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์œ„์น˜์—์„œ ๋ฐ์ดํ„ฐ ๋ณต์‚ฌํ•˜๊ธฐยถ

์™ธ๋ถ€ ์œ„์น˜(์ฆ‰, S3 ๋ฒ„ํ‚ท)์— ์ด๋ฏธ ์Šคํ…Œ์ด์ง•๋œ ํŒŒ์ผ์—์„œ ํ…Œ์ด๋ธ”๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋ ค๋ฉด COPY INTO <ํ…Œ์ด๋ธ”> ๋ช…๋ น์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ:

# Copying Data
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))
Copy

์—ฌ๊ธฐ์„œ

  • s3://<s3_๋ฒ„ํ‚ท>/data/ ์€ S3 ๋ฒ„ํ‚ท์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฒ„ํ‚ท์˜ ํŒŒ์ผ์—๋Š” data ์ ‘๋‘์‚ฌ๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • ๋ฒ„ํ‚ท์€ ๊ณ„์ • ๊ด€๋ฆฌ์ž(์ฆ‰, ACCOUNTADMIN ์—ญํ• ์˜ ์‚ฌ์šฉ์ž) ๋˜๋Š” ์ „์—ญ CREATE INTEGRATION ๊ถŒํ•œ์ด ์žˆ๋Š” ์—ญํ• ์ด CREATE STORAGE INTEGRATION ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•œ ์ €์žฅ์†Œ ํ†ตํ•ฉ์œผ๋กœ ์•ก์„ธ์Šค๋ฉ๋‹ˆ๋‹ค. ์ €์žฅ์†Œ ํ†ตํ•ฉ์„ ์‚ฌ์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž๋Š” ๊ฐœ์ธ ์ €์žฅ์†Œ ์œ„์น˜์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•œ ์ž๊ฒฉ ์ฆ๋ช…์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

์ด ์˜ˆ์—์„œ๋Š” format() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ํ™˜๊ฒฝ์— SQL ์‚ฝ์ž… ๊ณต๊ฒฉ์˜ ์œ„ํ—˜์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” format() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ๊ฐ’์„ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ์ฟผ๋ฆฌํ•˜๊ธฐยถ

Python์šฉ Snowflake ์ปค๋„ฅํ„ฐ๋ฅผ ํ†ตํ•ด ๋‹ค์Œ์„ ์ œ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋™๊ธฐ ์ฟผ๋ฆฌ, ์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ œ์–ด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

  • ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ, ์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ ์ „ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ œ์–ด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„, Cursor ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ์˜ ๋ชจ๋“  ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ, Python์šฉ Snowflake ์ปค๋„ฅํ„ฐ๋Š” Snowflake ๋ฐ์ดํ„ฐ ํƒ€์ž… ์—์„œ ๋„ค์ดํ‹ฐ๋ธŒ Python ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ ๊ฐ’์„ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. (๊ฐ’์„ ๋ฌธ์ž์—ด๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํƒ€์ž…์„ ๋ณ€ํ™˜ํ•˜๋„๋ก ์„ ํƒํ•  ์ˆ˜ ์žˆ์Œ์— ์œ ์˜ํ•˜์‹ญ์‹œ์˜ค. ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜์„ ์šฐํšŒํ•˜์—ฌ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ ํ–ฅ์ƒํ•˜๊ธฐ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.)

์ฐธ๊ณ 

๊ธฐ๋ณธ์ ์œผ๋กœ NUMBER ์—ด์˜ ๊ฐ’์€ ๋ฐฐ์ •๋ฐ€๋„ ๋ถ€๋™ ์†Œ์ˆ˜์  ๊ฐ’(float64)์œผ๋กœ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. fetch_pandas_all() ๋ฐ fetch_pandas_batches() ๋ฉ”์„œ๋“œ์—์„œ ์ด๋ฅผ 10์ง„์ˆ˜ ๊ฐ’(decimal.Decimal)์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋ ค๋ฉด connect() ๋ฉ”์„œ๋“œ์˜ True ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ arrow_number_to_decimal ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

๋™๊ธฐ ์ฟผ๋ฆฌ ์ˆ˜ํ–‰ํ•˜๊ธฐยถ

๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด Cursor ์˜ค๋ธŒ์ ํŠธ์˜ execute() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ:

conn = snowflake.connector.connect( ... )
cur = conn.cursor()
cur.execute('select * from products')
Copy

cursor ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ ์˜ ์„ค๋ช…๊ณผ ๊ฐ™์ด, Cursor ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ ์ˆ˜ํ–‰ํ•˜๊ธฐยถ

Python์šฉ Snowflake ๋“œ๋ผ์ด๋ฒ„๋Š” ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ(์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ์–ด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฟผ๋ฆฌ)๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ œ์ถœํ•˜๊ณ  ํด๋ง์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ์™„๋ฃŒ ์‹œ์ ์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋œ ํ›„์—๋Š” ๊ฒฐ๊ณผ๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด ABORT_DETACHED_QUERY ๊ตฌ์„ฑ ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ FALSE (๊ธฐ๋ณธ๊ฐ’)์ธ์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ์™€์˜ ์—ฐ๊ฒฐ์ด ๋Š์–ด์ง„ ๊ฒฝ์šฐ:

  • ๋™๊ธฐ์‹ ์ฟผ๋ฆฌ์˜ ๊ฒฝ์šฐ ์ง„ํ–‰ ์ค‘์ธ ๋ชจ๋“  ๋™๊ธฐ์‹ ์ฟผ๋ฆฌ๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜ ๊ฐ’์— ๊ด€๊ณ„์—†์ด ์ฆ‰์‹œ ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค.

  • ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ์˜ ๊ฒฝ์šฐ:

    • ABORT_DETACHED_QUERY๊ฐ€ FALSE ๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ ์ง„ํ–‰ ์ค‘์ธ ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ณ„์† ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

    • ABORT_DETACHED_QUERY๊ฐ€ TRUE ๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ 5๋ถ„ ํ›„์—๋„ ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์ด ๋‹ค์‹œ ์„ค์ •๋˜์ง€ ์•Š์œผ๋ฉด Snowflake๋Š” ์ง„ํ–‰ ์ค‘์ธ ๋ชจ๋“  ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.

      cursor.query_result(queryId) ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ 5๋ถ„์ด ์ง€๋‚˜๋ฉด ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๊ฐ€ ์ค‘๋‹จ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ˜ธ์ถœ์€ ์ฟผ๋ฆฌ๊ฐ€ ์•„์ง ์‹คํ–‰ ์ค‘์ด๋ฏ€๋กœ ์‹ค์ œ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ƒ‰ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ์ฟผ๋ฆฌ๊ฐ€ ์ทจ์†Œ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. query_result ํ˜ธ์ถœ์€ ๋™๊ธฐ ์ž‘์—…์ด๋ฏ€๋กœ ํŠน์ • ์‚ฌ์šฉ ์‚ฌ๋ก€์— ์ ํ•ฉํ•  ์ˆ˜๋„ ์žˆ๊ณ  ์ ํ•ฉํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ ์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆด ํ•„์š” ์—†์ด ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ œ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋™์ผํ•œ ์„ธ์…˜ ๋™์•ˆ ๋™๊ธฐ ๋ฐ ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ์˜ ์กฐํ•ฉ์„ ์‹คํ–‰ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

๋‹จ์ผ ์ฟผ๋ฆฌ์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฌธ์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด ์„ธ์…˜์—์„œ ์œ ํšจํ•œ ์›จ์–ดํ•˜์šฐ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ํ•œ ์—ฐ๊ฒฐ์—์„œ ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ œ์ถœํ•˜๊ณ  ๋‹ค๋ฅธ ์—ฐ๊ฒฐ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์žฅ๊ธฐ ์‹คํ–‰ ์ฟผ๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•˜๋ฉฐ ๋‚˜์ค‘์— ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์‹œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ ์ œ์ถœํ•˜๊ธฐยถ

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ œ์ถœํ•˜๋ ค๋ฉด Cursor ์˜ค๋ธŒ์ ํŠธ์˜ execute_async() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ:

conn = snowflake.connector.connect( ... )
cur = conn.cursor()
# Submit an asynchronous query for execution.
cur.execute_async('select count(*) from table(generator(timeLimit => 25))')
Copy

์ฟผ๋ฆฌ๋ฅผ ์ œ์ถœํ•œ ํ›„:

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ์˜ˆ๋Š” ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ์˜ ์˜ˆ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ์˜ ๋ชจ๋ฒ” ์‚ฌ๋ก€ยถ

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ œ์ถœํ•  ๋•Œ ๋”ฐ๋ผ์•ผ ํ•˜๋Š” ๋ชจ๋ฒ” ์‚ฌ๋ก€๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋‹ค๋ฅธ ์ฟผ๋ฆฌ์— ์ข…์†๋œ ์ฟผ๋ฆฌ๋ฅผ ํ™•์ธํ•œ ํ›„ ์ฟผ๋ฆฌ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ถ€ ์ฟผ๋ฆฌ๋Š” ์ƒํ˜ธ ์˜์กด์ ์ด๊ณ  ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•˜๋ฏ€๋กœ, ๋ณ‘๋ ฌ ์‹คํ–‰์— ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, INSERT ๋ฌธ์€ ํ•ด๋‹น CREATE TABLE ๋ฌธ์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์‹œ์ž‘๋˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”๋ชจ๋ฆฌ์— ๋Œ€ํ•ด ๋„ˆ๋ฌด ๋งŽ์€ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ฟผ๋ฆฌ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํŠนํžˆ, ๋ฉ”๋ชจ๋ฆฌ์— 2๊ฐœ ์ด์ƒ์˜ ๊ฒฐ๊ณผ ์„ธํŠธ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ๋™์‹œ์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉ๋Ÿ‰์ด ํฌ๊ฒŒ ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • ํด๋ง ์ค‘์— ์ฟผ๋ฆฌ๊ฐ€ ์‹คํŒจํ•˜๋Š” ๋“œ๋ฌธ ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ํŠธ๋žœ์žญ์…˜ ์ œ์–ด ๋ฌธ(BEGIN, COMMIT ๋ฐ ROLLBACK)์€ ๋‹ค๋ฅธ ๋ฌธ๊ณผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • SQL ์ž์ฒด์— ORDER BY ์ ˆ์ด ์žˆ๋‹ค ํ•ด๋„ ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐ˜๋“œ์‹œ ์ •๋ ฌ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์„ธ์š”. ๊ฒฐ๊ณผ์ ์œผ๋กœ result_scan ํ•จ์ˆ˜๋Š” ์ •๋ ฌ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Snowflake ์ฟผ๋ฆฌ ID ๊ฒ€์ƒ‰ํ•˜๊ธฐยถ

์ฟผ๋ฆฌ ID๋ฅผ ํ†ตํ•ด Snowflake์— ์˜ํ•ด ์‹คํ–‰๋˜๋Š” ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Python์šฉ Snowflake ์ปค๋„ฅํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” Cursor ์˜ค๋ธŒ์ ํŠธ์˜ sfqid ์†์„ฑ์„ ํ†ตํ•ด ์ฟผ๋ฆฌ ID์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# Retrieving a Snowflake Query ID
cur = con.cursor()
cur.execute("SELECT * FROM testtable")
print(cur.sfqid)
Copy

์ฟผ๋ฆฌ ID๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ ์ƒํƒœ ํ™•์ธํ•˜๊ธฐยถ

์ฟผ๋ฆฌ์˜ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด:

  1. Cursor ์˜ค๋ธŒ์ ํŠธ์˜ sfqid ํ•„๋“œ์—์„œ ์ฟผ๋ฆฌ ID๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

  2. Connection ์˜ค๋ธŒ์ ํŠธ์˜ get_query_status() ๋ฉ”์„œ๋“œ๋กœ ์ฟผ๋ฆฌ ID๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์ฟผ๋ฆฌ์˜ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” QueryStatus ์—ด๊ฑฐํ˜• ์ƒ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    ๊ธฐ๋ณธ์ ์œผ๋กœ get_query_status() ์—์„œ๋Š” ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ€ ์˜ค๋ฅ˜์ธ ๊ฒฝ์šฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋„๋ก ํ•˜๋ ค๋ฉด, ๋Œ€์‹  get_query_status_throw_if_error() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  3. ์ฟผ๋ฆฌ์˜ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด QueryStatus ์—ด๊ฑฐํ˜• ์ƒ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    • ์ฟผ๋ฆฌ๊ฐ€ ์•„์ง ์‹คํ–‰ ์ค‘์ธ์ง€ ํ™•์ธ(์˜ˆ: ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ์ธ์ง€ ํ™•์ธ)ํ•˜๋ ค๋ฉด Connection ์˜ค๋ธŒ์ ํŠธ์˜ is_still_running() ๋ฉ”์„œ๋“œ๋กœ ์ด ์ƒ์ˆ˜๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

    • ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด ์ด ์ƒ์ˆ˜๋ฅผ is_an_error() ๋ฉ”์„œ๋“œ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

    ์—ด๊ฑฐํ˜• ์ƒ์ˆ˜์˜ ์ „์ฒด ๋ชฉ๋ก์€ QueryStatus ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๋‹ค์Œ ์˜ˆ์—์„œ๋Š” ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•œ ํ›„ ์ฟผ๋ฆฌ์˜ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

import time
...
# 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.sfqid
while conn.is_still_running(conn.get_query_status(query_id)):
  time.sleep(1)
Copy

๋‹ค์Œ ์˜ˆ์—์„œ๋Š” ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

from snowflake.connector import ProgrammingError
import time
...
# 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.sfqid
  while conn.is_still_running(conn.get_query_status_throw_if_error(query_id)):
    time.sleep(1)
except ProgrammingError as err:
  print('Programming Error: {0}'.format(err))
Copy

์ฟผ๋ฆฌ ID๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ๊ฒ€์ƒ‰ํ•˜๊ธฐยถ

์ฐธ๊ณ 

Cursor ์˜ค๋ธŒ์ ํŠธ์— ๋Œ€ํ•œ execute() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•œ ๊ฒฝ์šฐ์—๋Š” ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ธฐ ์œ„ํ•ด ์ฟผ๋ฆฌ ID๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. cursor ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ ์—์„œ์˜ ์„ค๋ช…๊ณผ ๊ฐ™์ด, ๊ฒฐ๊ณผ์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ ๋˜๋Š” ์ด์ „์— ์ œ์ถœํ•œ ๋™๊ธฐ ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด, ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์‹ญ์‹œ์˜ค.

  1. ์ฟผ๋ฆฌ์˜ ์ฟผ๋ฆฌ ID๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. Snowflake ์ฟผ๋ฆฌ ID ๊ฒ€์ƒ‰ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  2. Cursor ์˜ค๋ธŒ์ ํŠธ์˜ get_results_from_sfqid() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

  3. cursor ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ ์˜ ์„ค๋ช…๊ณผ ๊ฐ™์ด, Cursor ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ๊ฐ€ ์•„์ง ์‹คํ–‰ ์ค‘์ธ ๊ฒฝ์šฐ, ํŽ˜์น˜ ๋ฉ”์„œ๋“œ(fetchone(), fetchmany(), fetchall() ๋“ฑ)๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ:

# Get the results from a query.
cur.get_results_from_sfqid(query_id)
results = cur.fetchall()
print(f'{results[0]}')
Copy

cursor ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐยถ

์ปค์„œ ์˜ค๋ธŒ์ ํŠธ ๋ฐ˜๋ณต๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ด๋ธ”์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ํ…Œ์ด๋ธ” ์ƒ์„ฑ ๋ฐ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…ํ•˜๊ธฐ ์—์„œ ์ด์ „์— ์ƒ์„ฑํ•œ testtable ํ…Œ์ด๋ธ”์—์„œ โ€œcol1โ€ ๋ฐ โ€œcol2โ€ ์—ด์„ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด ๋‹ค์Œ๊ณผ ์œ ์‚ฌํ•œ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

cur = conn.cursor()
try:
    cur.execute("SELECT col1, col2 FROM test_table ORDER BY col1")
    for (col1, col2) in cur:
        print('{0}, {1}'.format(col1, col2))
finally:
    cur.close()
Copy

๋˜๋Š” Python์šฉ Snowflake ์ปค๋„ฅํ„ฐ๊ฐ€ ํŽธ๋ฆฌํ•œ ๋ฐ”๋กœ ๊ฐ€๊ธฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

for (col1, col2) in con.cursor().execute("SELECT col1, col2 FROM testtable"):
    print('{0}, {1}'.format(col1, col2))
Copy

๋‹จ์ผ ๊ฒฐ๊ณผ(์ฆ‰, ๋‹จ์ผ ํ–‰)๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด fetchone ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

col1, col2 = con.cursor().execute("SELECT col1, col2 FROM testtable").fetchone()
print('{0}, {1}'.format(col1, col2))
Copy

์ง€์ •๋œ ํ–‰์˜ ๊ฐœ์ˆ˜๋ฅผ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋ ค๋ฉด ํ–‰์˜ ๊ฐœ์ˆ˜์™€ ํ•จ๊ป˜ fetchmany ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

cur = con.cursor().execute("SELECT col1, col2 FROM testtable")
ret = cur.fetchmany(3)
print(ret)
while len(ret) > 0:
    ret = cur.fetchmany(3)
    print(ret)
Copy

์ฐธ๊ณ 

๊ฒฐ๊ณผ ์„ธํŠธ๊ฐ€ ๋„ˆ๋ฌด ์ปค ๋ฉ”๋ชจ๋ฆฌ์— ์ ํ•ฉํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋Š” fetchone ๋˜๋Š” fetchmany ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋ ค๋ฉด:

results = con.cursor().execute("SELECT col1, col2 FROM testtable").fetchall()
for rec in results:
    print('%s, %s' % (rec[0], rec[1]))
Copy

์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์„ค์ •ํ•˜๋ ค๋ฉด โ€œ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 query

except ProgrammingError as e:
   if e.errno == 604:
      print("timeout")
      conn.cursor().execute("rollback")
   else:
      raise e
else:
   conn.cursor().execute("commit")
Copy

DictCursor ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ด ์ด๋ฆ„์„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐยถ

์—ด ์ด๋ฆ„์„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด, DictCursor ํƒ€์ž…์˜ cursor ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ:

# Querying data by DictCursor
from snowflake.connector import DictCursor
cur = con.cursor(DictCursor)
try:
    cur.execute("SELECT col1, col2 FROM testtable")
    for rec in cur:
        print('{0}, {1}'.format(rec['COL1'], rec['COL2']))
finally:
    cur.close()
Copy

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ์˜ ์˜ˆยถ

๋น„๋™๊ธฐ ์ฟผ๋ฆฌ์˜ ๊ฐ„๋‹จํ•œ ์˜ˆ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

from snowflake.connector import ProgrammingError
import time

conn = 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]}')
Copy

๋‹ค์Œ ์˜ˆ์—์„œ๋Š” 1๊ฐœ์˜ ์—ฐ๊ฒฐ์—์„œ ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ์ œ์ถœํ•˜๊ณ  ๋‹ค๋ฅธ ์—ฐ๊ฒฐ์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

from snowflake.connector import ProgrammingError
import time

conn = 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]}')
Copy

์ฟผ๋ฆฌ ID๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฟผ๋ฆฌ ์ทจ์†Œํ•˜๊ธฐยถ

์ฟผ๋ฆฌ ID ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ทจ์†Œํ•ฉ๋‹ˆ๋‹ค.

cur = cn.cursor()

try:
  cur.execute(r"SELECT SYSTEM$CANCEL_QUERY('queryID')")
  result = cur.fetchall()
  print(len(result))
  print(result[0])
finally:
  cur.close()
Copy

โ€œqueryIDโ€ ๋ฌธ์ž์—ด์„ ์‹ค์ œ ์ฟผ๋ฆฌ ID๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ์˜ ID๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด Snowflake ์ฟผ๋ฆฌ ID ๊ฒ€์ƒ‰ํ•˜๊ธฐ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๋ฐ์ดํ„ฐ ๋ณ€ํ™˜์„ ์šฐํšŒํ•˜์—ฌ ์ฟผ๋ฆฌ ์„ฑ๋Šฅ ํ–ฅ์ƒํ•˜๊ธฐยถ

์ฟผ๋ฆฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒํ•˜๋ ค๋ฉด, snowflake.connector.converter_null ๋ชจ๋“ˆ์˜ SnowflakeNoConverterToPython ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Snowflake ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ ํƒ€์ž…์—์„œ ๋„ค์ดํ‹ฐ๋ธŒ Python ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ์˜ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜์„ ์šฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ:

from snowflake.connector.converter_null import SnowflakeNoConverterToPython

con = snowflake.connector.connect(
    ...
    converter_class=SnowflakeNoConverterToPython
)
for rec in con.cursor().execute("SELECT * FROM large_table"):
    # rec includes raw Snowflake data
Copy

๊ฒฐ๊ณผ์ ์œผ๋กœ, ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋Š” ๋ฌธ์ž์—ด ํ˜•์‹์œผ๋กœ ํ‘œ์‹œ๋˜์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋„ค์ดํ‹ฐ๋ธŒ 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 ๋„๊ตฌ์˜ ๊ฒฝ์šฐ ์ด ์„ค์ •์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด๋–ค ๊ฐ’์„ ์‚ฌ์šฉํ•ด์•ผ ํ• ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค๋ฉด ์กฐ์ง์˜ ํ•ด๋‹น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณด์•ˆ ์ •์ฑ…์„ ๋‹ด๋‹นํ•˜๋Š” ํŒ€์— ๋ฌธ์˜ํ•˜์‹ญ์‹œ์˜ค.

๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉํ•˜๊ธฐยถ

SQL ๋ฌธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’์„ ์ง€์ •ํ•˜๋ ค๋ฉด, ๋ฌธ์— ๋ฆฌํ„ฐ๋Ÿด์„ ํฌํ•จํ•˜๊ฑฐ๋‚˜ ๋ณ€์ˆ˜๋ฅผ ๋ฐ”์ธ๋”ฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€์ˆ˜๋ฅผ ๋ฐ”์ธ๋”ฉํ•  ๋•Œ SQL ๋ฌธ์˜ ํ…์ŠคํŠธ์— ์ž๋ฆฌ ํ‘œ์‹œ์ž๋ฅผ 1๊ฐœ ์ด์ƒ ์ถ”๊ฐ€ํ•˜๊ณ  ๊ฐ ์ž๋ฆฌ ํ‘œ์‹œ์ž์— ๋ณ€์ˆ˜(์‚ฌ์šฉํ•  ๋ณ€์ˆ˜)๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ๋Š” ๋ฆฌํ„ฐ๋Ÿด๊ณผ ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•  ๋•Œ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

๋ฆฌํ„ฐ๋Ÿด:

con.cursor().execute("INSERT INTO testtable(col1, col2) VALUES(789, 'test string3')")
Copy

๋ฐ”์ธ๋”ฉ:

con.cursor().execute(
    "INSERT INTO testtable(col1, col2) "
    "VALUES(%s, %s)", (
        789,
        'test string3'
    ))
Copy

์ฐธ๊ณ 

๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ๊ฑฐ๋‚˜ ์ผ๊ด„ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ํฌ๊ธฐ์˜ ์ƒํ•œ์—๋Š” ์ œํ•œ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฟผ๋ฆฌ ํ…์ŠคํŠธ ํฌ๊ธฐ ์ œํ•œ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

Snowflake์—์„œ ์ง€์›๋˜๋Š” ๋ฐ”์ธ๋”ฉ์˜ ํƒ€์ž…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ด์™€ ๊ด€๋ จํ•œ ๊ฐ๊ฐ์˜ ์„ค๋ช…์€ ์•„๋ž˜์—์„œ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

pyformat ๋˜๋Š” format ๋ฐ”์ธ๋”ฉยถ

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',
            })
    
    Copy
  • %s ๋ฅผ ์ž๋ฆฌ ํ‘œ์‹œ์ž๋กœ ์‚ฌ์šฉ:

    con.cursor().execute(
        "INSERT INTO testtable(col1, col2) "
        "VALUES(%s, %s)", (
            789,
            'test string3'
        ))
    
    Copy

pyformat ๋ฐ format ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ชฉ๋ก ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ IN ์—ฐ์‚ฐ์ž๋ฅผ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# Binding data for IN operator
con.cursor().execute(
    "SELECT col1, col2 FROM testtable"
    " WHERE col2 IN (%s)", (
        ['test string1', 'test string3'],
    ))
Copy

ํผ์„ผํŠธ ๋ฌธ์ž(โ€œ%โ€)๋Š” SQL LIKE์šฉ ์™€์ผ๋“œ์นด๋“œ ๋ฌธ์ž ๋ฐ Python์šฉ ํ˜•์‹ ๋ฐ”์ธ๋”ฉ ๋ฌธ์ž๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜•์‹ ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜๊ณ  SQL ๋ช…๋ น์— ํผ์„ผํŠธ ๋ฌธ์ž๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ์—๋Š” ํผ์„ผํŠธ ๋ฌธ์ž๋ฅผ ์ด์Šค์ผ€์ดํ”„ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, SQL ๋ฌธ์ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ:

SELECT col1, col2
    FROM test_table
    WHERE col2 ILIKE '%York' LIMIT 1;  -- Find York, New York, etc.
Copy

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)
Copy

qmark ๋˜๋Š” numeric ๋ฐ”์ธ๋”ฉยถ

qmark ๋ฐ”์ธ๋”ฉ ๋ฐ numeric ๋ฐ”์ธ๋”ฉ์€ ํด๋ผ์ด์–ธํŠธ์ธก์ด ์•„๋‹Œ ์„œ๋ฒ„์ธก์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค.

  • qmark ๋ฐ”์ธ๋”ฉ์˜ ๊ฒฝ์šฐ, ๋ฌผ์Œํ‘œ ๋ฌธ์ž(?)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ž์—ด์—์„œ ๋ณ€์ˆ˜ ๊ฐ’์„ ์‚ฝ์ž…ํ•  ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

  • numeric ๋ฐ”์ธ๋”ฉ์˜ ๊ฒฝ์šฐ, ์ฝœ๋ก (:) ๋’ค์— ์ˆซ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ์œ„์น˜์— ๋Œ€์ฒด๋  ๋ณ€์ˆ˜์˜ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, :2 ๋Š” ๋‘ ๋ฒˆ์งธ ๋ณ€์ˆ˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

    ์ˆซ์ž ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ผํ•œ ์ฟผ๋ฆฌ์—์„œ ๋™์ผํ•œ ๊ฐ’์„ ๋‘ ๋ฒˆ ์ด์ƒ ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‘ ๋ฒˆ ์ด์ƒ ์‚ฌ์šฉํ•  Long VARCHAR ๋˜๋Š” BINARY ๋˜๋Š” ๋ฐ˜์ •ํ˜• ๊ฐ’์ด ์žˆ๋Š” ๊ฒฝ์šฐ numeric ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋ฉด ์„œ๋ฒ„์— ๊ฐ’์„ ํ•œ ๋ฒˆ ์ „์†กํ•˜๊ณ  ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” qmark ๋ฐ numeric ๋ฐ”์ธ๋”ฉ์˜ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

qmark ๋˜๋Š” numeric ๋ฐ”์ธ๋”ฉ ์‚ฌ์šฉํ•˜๊ธฐยถ

qmark ๋˜๋Š” numeric ์Šคํƒ€์ผ ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ connect() ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์—ฐ๊ฒฐ ๋งค๊ฐœ ๋ณ€์ˆ˜์˜ ์ผ๋ถ€๋กœ paramstyle ์„ ์„ค์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  • snowflake.connector.paramstyle='qmark'

  • snowflake.connector.paramstyle='numeric'

paramstyle ์„ qmark ๋˜๋Š” numeric ์„ ์„ค์ •ํ•œ ๊ฒฝ์šฐ์—๋Š”, ? ๋˜๋Š” :N (์—ฌ๊ธฐ์„œ N ์„ ์ˆซ์ž๋กœ ๋Œ€์ฒด)์„ ๊ฐ๊ฐ ์ž๋ฆฌ ํ‘œ์‹œ์ž๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ:

  • ? ๋ฅผ ์ž๋ฆฌ ํ‘œ์‹œ์ž๋กœ ์‚ฌ์šฉ:

    from snowflake.connector import connect
    
    connection_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())
        )
    )
    
    Copy
  • :N ์„ ์ž๋ฆฌ ํ‘œ์‹œ์ž๋กœ ์‚ฌ์šฉ:

    import snowflake.connector
    
    snowflake.connector.paramstyle='numeric'
    
    con = snowflake.connector.connect(...)
    
    con.cursor().execute(
        "INSERT INTO testtable(col1, col2) "
        "VALUES(:1, :2)", (
            789,
            'test string3'
        ))
    
    Copy

    ๋‹ค์Œ ์ฟผ๋ฆฌ๋Š” numeric ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€์ˆ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

    con.cursor().execute(
        "INSERT INTO testtable(complete_video, short_sample_of_video) "
        "VALUES(:1, SUBSTRING(:1, :2, :3))", (
            binary_value_that_stores_video,          # variable :1
            starting_offset_in_bytes_of_video_clip,  # variable :2
            length_in_bytes_of_video_clip            # variable :3
        ))
    
    Copy

datetime ์˜ค๋ธŒ์ ํŠธ์™€ ํ•จ๊ป˜ qmark ๋˜๋Š” numeric ๋ฐ”์ธ๋”ฉ ์‚ฌ์šฉํ•˜๊ธฐยถ

qmark ๋˜๋Š” numeric ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ Snowflake TIMESTAMP ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒฝ์šฐ, ๋ฐ”์ธ๋”ฉ ๋ณ€์ˆ˜๋ฅผ Snowflake ํƒ€์ž„์Šคํƒฌํ”„ ๋ฐ์ดํ„ฐ ํƒ€์ž…(TIMESTAMP_LTZ ๋˜๋Š” TIMESTAMP_TZ) ๋ฐ ๊ฐ’์„ ์ง€์ •ํ•˜๋Š” ํŠœํ”Œ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ:

import snowflake.connector

snowflake.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())
    )
 )
Copy

ํด๋ผ์ด์–ธํŠธ์ธก ๋ฐ”์ธ๋”ฉ๊ณผ ๋‹ฌ๋ฆฌ, ์„œ๋ฒ„์ธก ๋ฐ”์ธ๋”ฉ์—๋Š” ์—ด์— ๋Œ€ํ•œ Snowflake ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ Python ๋ฐ์ดํ„ฐ ํƒ€์ž…์—๋Š” ์ด๋ฏธ Snowflake ๋ฐ์ดํ„ฐ ํƒ€์ž…์— ๋Œ€ํ•œ ์•”์‹œ์  ๋งคํ•‘(์˜ˆ: int ์ด FIXED ๋กœ ๋งคํ•‘๋จ)์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Python datetime ๋ฐ์ดํ„ฐ๋Š” ์—ฌ๋Ÿฌ Snowflake ๋ฐ์ดํ„ฐ ํƒ€์ž… ์ค‘ 1๊ฐœ(TIMESTAMP_NTZ, TIMESTAMP_LTZ ๋˜๋Š” TIMESTAMP_TZ)์— ๋ฐ”์ธ๋”ฉ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ธฐ๋ณธ ๋งคํ•‘์€ TIMESTAMP_NTZ ์ด๋ฏ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์šฉํ•  Snowflake ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

IN ์—ฐ์‚ฐ์ž์™€ ํ•จ๊ป˜ ๋ฐ”์ธ๋”ฉ ๋ณ€์ˆ˜ ์‚ฌ์šฉํ•˜๊ธฐยถ

qmark ๋ฐ numeric (์„œ๋ฒ„์ธก ๋ฐ”์ธ๋”ฉ)์—์„œ๋Š” IN ์—ฐ์‚ฐ์ž์™€ ํ•จ๊ป˜ ๋ณ€์ˆ˜๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒƒ์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ฐ”์ธ๋”ฉ ๋ณ€์ˆ˜์™€ IN ์—ฐ์‚ฐ์ž๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ํด๋ผ์ด์–ธํŠธ์ธก ๋ฐ”์ธ๋”ฉ (pyformat ๋˜๋Š” format)์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

์ผ๊ด„ ์‚ฝ์ž…์„ ์œ„ํ•ด ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ๋ณ€์ˆ˜์— ๋ฐ”์ธ๋”ฉํ•˜๊ธฐยถ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ๋Š” ๋‹จ์ผ ์ผ๊ด„ ์ฒ˜๋ฆฌ์— ์—ฌ๋Ÿฌ ํ–‰์„ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด INSERT ๋ฌธ์—์„œ ๊ฐ’์— ๋Œ€ํ•œ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค์Œ ๋ฌธ์—์„œ๋Š” INSERT ๋ฌธ์—์„œ qmark ๋ฐ”์ธ๋”ฉ์„ ์œ„ํ•ด ์ž๋ฆฌ ํ‘œ์‹œ์ž๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

insert into grocery (item, quantity) values (?, ?)
Copy

๊ทธ๋ฆฌ๊ณ  ์‚ฝ์ž…ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์ •ํ•˜๋ ค๋ฉด ์‹œํ€€์Šค์˜ ์‹œํ€€์Šค(์˜ˆ: ํŠœํ”Œ ๋ชฉ๋ก)์ธ ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

rows_to_insert = [('milk', 2), ('apple', 3), ('egg', 2)]
Copy

์œ„์˜ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด, ๋ชฉ๋ก์˜ ๊ฐ ํ•ญ๋ชฉ์€ ์‚ฝ์ž…ํ•  ํ–‰์— ๋Œ€ํ•œ ์—ด ๊ฐ’์ด ํฌํ•จ๋œ ํŠœํ”Œ์ž…๋‹ˆ๋‹ค.

๋ฐ”์ธ๋”ฉ์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด 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)
Copy

์„œ๋ฒ„์ธก์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉ (์ฆ‰, 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.
Copy

์ฐธ๊ณ 

Snowflake ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋Š” ๋Œ€์ฒด ๋ฐฉ๋ฒ•(COPY ๋ช…๋ น์„ ์‚ฌ์šฉํ•œ ๋Œ€๋Ÿ‰ ๋กœ๋“œ ๋“ฑ)๊ณผ ๊ด€๋ จํ•ด์„œ๋Š” Snowflake์— ๋ฐ์ดํ„ฐ ๋กœ๋“œํ•˜๊ธฐ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

SQL ์‚ฝ์ž… ๊ณต๊ฒฉ ๋ฐฉ์ง€ํ•˜๊ธฐยถ

SQL ์‚ฝ์ž…์˜ ์œ„ํ—˜์ด ์žˆ์œผ๋ฏ€๋กœ Python์˜ ํ˜•์‹ ์ง€์ • ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜์ง€ ๋ง์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ:

# Binding data (UNSAFE EXAMPLE)
con.cursor().execute(
    "INSERT INTO testtable(col1, col2) "
    "VALUES(%(col1)d, '%(col2)s')" % {
        'col1': 789,
        'col2': 'test string3'
    })
Copy
# Binding data (UNSAFE EXAMPLE)
con.cursor().execute(
    "INSERT INTO testtable(col1, col2) "
    "VALUES(%d, '%s')" % (
        789,
        'test string3'
    ))
Copy
# Binding data (UNSAFE EXAMPLE)
con.cursor().execute(
    "INSERT INTO testtable(col1, col2) "
    "VALUES({col1}, '{col2}')".format(
        col1=789,
        col2='test string3')
    )
Copy

๋Œ€์‹ ์— ๊ฐ’์„ ๋ณ€์ˆ˜์— ์ €์žฅํ•œ ๋‹ค์Œ qmark ๋˜๋Š” ์ˆซ์ž ๋ฐ”์ธ๋”ฉ ์Šคํƒ€์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค.

์—ด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ฒ€์ƒ‰ํ•˜๊ธฐยถ

๊ฒฐ๊ณผ ์„ธํŠธ์—์„œ ๊ฐ ์—ด์— ๋Œ€ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(์˜ˆ: ๊ฐ ์—ด์˜ ์ด๋ฆ„, ํƒ€์ž…, ์ „์ฒด ์ž๋ฆฟ์ˆ˜, ์†Œ์ˆ˜ ์ž๋ฆฟ์ˆ˜ ๋“ฑ)๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

  • ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด 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] for col in cur.description]))
Copy

์˜ˆ: ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ด ์ด๋ฆ„ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ(2.4.6 ์ด์ƒ ๋ฒ„์ „):

๋‹ค์Œ ์˜ˆ์—์„œ๋Š” description ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•œ ํ›„ ์—ด ์ด๋ฆ„ ๋ชฉ๋ก์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ์†์„ฑ์€ ResultMetaData ์˜ค๋ธŒ์ ํŠธ์˜ ๋ชฉ๋ก์œผ๋กœ, ์ด ์˜ˆ์—์„œ๋Š” ๊ฐ ResultMetadata ์˜ค๋ธŒ์ ํŠธ์˜ name ์†์„ฑ์—์„œ ์—ด ์ด๋ฆ„์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค.

cur = conn.cursor()
cur.execute("SELECT * FROM test_table")
print(','.join([col.name for col in cur.description]))
Copy

์˜ˆ: ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์—ด ์ด๋ฆ„ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ(2.4.6 ์ด์ƒ ๋ฒ„์ „):

๋‹ค์Œ ์˜ˆ์—์„œ๋Š” describe ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์—ด ์ด๋ฆ„ ๋ชฉ๋ก์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. describe() ๋ฉ”์„œ๋“œ๋Š” ResultMetaData ์˜ค๋ธŒ์ ํŠธ์˜ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ์ด ์˜ˆ์—์„œ๋Š” ๊ฐ ResultMetadata ์˜ค๋ธŒ์ ํŠธ์˜ name ์†์„ฑ์—์„œ ์—ด ์ด๋ฆ„์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค.

cur = conn.cursor()
result_metadata_list = cur.describe("SELECT * FROM test_table")
print(','.join([col.name for col in result_metadata_list]))
Copy

์˜ค๋ฅ˜ ์ฒ˜๋ฆฌยถ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ Snowflake ์ปค๋„ฅํ„ฐ์—์„œ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ฝ”๋“œ ์‹คํ–‰์˜ ๊ณ„์† ๋˜๋Š” ์ค‘์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

# Catching the syntax error
cur = con.cursor()
try:
    cur.execute("SELECT * FROM testtable")
except snowflake.connector.errors.ProgrammingError as e:
    # default error message
    print(e)
    # customer error message
    print('Error {0} ({1}): {2} ({3})'.format(e.errno, e.sqlstate, e.msg, e.sfqid))
finally:
    cur.close()
Copy

execute_stream ์„ ์‚ฌ์šฉํ•˜์—ฌ SQL ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ํ•˜๊ธฐยถ

execute_stream ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ŠคํŠธ๋ฆผ์—์„œ 1๊ฐœ ์ด์ƒ์˜ SQL ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

from codecs import open
with open(sqlfile, 'r', encoding='utf-8') as f:
    for cur in con.execute_stream(f):
        for ret in cur:
            print(ret)
Copy

์ฐธ๊ณ 

sql_stream ์— ์ฃผ์„์ด ํฌํ•จ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ์ถ”๊ฐ€ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. execute_stream์„ ์‚ฌ์šฉํ•˜์—ฌ SQL ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์—ฐ๊ฒฐ ์ข…๋ฃŒํ•˜๊ธฐยถ

close ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์—ฐ๊ฒฐ์„ ์ข…๋ฃŒํ•˜๋Š” ๊ฒƒ์ด ๋ชจ๋ฒ” ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.

connection.close()
Copy

์ด๋ฅผ ํ†ตํ•ด ์ˆ˜์ง‘๋œ ํด๋ผ์ด์–ธํŠธ ๋ฉ”ํŠธ๋ฆญ์„ ์„œ๋ฒ„๋กœ ์ œ์ถœํ•˜๊ณ  ์„ธ์…˜์„ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, try-finally ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ค‘๊ฐ„์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ์—ฐ๊ฒฐ์ด ์ข…๋ฃŒ๋˜์ง€ ์•Š๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# Connecting to Snowflake
con = snowflake.connector.connect(...)
try:
    # Running queries
    con.cursor().execute(...)
    ...
finally:
    # Closing the connection
    con.close()
Copy

์กฐ์‹ฌ

๋‹ซํžˆ์ง€ ์•Š์€ ์—ฐ๊ฒฐ์ด ์—ฌ๋Ÿฌ ๊ฐœ ์žˆ์œผ๋ฉด ์‹œ์Šคํ…œ ๋ฆฌ์†Œ์Šค๊ฐ€ ๊ณ ๊ฐˆ๋˜์–ด ๊ฒฐ๊ตญ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž๋ฅผ ์‚ฌ์šฉํ•œ ํŠธ๋žœ์žญ์…˜ ์—ฐ๊ฒฐ ๋ฐ ๊ด€๋ฆฌํ•˜๊ธฐยถ

Python์šฉ Snowflake ์ปค๋„ฅํ„ฐ๋Š” ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ฆฌ์†Œ์Šค๋ฅผ ํ• ๋‹น ๋ฐ ํ•ด์ œํ•˜๋Š” ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž๋Š” autocommit ์ด ๋น„ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ๋ฌธ์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ ๋˜๋Š” ๋กค๋ฐฑํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

# Connecting to Snowflake using the context manager
with snowflake.connector.connect(
  user=USER,
  password=PASSWORD,
  account=ACCOUNT,
  autocommit=False,
) as con:
    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
Copy

์œ„์˜ ์˜ˆ์—์„œ, ์„ธ ๋ฒˆ์งธ ๋ฌธ์ด ์‹คํŒจํ•˜๋ฉด ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž๊ฐ€ ํŠธ๋žœ์žญ์…˜์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋กค๋ฐฑํ•˜๊ณ  ์—ฐ๊ฒฐ์„ ๋Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ฌธ์ด ์„ฑ๊ณตํ•˜๋ฉด ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž๊ฐ€ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ปค๋ฐ‹ํ•˜๊ณ  ์—ฐ๊ฒฐ์„ ๋Š์Šต๋‹ˆ๋‹ค.

try ๋ฐ except ๋ธ”๋ก์— ํ•ด๋‹นํ•˜๋Š” ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

# Connecting to Snowflake using try and except blocks
con = 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')") # fail
    con.commit()
except Exception as e:
    con.rollback()
    raise e
finally:
    con.close()
Copy

VECTOR ๋ฐ์ดํ„ฐ ํƒ€์ž… ์‚ฌ์šฉํ•˜๊ธฐยถ

VECTOR ๋ฐ์ดํ„ฐ ํƒ€์ž… ์— ๋Œ€ํ•œ ์ง€์›์€ ๋ฒ„์ „ 3.6.0์˜ Snowflake Python Connector์— ๋„์ž…๋˜์—ˆ์Šต๋‹ˆ๋‹ค. VECTOR ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋ฒกํ„ฐ ์œ ์‚ฌ์„ฑ ํ•จ์ˆ˜ ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒกํ„ฐ ๊ฒ€์ƒ‰ ๋˜๋Š” RAG (retrieval-augmented-generation)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ ์˜ˆ์ œ๋Š” Python ์ปค๋„ฅํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ VECTOR ์—ด์ด ์žˆ๋Š” ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๊ณ  VECTOR_INNER_PRODUCT ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

import snowflake.connector

conn = ... # Set up connection
cur = conn.cursor()

# Create a table and insert some vectors
cur.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])]
for row in values:
    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 b
cur.execute("SELECT VECTOR_INNER_PRODUCT(a, b) FROM vectors")
print(cur.fetchall())
Copy
[(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())
Copy
[([1.0, 2.2..., 3.0], 0.9990...)]

์ฐธ๊ณ 

VECTOR ๋ฐ์ดํ„ฐ ํƒ€์ž…์—๋Š” ๋ณ€์ˆ˜ ๋ฐ”์ธ๋”ฉ์ด ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋กœ๊น…ยถ

Python์šฉ Snowflake ์ปค๋„ฅํ„ฐ๋Š” ํ‘œ์ค€ Python logging ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ผ์ • ๊ฐ„๊ฒฉ์œผ๋กœ ์ƒํƒœ๋ฅผ ๊ธฐ๋กํ•จ์œผ๋กœ์จ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ์‹คํ–‰๋˜๋Š” ํ™œ๋™์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค. ๋กœ๊น…์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹œ์ž‘ํ•  ๋•Œ logging.basicConfig() ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๋กœ๊น… ์ˆ˜์ค€์„ INFO ๋กœ ์„ค์ •ํ•˜๊ณ  /tmp/snowflake_python_connector.log ํŒŒ์ผ์— ๋กœ๊ทธ๋ฅผ ์ €์žฅํ•˜๋ ค๋ฉด:

logging.basicConfig(
    filename=file_name,
    level=logging.INFO)
Copy

๋ณด๋‹ค ํฌ๊ด„์ ์ธ ๋กœ๊น…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋กœ๊น… ์ˆ˜์ค€์„ DEBUG ๋กœ ์„ค์ •ํ•˜์—ฌ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# Logging including the timestamp, thread and the source code location
import logging
for logger_name in ['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)
Copy

์„ ํƒ ์‚ฌํ•ญ์ด์ง€๋งŒ ๊ถŒ์žฅ๋˜๋Š” SecretDetector ํฌ๋งทํ„ฐ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•Œ๋ ค์ง„ ๋ฏผ๊ฐํ•œ ์ •๋ณด ์„ธํŠธ๋ฅผ ๋งˆ์Šคํ‚นํ•œ ํ›„ Snowflake Python Connector ๋กœ๊ทธ ํŒŒ์ผ์— ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. SecretDetector๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, ๋‹ค์Œ๊ณผ ์œ ์‚ฌํ•œ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

# Logging including the timestamp, thread and the source code location
import logging
from snowflake.connector.secret_detector import SecretDetector
for logger_name in ['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)
Copy

์ฐธ๊ณ 

botocore ๋ฐ boto3 ๋Š” Python์šฉ AWS(Amazon Web Services) SDK๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ํŒŒ์ผ ๋กœ๊ทธํ•˜๊ธฐยถ

๋˜๋Š” ๋กœ๊ทธ ์ˆ˜์ค€๊ณผ config.toml ๊ตฌ์„ฑ ํŒŒ์ผ์— ๋กœ๊ทธ ํŒŒ์ผ์„ ์ €์žฅํ•  ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์†์‰ฝ๊ฒŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŒŒ์ผ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ connections.toml ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฐ๊ฒฐํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ฐธ๊ณ 

์ด ๋กœ๊น… ๊ตฌ์„ฑ ๊ธฐ๋Šฅ์€ Python ๋กœ๊น… ๋ฌธ์„œ์— ์ •์˜๋œ ๋Œ€๋กœ ๋กœ๊ทธ ์ˆ˜์ค€์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๋กœ๊น… ์ˆ˜์ค€์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ Python ๊ธฐ๋ณธ ๋กœ๊น… ์ž์Šต์„œ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ด ๋กœ๊น… ๊ตฌ์„ฑ ํŒŒ์ผ์—์„œ๋Š” toml์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด save_logs, level, path ๋กœ๊น… ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

[log]
save_logs = true
level = "INFO"
path = "<directory to store logs>"
Copy

์—ฌ๊ธฐ์„œ,

  • save_logs ๋Š” ๋กœ๊ทธ๋ฅผ ์ €์žฅํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

  • level ์€ ๋กœ๊น… ์ˆ˜์ค€์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ •์˜ํ•˜์ง€ ์•Š์œผ๋ฉด ๋“œ๋ผ์ด๋ฒ„์˜ ๊ธฐ๋ณธ๊ฐ’์€ INFO ์ž…๋‹ˆ๋‹ค.

  • ``path``๋Š” ๋กœ๊ทธ ํŒŒ์ผ์„ ์ €์žฅํ•  ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค. ์ •์˜๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋“œ๋ผ์ด๋ฒ„์—์„œ ๊ธฐ๋ณธ $SNOWFLAKE_HOME/logs/ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋กœ๊ทธ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

config.toml ํŒŒ์ผ์— [log] ์„น์…˜์ด ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๊ฐ€ ์ €์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•˜๋ฃจ ๋™์•ˆ์˜ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋Š” python-connector.log ํŒŒ์ผ์— ์ถ”๊ฐ€๋˜๋ฉฐ, ์ด ํŒŒ์ผ์€ ๋‚˜์ค‘์— python-connector.log.YYYY-MM-DD ๋กœ ์ด๋ฆ„์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

์ƒ˜ํ”Œ ํ”„๋กœ๊ทธ๋žจยถ

๋‹ค์Œ ์ƒ˜ํ”Œ ์ฝ”๋“œ์—์„œ๋Š” ์ด์ „ ์„น์…˜์—์„œ ์„ค๋ช…ํ•œ ์—ฌ๋Ÿฌ ์˜ˆ๋ฅผ ์ž‘๋™ํ•˜๋Š” Python ํ”„๋กœ๊ทธ๋žจ์— ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์—๋Š” ๋‹ค์Œ์˜ ๋‘ ๋ถ€๋ถ„์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

  • ์ƒ์œ„ ํด๋ž˜์Šค(โ€œpython_veritas_baseโ€)์—๋Š” ์„œ๋ฒ„ ์—ฐ๊ฒฐ๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ ์ผ๋ฐ˜ ์ž‘์—…์„ ์œ„ํ•œ ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

  • ํ•˜์œ„ ํด๋ž˜์Šค(โ€œpython_connector_exampleโ€)๋Š” ํ…Œ์ด๋ธ” ์ฟผ๋ฆฌ์™€ ๊ฐ™์€ ํŠน์ • ํด๋ผ์ด์–ธํŠธ๋ฅผ ์œ„ํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ๋ถ€๋ถ„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

์ด ์ƒ˜ํ”Œ ์ฝ”๋“œ๋Š” ์ตœ์‹  ์ œํ’ˆ ๋นŒ๋“œ์—์„œ์˜ ์‹คํ–‰์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ ์ค‘ 1๊ฐœ์—์„œ ์ง์ ‘ ๊ฐ€์ ธ์˜จ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

์ด ์ฝ”๋“œ๋Š” ํ…Œ์ŠคํŠธ์—์„œ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ด๋ฏ€๋กœ ์ผ๋ถ€ ํ…Œ์ŠคํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋Œ€์ฒด ํฌํŠธ ๋ฐ ํ”„๋กœํ† ์ฝœ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•œ ์†Œ๋Ÿ‰์˜ ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ํ”„๋กœํ† ์ฝœ ๋˜๋Š” ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๋ฉฐ, ๋Œ€์‹  ์ด๋Ÿฌํ•œ ๋‹จ๊ณ„๋ฅผ ์ƒ๋žตํ•˜๊ณ  ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—๋Š” ๋ฌธ์„œ์— ๋ณ„๋„๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•œ ์„น์…˜ ๋งˆ์ปค(โ€œ์ฝ”๋“œ ์กฐ๊ฐ ํƒœ๊ทธโ€๋ผ๊ณ ๋„ ํ•จ)๋„ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์„น์…˜ ๋งˆ์ปค๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

# -- (> ---------------------- SECTION=import_connector ---------------------
...
# -- <) ---------------------------- END_SECTION ----------------------------
Copy

์ด๋Ÿฌํ•œ ์„น์…˜ ๋งˆ์ปค๋Š” ์‚ฌ์šฉ์ž ์ฝ”๋“œ์—์„œ ํ•„์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

์ฝ”๋“œ ์ƒ˜ํ”Œ์˜ ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์—๋Š” ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ๊ณตํ†ต ์„œ๋ธŒ๋ฃจํ‹ด์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์—ฐ๊ฒฐ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ๋ช…๋ น์ค„ ์ธ์ž(์˜ˆ: โ€œโ€“warehouse MyWarehouseโ€) ์ฝ๊ธฐ.

  • ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๊ธฐ.

  • ์›จ์–ดํ•˜์šฐ์Šค, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ์Šคํ‚ค๋งˆ ๋งŒ๋“ค๊ธฐ ๋ฐ ์‚ฌ์šฉํ•˜๊ธฐ.

  • ์‚ฌ์šฉ ์™„๋ฃŒ ์‹œ ์Šคํ‚ค๋งˆ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ์›จ์–ดํ•˜์šฐ์Šค ์‚ญ์ œํ•˜๊ธฐ.


import logging
import os
import sys


# -- (> ---------------------- SECTION=import_connector ---------------------
import snowflake.connector
# -- <) ---------------------------- END_SECTION ----------------------------


class python_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_name
        if file_name is None:
            file_name = '/tmp/snowflake_python_connector.log'

        # -- (> ---------- SECTION=begin_logging -----------------------------
        logging.basicConfig(
            filename=file_name,
            level=logging.INFO)
        # -- <) ---------- END_SECTION ---------------------------------------


    # -- (> ---------------------------- SECTION=main ------------------------
    def main(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 --------------------


    def args_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 = 1
        while i < 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_value
            i += 2

        return connection_parameters


    def create_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.
        if len(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...
        if PASSWORD is None or PASSWORD == '':
            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!!).
        if PROTOCOL is None or PROTOCOL == "" or PORT is None or PORT == "":
            # -- (> ------------------- 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
                )

        return conn


    def set_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)


    def do_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):
        for row in cursor1:
            print(row[0])
        # Close this cursor.
        cursor1.close()


    def clean_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)


    def create_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 ---------------------


    def drop_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)


Copy

์ฝ”๋“œ ์ƒ˜ํ”Œ์˜ ๋‘ ๋ฒˆ์งธ ๋ถ€ํ’ˆ์—์„œ๋Š” ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๊ณ  ํ…Œ์ด๋ธ”์— ํ–‰์„ ์‚ฝ์ž…ํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.


import sys

# -- (> ---------------------- SECTION=import_connector ---------------------
import snowflake.connector
# -- <) ---------------------------- END_SECTION ----------------------------


# Import the base class that contains methods used in many tests and code 
# examples.
from python_veritas_base import python_veritas_base


class python_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):
    pass


  def do_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) in cur:
            print('{0}, {1}'.format(col1, col2))
    finally:
        cur.close()
    # -- <) ---------------------------- END_SECTION -------------------------




# ============================================================================

if __name__ == '__main__':

    test_case = python_connector_example()
    test_case.main(sys.argv)

Copy

์ด ์ƒ˜ํ”Œ์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  1. ์ฝ”๋“œ์˜ ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์„ โ€œpython_veritas_base.pyโ€ ํŒŒ์ผ๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

  2. ์ฝ”๋“œ์˜ ๋‘ ๋ฒˆ์งธ ๋ถ€๋ถ„์„ โ€œpython_connector_example.pyโ€ ํŒŒ์ผ๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

  3. SNOWSQL_PWD ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋น„๋ฐ€๋ฒˆํ˜ธ์— ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ:

    export SNOWSQL_PWD='MyPassword'
    
    Copy
  4. ๋‹ค์Œ๊ณผ ์œ ์‚ฌํ•œ ๋ช…๋ น์ค„์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค(์‚ฌ์šฉ์ž ๋ฐ ๊ณ„์ • ์ •๋ณด๋ฅผ ์‚ฌ์šฉ์ž ๋ฐ ๊ณ„์ • ์ •๋ณด๋กœ ๋ฐ”๊ฟ”์•ผ ํ•จ).

    ๊ฒฝ๊ณ 

    ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์˜ ๋งˆ์ง€๋ง‰์— ์›จ์–ดํ•˜์šฐ์Šค, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ์Šคํ‚ค๋งˆ๊ฐ€ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค! ์†์‹ค๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

    python3 python_connector_example.py --warehouse <unique_warehouse_name> --database <new_warehouse_zzz_test> --schema <new_schema_zzz_test> --account myorganization-myaccount --user MyUserName
    
    Copy

์ถœ๋ ฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Connecting...

Creating warehouse, database, schema...

Creating table test_table...

Selecting from test_table...
123, test string1
456, test string2

Closing connection...
Copy

๊ธธ์ด๊ฐ€ ๋” ๊ธด ์˜ˆ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

๊ณ„์ • ๋ฐ ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์„ค์ •ํ•œ ์„น์…˜์—์„œ Snowflake ๋กœ๊ทธ์ธ ์ •๋ณด(์ด๋ฆ„, ๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ)์™€ ์ผ์น˜ํ•˜๋„๋ก ๋ณ€์ˆ˜๋ฅผ ๋ฐ”๊ฟจ๋Š”์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.

์ด ์˜ˆ์—์„œ๋Š” format() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ํ™˜๊ฒฝ์— SQL ์‚ฝ์ž… ๊ณต๊ฒฉ์˜ ์œ„ํ—˜์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” format() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ๊ฐ’์„ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#!/usr/bin/env python
#
# Snowflake Connector for Python Sample Program
#

# Logging
import logging
logging.basicConfig(
    filename='/tmp/snowflake_python_connector.log',
    level=logging.INFO)

import snowflake.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>'

import os

# Only required if you copy data from your S3 bucket
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')

# Connecting to Snowflake
con = snowflake.connector.connect(
  user=USER,
  password=PASSWORD,
  account=ACCOUNT,
)

# Creating a database, schema, and warehouse if none exists
con.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 warehouse
con.cursor().execute("USE WAREHOUSE tiny_warehouse")
con.cursor().execute("USE SCHEMA testdb.testschema")

# Creating a table and inserting data
con.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 data
cur = con.cursor()
try:
    cur.execute("SELECT col1, col2 FROM testtable")
    for (col1, col2) in cur:
        print('{0}, {1}'.format(col1, col2))
finally:
    cur.close()

# Binding data
con.cursor().execute(
    "INSERT INTO testtable(col1, col2) "
    "VALUES(%(col1)s, %(col2)s)", {
        'col1': 789,
        'col2': 'test string3',
        })

# Retrieving column names
cur = con.cursor()
cur.execute("SELECT * FROM testtable")
print(','.join([col[0] for col in cur.description]))

# Catching syntax errors
cur = con.cursor()
try:
    cur.execute("SELECT * FROM testtable")
except snowflake.connector.errors.ProgrammingError as e:
    # default error message
    print(e)
    # user error message
    print('Error {0} ({1}): {2} ({3})'.format(e.errno, e.sqlstate, e.msg, e.sfqid))
finally:
    cur.close()

# Retrieving the Snowflake query ID
cur = con.cursor()
cur.execute("SELECT * FROM testtable")
print(cur.sfqid)

# Closing the connection
con.close()
Copy