Python์œผ๋กœ UDTF ์ž‘์„ฑํ•˜๊ธฐยถ

Python์œผ๋กœ ์‚ฌ์šฉ์ž ์ •์˜ ํ…Œ์ด๋ธ” ํ•จ์ˆ˜ (UDTF) ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•ธ๋“ค๋Ÿฌ ์ฝ”๋“œ๋Š” UDTF ํ˜ธ์ถœ ์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ํ•ญ๋ชฉ์—์„œ๋Š” Python์œผ๋กœ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  UDTF๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

UDTF๋Š” ํ…Œ์ด๋ธ” ํ˜•์‹์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜(UDF)์ž…๋‹ˆ๋‹ค. Python์œผ๋กœ ๊ตฌํ˜„ํ•œ UDF ํ•ธ๋“ค๋Ÿฌ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ Python UDF ๋งŒ๋“ค๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. UDF์— ๋Œ€ํ•œ ๋ณด๋‹ค ์ผ๋ฐ˜์ ์ธ ๋‚ด์šฉ์€ ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜ ๊ฐœ์š” ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

UDTF์šฉ ํ•ธ๋“ค๋Ÿฌ์—์„œ ์ž…๋ ฅ ํ–‰์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ด ํ•ญ๋ชฉ์˜ ํ–‰ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ฐธ์กฐ). ๋˜ํ•œ ๊ฐ ์ž…๋ ฅ ํŒŒํ‹ฐ์…˜์— ๋Œ€ํ•ด ์‹คํ–‰๋˜๋Š” ๋…ผ๋ฆฌ๊ฐ€ ์žˆ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค(์ด ํ•ญ๋ชฉ์˜ ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ฐธ์กฐ).

Python UDTF๋ฅผ ๋งŒ๋“ค ๋•Œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  1. UDTF ํ˜ธ์ถœ ์‹œ Snowflake๊ฐ€ ํ˜ธ์ถœํ•  ๋ฉ”์„œ๋“œ๋กœ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

    ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ํ•ธ๋“ค๋Ÿฌ ๊ตฌํ˜„ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  2. CREATE FUNCTION ๋ช…๋ น์œผ๋กœ SQL๋กœ ์ž‘์„ฑ๋œ UDTF๋ฅผ ๋งŒ๋“ค์–ด ํด๋ž˜์Šค๋ฅผ ํ•ธ๋“ค๋Ÿฌ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. UDTF๋ฅผ ๋งŒ๋“ค ๋•Œ ๋‹ค์Œ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

    • UDTF ์ž…๋ ฅ ๋งค๊ฐœ ๋ณ€์ˆ˜์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž….

    • UDTF์—์„œ ๋ฐ˜ํ™˜๋œ ์—ด์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž….

    • UDTF ํ˜ธ์ถœ ์‹œ ํ•ธ๋“ค๋Ÿฌ๋กœ ์‹คํ–‰ํ•˜๋Š” ์ฝ”๋“œ.

    • ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๊ตฌํ˜„๋˜๋Š” ์–ธ์–ด.

    ๊ตฌ๋ฌธ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ CREATE FUNCTION ์œผ๋กœ UDTF ๋งŒ๋“ค๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

UDF ์‹คํ–‰ํ•˜๊ธฐ ์— ์„ค๋ช…๋œ ๋Œ€๋กœ UDF ๋˜๋Š” UDTF๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

ํ…Œ์ด๋ธ” ํ•จ์ˆ˜(UDTF)์˜ ์ž…๋ ฅ ์ธ์ž 500๊ฐœ, ์ถœ๋ ฅ ์—ด 500๊ฐœ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.

Snowflake๋Š” ํ˜„์žฌ ๋‹ค์Œ Python ๋ฒ„์ „์—์„œ UDF ์ž‘์„ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

  • 3.9

  • 3.10

  • 3.11

  • 3.12

CREATE FUNCTION ๋ฌธ์—์„œ runtime_version ์„ ์›ํ•˜๋Š” ๋ฒ„์ „์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

ํ•ธ๋“ค๋Ÿฌ ๊ตฌํ˜„ํ•˜๊ธฐยถ

UDTF ์ธ์ž ๊ฐ’์„ ํ…Œ์ด๋ธ” ํ˜•์‹์˜ ๊ฒฐ๊ณผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ถ„ํ• ๋œ ์ž…๋ ฅ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์˜ ์˜ˆ๋Š” ์ด ํ•ญ๋ชฉ์˜ ์ฒ˜๋ฆฌ๊ธฐ ํด๋ž˜์Šค์˜ ์˜ˆ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

CREATE FUNCTION์œผ๋กœ UDTF๋ฅผ ๋งŒ๋“ค ๋•Œ ์ด ํด๋ž˜์Šค๋ฅผ UDTF์˜ ํ•ธ๋“ค๋Ÿฌ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“œ๋Š” SQL์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ CREATE FUNCTION ์œผ๋กœ UDTF ๋งŒ๋“ค๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค๋Š” UDTF ํ˜ธ์ถœ ์‹œ Snowflake๊ฐ€ ํ˜ธ์ถœํ•  ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” UDTF์˜ ๋…ผ๋ฆฌ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”์„œ๋“œ

์š”๊ตฌ ์‚ฌํ•ญ

์„ค๋ช…

__init__ ๋ฉ”์„œ๋“œ

์„ ํƒ ์‚ฌํ•ญ

์ž…๋ ฅ ํŒŒํ‹ฐ์…˜์˜ ์ƒํƒœ ์ €์žฅ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ์ฒ˜๋ฆฌ๊ธฐ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

process ๋ฉ”์„œ๋“œ

ํ•„์ˆ˜

๊ฐ ์ž…๋ ฅ ํ–‰์„ ์ฒ˜๋ฆฌํ•˜์—ฌ ํ…Œ์ด๋ธ” ํ˜•์‹ ๊ฐ’์„ ํŠœํ”Œ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. Snowflake๋Š” ์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ UDTF์˜ ์ธ์ž์—์„œ ์ž…๋ ฅ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ process ๋ฉ”์„œ๋“œ ์ •์˜ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

end_partition ๋ฉ”์„œ๋“œ

์„ ํƒ ์‚ฌํ•ญ

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

ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์˜ ์ž„์˜์˜ ๋ฉ”์„œ๋“œ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ฒ˜๋ฆฌ๊ฐ€ ์ค‘์ง€๋ฉ๋‹ˆ๋‹ค. UDTF๋ฅผ ํ˜ธ์ถœํ•œ ์ฟผ๋ฆฌ๋Š” ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

์ฝ”๋“œ๊ฐ€ ์—ฌ๊ธฐ์— ์„ค๋ช…๋œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜์ง€ ์•Š์œผ๋ฉด UDTF ์ƒ์„ฑ ๋˜๋Š” ์‹คํ–‰์ด ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Snowflake๋Š” CREATE FUNCTION ๋ฌธ ์‹คํ–‰ ์‹œ ์œ„๋ฐ˜ ์‚ฌํ•ญ์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์ฒ˜๋ฆฌ๊ธฐ ํด๋ž˜์Šค์˜ ์˜ˆยถ

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ํŒŒํ‹ฐ์…˜์˜ ํ–‰์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋Š” UDTF๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. process ๋ฉ”์„œ๋“œ๋Š” ๊ฐ ์ž…๋ ฅ ํ–‰์„ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ฃผ์‹ ๋งค๋งค์˜ ์ด๋น„์šฉ์ด ์žˆ๋Š” ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํŒŒํ‹ฐ์…˜์˜ ํ–‰์„ ์ฒ˜๋ฆฌํ•œ ํ›„, ํŒŒํ‹ฐ์…˜์— ํฌํ•จ๋œ ๋ชจ๋“  ๋งค๋งค์˜ ํ•ฉ๊ณ„๋ฅผ end_partition ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION stock_sale_sum(symbol VARCHAR, quantity NUMBER, price NUMBER(10,2))
  RETURNS TABLE (symbol VARCHAR, total NUMBER(10,2))
  LANGUAGE PYTHON
  RUNTIME_VERSION = 3.9
  HANDLER = 'StockSaleSum'
AS $$
class StockSaleSum:
    def __init__(self):
        self._cost_total = 0
        self._symbol = ""

    def process(self, symbol, quantity, price):
      self._symbol = symbol
      cost = quantity * price
      self._cost_total += cost
      yield (symbol, cost)

    def end_partition(self):
      yield (self._symbol, self._cost_total)
$$;
Copy

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ์—์„œ๋Š” ์ด์ „ UDF๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ stocks_table ํ…Œ์ด๋ธ”์˜ symbol, quantity ๋ฐ price ์—ด์˜ ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. UDTF ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ UDF ์‹คํ–‰ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

SELECT stock_sale_sum.symbol, total
  FROM stocks_table, TABLE(stock_sale_sum(symbol, quantity, price) OVER (PARTITION BY symbol));
Copy

์ฒ˜๋ฆฌ๊ธฐ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐยถ

ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ–‰ ์ฒ˜๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— Snowflake๊ฐ€ ํ˜ธ์ถœํ•  ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์—์„œ __init__ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ธ๋“ค๋Ÿฌ์— ๋Œ€ํ•ด ํŒŒํ‹ฐ์…˜ ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. __init__ ๋ฉ”์„œ๋“œ๋กœ ์ถœ๋ ฅ ํ–‰์ด ์ƒ์„ฑ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฉ”์„œ๋“œ์˜ ์„œ๋ช…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

def __init__(self):
Copy

์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํŒŒํ‹ฐ์…˜์˜ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ ๋‹ค์Œ process ๋ฐ end_partition ๋ฉ”์„œ๋“œ์—์„œ ์ด ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ํ–‰๋‹น ํ•œ ๋ฒˆ์ด ์•„๋‹ˆ๋ผ ํŒŒํ‹ฐ์…˜๋‹น ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ์žฅ๊ธฐ ์‹คํ–‰ ์ดˆ๊ธฐํ™”๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค ์™ธ๋ถ€์— ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•จ์œผ๋กœ์จ, ํด๋ž˜์Šค ์„ ์–ธ ์ด์ „๊ณผ ๊ฐ™์ด ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ๊ฐ€ ์‹œ์ž‘๋˜๊ธฐ ์ „์— ๋…ผ๋ฆฌ๋ฅผ ํ•œ ๋ฒˆ ์‹คํ–‰ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

__init__ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ __init__ ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ง•์ด ์žˆ๋‹ค๋Š” ์ ์„ ์œ ๋…ํ•˜์‹ญ์‹œ์˜ค.

  • self ๋งŒ ์ธ์ž๋กœ ์ทจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ถœ๋ ฅ ํ–‰์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ ๋Œ€์‹  process ๋ฉ”์„œ๋“œ ๊ตฌํ˜„์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

  • ๊ฐ ํŒŒํ‹ฐ์…˜์— ๋Œ€ํ•ด ํ•œ ๋ฒˆ, ๊ทธ๋ฆฌ๊ณ  process ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๊ธฐ ์ „์— ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

ํ–‰ ์ฒ˜๋ฆฌํ•˜๊ธฐยถ

Snowflake๊ฐ€ ๊ฐ ์ž…๋ ฅ ํ–‰์— ๋Œ€ํ•ด ํ˜ธ์ถœํ•  process ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

process ๋ฉ”์„œ๋“œ ์ •์˜ํ•˜๊ธฐยถ

SQL ํ˜•์‹์—์„œ ๋ณ€ํ™˜๋œ UDTF ์ธ์ž๋ฅผ ๊ฐ’์œผ๋กœ ๋ฐ›๋Š” process ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜์—ฌ Snowflake๊ฐ€ UDTF์˜ ํ…Œ์ด๋ธ” ํ˜•์‹ ๋ฐ˜ํ™˜ ๊ฐ’์„ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฉ”์„œ๋“œ์˜ ์„œ๋ช…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

def process(self, *args):
Copy

process ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ ์กฐ๊ฑด์„ ์ถฉ์กฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • self ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • UDTF ๋งค๊ฐœ ๋ณ€์ˆ˜์— ํ•ด๋‹นํ•˜๋Š” ๋ฉ”์„œ๋“œ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ๋ฉ”์„œ๋“œ ๋งค๊ฐœ ๋ณ€์ˆ˜ ์ด๋ฆ„์ด UDTF ๋งค๊ฐœ ๋ณ€์ˆ˜ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ, UDTF ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์„ ์–ธ๋œ ๊ฒƒ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ๋ฉ”์„œ๋“œ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    UDTF ์ธ์ž ๊ฐ’์„ ๋ฉ”์„œ๋“œ์— ์ „๋‹ฌํ•  ๋•Œ Snowflake๋Š” ๊ฐ’์„ SQL ํ˜•์‹์—์„œ ๋ฉ”์„œ๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋Š” Python ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. Snowflake๊ฐ€ SQL ๋ฐ์ดํ„ฐ ํƒ€์ž…๊ณผ Python ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฐ„์— ๋งคํ•‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ SQL-Python ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋งคํ•‘ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  • ํ•˜๋‚˜ ์ด์ƒ์˜ ํŠœํ”Œ์„ ์ƒ์„ฑ(๋˜๋Š” ํŠœํ”Œ์„ ํฌํ•จํ•œ ์ดํ„ฐ๋Ÿฌ๋ธ”์„ ๋ฐ˜ํ™˜)ํ•ด์•ผ ํ•˜๋ฉฐ, ์ด๋•Œ ํŠœํ”Œ์˜ ์‹œํ€€์Šค๋Š” UDTF ๋ฐ˜ํ™˜ ๊ฐ’ ์—ด์˜ ์‹œํ€€์Šค์™€ ์ƒ์‘ํ•ฉ๋‹ˆ๋‹ค.

    ํŠœํ”Œ ์š”์†Œ๋Š” UDTF ๋ฐ˜ํ™˜ ๊ฐ’ ์—ด์ด ์„ ์–ธ๋œ ๊ฒƒ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ๋‚˜ํƒ€๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ๊ฐ’ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

    Snowflake๋Š” ๊ฐ’์„ Python ํ˜•์‹์—์„œ UDTF ์„ ์–ธ์—์„œ ์š”๊ตฌ๋˜๋Š” SQL ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. Snowflake๊ฐ€ SQL ๋ฐ์ดํ„ฐ ํƒ€์ž…๊ณผ Python ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ฐ„์— ๋งคํ•‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ SQL-Python ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋งคํ•‘ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ฒ˜๋ฆฌ๊ฐ€ ์ค‘์ง€๋ฉ๋‹ˆ๋‹ค. UDTF๋ฅผ ํ˜ธ์ถœํ•œ ์ฟผ๋ฆฌ๋Š” ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. process ๋ฉ”์„œ๋“œ๊ฐ€ None ์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์ฒ˜๋ฆฌ๊ฐ€ ์ค‘์ง€๋ฉ๋‹ˆ๋‹ค. (process ๋ฉ”์„œ๋“œ๊ฐ€ None ์„ ๋ฐ˜ํ™˜ํ•˜๋”๋ผ๋„ end_partition ๋ฉ”์„œ๋“œ๋Š” ์—ฌ์ „ํžˆ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.)

process ๋ฉ”์„œ๋“œ์˜ ์˜ˆ

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์„ธ UDTF ์ธ์ž(symbol, quantity, price)๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋‘ ์—ด(symbol, total)์ด ์žˆ๋Š” ๋‹จ์ผ ํ–‰์„ ๋ฐ˜ํ™˜ํ•˜๋Š” process ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” StockSale ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. process ๋ฉ”์„œ๋“œ ๋งค๊ฐœ ๋ณ€์ˆ˜๋Š” stock_sale ๋งค๊ฐœ ๋ณ€์ˆ˜์™€ ๋™์ผํ•œ ์ˆœ์„œ๋กœ ์„ ์–ธ๋ฉ๋‹ˆ๋‹ค. process ๋ฉ”์„œ๋“œ์˜ yield ๋ฌธ์— ์žˆ๋Š” ์ธ์ž๋Š” stock_sale RETURNS TABLE ์ ˆ์— ์„ ์–ธ๋œ ์—ด๊ณผ ๊ฐ™์€ ์ˆœ์„œ์ž…๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION stock_sale(symbol VARCHAR, quantity NUMBER, price NUMBER(10,2))
  RETURNS TABLE (symbol VARCHAR, total NUMBER(10,2))
  LANGUAGE PYTHON
  RUNTIME_VERSION = 3.9
  HANDLER = 'StockSale'
AS $$
class StockSale:
    def process(self, symbol, quantity, price):
      cost = quantity * price
      yield (symbol, cost)
$$;
Copy

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ์—์„œ๋Š” ์ด์ „ UDF๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ stocks_table ํ…Œ์ด๋ธ”์˜ symbol, quantity ๋ฐ price ์—ด์˜ ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

SELECT stock_sale.symbol, total
  FROM stocks_table, TABLE(stock_sale(symbol, quantity, price) OVER (PARTITION BY symbol));
Copy

๊ฐ’ ๋ฐ˜ํ™˜ํ•˜๊ธฐยถ

์ถœ๋ ฅ ํ–‰์„ ๋ฐ˜ํ™˜ํ•  ๋•Œ yield ๋˜๋Š” return (๋‹จ, ๋‘˜ ๋ชจ๋‘๋Š” ์•„๋‹˜)์„ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ด๋ธ” ํ˜•์‹ ๊ฐ’์ด ์žˆ๋Š” ํŠœํ”Œ์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ๊ฐ€ None ์„ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ์ƒ์„ฑํ•˜๋ฉด ํ˜„์žฌ ํ–‰์˜ ์ฒ˜๋ฆฌ๊ฐ€ ์ค‘์ง€๋ฉ๋‹ˆ๋‹ค.

  • yield ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๊ฐ ์ถœ๋ ฅ ํ–‰์— ๋Œ€ํ•ด ๋ณ„๋„์˜ yield ๋ฌธ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” yield ์™€ ํ•จ๊ป˜ ์ˆ˜๋ฐ˜๋˜๋Š” ์ง€์—ฐ ํ‰๊ฐ€๋ฅผ ํ†ตํ•ด ๋ณด๋‹ค ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋ฐฉ์ง€์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ชจ๋ฒ” ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.

    ํŠœํ”Œ์˜ ๊ฐ ์š”์†Œ๋Š” UDTF๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฐ๊ณผ์—์„œ ์—ด ๊ฐ’์ด ๋ฉ๋‹ˆ๋‹ค. yield ์ธ์ž์˜ ์ˆœ์„œ๋Š” CREATE FUNCTION์˜ RETURNS TABLE ์ ˆ์—์„œ ๋ฐ˜ํ™˜ ๊ฐ’์— ๋Œ€ํ•ด ์„ ์–ธ๋œ ์—ด์˜ ์ˆœ์„œ์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋Š” ๋‘ ํ–‰์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    def process(self, symbol, quantity, price):
      cost = quantity * price
      yield (symbol, cost)
      yield (symbol, cost)
    
    Copy

    yield ์ธ์ž๋Š” ํŠœํ”Œ์ด๋ฏ€๋กœ ๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ํŠœํ”Œ์˜ ๋‹จ์ผ ๊ฐ’์„ ์ „๋‹ฌํ•  ๋•Œ ํ›„ํ–‰ ์‰ผํ‘œ๋ฅผ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    yield (cost,)
    
    Copy
  • return ์‚ฌ์šฉ ์‹œ, ํŠœํ”Œ์ด ์žˆ๋Š” ์ดํ„ฐ๋Ÿฌ๋ธ”์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    ํŠœํ”Œ์—์„œ ๊ฐ๊ฐ์˜ ๊ฐ’์€ UDTF๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฐ๊ณผ์—์„œ ์—ด ๊ฐ’์ด ๋ฉ๋‹ˆ๋‹ค. ํŠœํ”Œ์—์„œ ์—ด ๊ฐ’์˜ ์ˆœ์„œ๋Š” CREATE FUNCTION์˜ RETURNS TABLE ์ ˆ์—์„œ ๋ฐ˜ํ™˜ ๊ฐ’์— ๋Œ€ํ•ด ์„ ์–ธ๋œ ์—ด์˜ ์ˆœ์„œ์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋Š” ๊ฐ๊ฐ symbol๊ณผ total์ด๋ผ๋Š” ๋‘ ์—ด์ด ์žˆ๋Š” ๋‘ ํ–‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    def process(self, symbol, quantity, price):
      cost = quantity * price
      return [(symbol, cost), (symbol, cost)]
    
    Copy

ํ–‰ ๊ฑด๋„ˆ๋›ฐ๊ธฐยถ

์ž…๋ ฅ ํ–‰์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๊ทธ๋‹ค์Œ ํ–‰์„ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด(์˜ˆ: ์ž…๋ ฅ ํ–‰์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•  ๋•Œ) process ๋ฉ”์„œ๋“œ๊ฐ€ ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜์‹ญ์‹œ์˜ค.

  • return ์„ ์‚ฌ์šฉํ•  ๋•Œ ํ–‰์„ ๊ฑด๋„ˆ๋›ฐ๋ ค๋ฉด None, None ์„ ํฌํ•จํ•œ ๋ชฉ๋ก ๋˜๋Š” ๋นˆ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•˜์‹ญ์‹œ์˜ค.

  • yield ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ–‰์„ ๊ฑด๋„ˆ๋›ฐ๋ ค๋ฉด None ์„ ๋ฐ˜ํ™˜ํ•˜์‹ญ์‹œ์˜ค.

    yield ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ Snowflake์—์„œ๋Š” None ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ˜ธ์ถœ ์ดํ›„์˜ ํ˜ธ์ถœ์€ ์ „๋ถ€ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋Š” number ๊ฐ€ ์–‘์˜ ์ •์ˆ˜์ธ ํ–‰๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. number ๊ฐ€ ์–‘์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ ๋ฉ”์„œ๋“œ๋Š” ํ˜„์žฌ ํ–‰์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋‹ค์Œ ํ–‰์„ ๊ณ„์† ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด None ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

def process(self, number):
  if number < 1:
    yield None
  else:
    yield (number)
Copy

์ƒํƒœ ์ €์žฅ ๋ฐ ์ƒํƒœ ๋น„์ €์žฅ ์ฒ˜๋ฆฌยถ

ํŒŒํ‹ฐ์…˜ ์ธ์‹ ๋ฐฉ์‹์œผ๋กœ ํ–‰์„ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋‹จ์ˆœํžˆ ํ–‰ ๋‹จ์œ„๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํŒŒํ‹ฐ์…˜ ์ธ์‹ ์ฒ˜๋ฆฌ ์—์„œ๋Š” ํ•ธ๋“ค๋Ÿฌ์— ํŒŒํ‹ฐ์…˜ ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ ์‹œ์ž‘ ์‹œ์— ์‹คํ–‰๋˜๋Š” __init__ ๋ฉ”์„œ๋“œ์™€ ํŒŒํ‹ฐ์…˜์˜ ๋งˆ์ง€๋ง‰ ํ–‰์„ ์ฒ˜๋ฆฌํ•œ ํ›„ Snowflake๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” end_partition ๋ฉ”์„œ๋“œ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  • ํŒŒํ‹ฐ์…˜ ๋น„์ธ์‹ ์ฒ˜๋ฆฌ ์—์„œ๋Š” ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํŒŒํ‹ฐ์…˜ ๊ฒฝ๊ณ„๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

    ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ด์™€ ๊ฐ™์ด ์‹คํ–‰๋˜๋„๋ก ํ•˜๋ ค๋ฉด __init__ ๋˜๋Š” end_partition ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌํ•˜๊ธฐยถ

ํŒŒํ‹ฐ์…˜๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ(์˜ˆ: ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์ฝ”๋“œ)์™€ ํŒŒํ‹ฐ์…˜์˜ ๊ฐ ํ–‰์— ๋Œ€ํ•ด ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋กœ ์ž…๋ ฅ์—์„œ ํŒŒํ‹ฐ์…˜์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

UDTF ํ˜ธ์ถœ ์‹œ ํŒŒํ‹ฐ์…˜ ์ง€์ •์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ํ…Œ์ด๋ธ” ํ•จ์ˆ˜ ๋ฐ ํŒŒํ‹ฐ์…˜ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

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

๋‹ค์Œ SQL ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋Š” ์ฃผ์‹ ๋งค๋งค ์ •๋ณด๋ฅผ ์ฟผ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” ์ž…๋ ฅ์ด symbol ์—ด์˜ ๊ฐ’์œผ๋กœ ๋ถ„ํ• ๋œ stock_sale_sum UDTF๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

SELECT stock_sale_sum.symbol, total
  FROM stocks_table, TABLE(stock_sale_sum(symbol, quantity, price) OVER (PARTITION BY symbol));
Copy

์ˆ˜์‹  ํ–‰์ด ๋ถ„ํ• ๋˜์–ด ์žˆ๋”๋ผ๋„ ์ฝ”๋“œ์—์„œ ํŒŒํ‹ฐ์…˜ ๋ถ„๋ฆฌ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ๊ทธ๋ƒฅ ํ–‰์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์œ ๋…ํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค __init__ ๋ฉ”์„œ๋“œ ๋ฐ end_partition ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์€ ํŒŒํ‹ฐ์…˜ ๋ฒ”์œ„ ์ง€์ • ์ƒํƒœ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„๋œ ์ฝ”๋“œ๋ฅผ ์ƒ๋žตํ•˜๊ณ  process ๋ฉ”์„œ๋“œ๋ฅผ ๊ทธ๋ƒฅ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ์ƒํƒœ ์ €์žฅ ๋ฐ ์ƒํƒœ ๋น„์ €์žฅ ์ฒ˜๋ฆฌ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๊ฐ ํŒŒํ‹ฐ์…˜์„ ํ•˜๋‚˜์˜ ๋‹จ์œ„๋กœ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  • ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค __init__ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

    ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ์ฒ˜๋ฆฌ๊ธฐ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  • process ๋ฉ”์„œ๋“œ๋กœ ๊ฐ ํ–‰์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ํŒŒํ‹ฐ์…˜ ์ธ์‹ ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

    ํ–‰ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ํ–‰ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  • end_partition ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

    ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ ๋งˆ๋ฌด๋ฆฌํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๋‹ค์Œ์€ ํŒŒํ‹ฐ์…˜๋ณ„๋กœ ์‹คํ–‰ํ•˜๋„๋ก ์„ค๊ณ„๋œ ์ฝ”๋“œ๋ฅผ ํฌํ•จํ–ˆ์„ ๋•Œ ํ•ธ๋“ค๋Ÿฌ์˜ ํ˜ธ์ถœ ์‹œํ€€์Šค์— ๋Œ€ํ•œ ์„ค๋ช…์ž…๋‹ˆ๋‹ค.

  1. ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ๊ฐ€ ์‹œ์ž‘๋˜์–ด ์ฒซ ๋ฒˆ์งธ ํ–‰์˜ ์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ ์ „์—, Snowflake๋Š” ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์˜ __init__ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

    ์—ฌ๊ธฐ์„œ๋Š” ํŒŒํ‹ฐ์…˜ ๋ฒ”์œ„ ์ง€์ • ์ƒํƒœ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํŒŒํ‹ฐ์…˜์˜ ํ–‰์—์„œ ๊ณ„์‚ฐ๋œ ๊ฐ’์„ ๋ณด์œ ํ•˜๋Š” ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  2. ํŒŒํ‹ฐ์…˜์˜ ๊ฐ ํ–‰์— ๋Œ€ํ•ด Snowflake๋Š” process ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

    ์ด ๋ฉ”์„œ๋“œ๋Š” ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ์ƒํƒœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, process ๋ฉ”์„œ๋“œ๊ฐ€ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  3. Snowflake๋Š” ์ฝ”๋“œ๊ฐ€ ํŒŒํ‹ฐ์…˜์˜ ๋งˆ์ง€๋ง‰ ํ–‰์„ ์ฒ˜๋ฆฌํ•œ ํ›„์— end_partition ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

    ์ด ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ˜ํ™˜ํ•˜๋ ค๋Š” ํŒŒํ‹ฐ์…˜ ์ˆ˜์ค€ ๊ฐ’์ด ํฌํ•จ๋œ ์ถœ๋ ฅ ํ–‰์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํŒŒํ‹ฐ์…˜์˜ ํ–‰์„ ์ฒ˜๋ฆฌํ•˜๋ฉด์„œ ์—…๋ฐ์ดํŠธํ•œ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    end_partition ๋ฉ”์„œ๋“œ๋Š” ํŒŒํ‹ฐ์…˜์˜ ๋งˆ์ง€๋ง‰ ํ–‰์„ ์ฒ˜๋ฆฌํ•œ ํ›„ ๋‹จ์ˆœํžˆ ํ˜ธ์ถœํ•˜๊ธฐ๋งŒ ํ•˜๋Š” Snowflake์—์„œ๋Š” ์–ด๋–ค ์ธ์ž๋„ ๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ ๋งˆ๋ฌด๋ฆฌํ•˜๊ธฐยถ

ํŒŒํ‹ฐ์…˜์˜ ๋ชจ๋“  ํ–‰์„ ์ฒ˜๋ฆฌํ•œ ํ›„ Snowflake๊ฐ€ ํ˜ธ์ถœํ•  ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์—์„œ end_partition ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ์—์„œ๋Š” ํŒŒํ‹ฐ์…˜์˜ ๋ชจ๋“  ํ–‰์ด ์ฒ˜๋ฆฌ๋œ ํ›„ ํŒŒํ‹ฐ์…˜์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. end_partition ๋ฉ”์„œ๋“œ๋Š” ํŒŒํ‹ฐ์…˜ ๋ฒ”์œ„ ์ง€์ • ๊ณ„์‚ฐ์˜ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜๊ณผ ๊ฐ™์€ ์ถœ๋ ฅ ํ–‰์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ํ•ญ๋ชฉ์˜ ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ด ๋ฉ”์„œ๋“œ์˜ ์„œ๋ช…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

def end_partition(self):
Copy

Snowflake๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ end_partition ๋ฉ”์„œ๋“œ ๊ตฌํ˜„์„ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฉ”์„œ๋“œ๊ฐ€ ์ •์ ์ด์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฉ”์„œ๋“œ์— self ์ด์™ธ์—๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํ…Œ์ด๋ธ” ํ˜•์‹ ๊ฐ’ ๋ฐ˜ํ™˜์˜ ๋Œ€์ฒด ์ˆ˜๋‹จ์œผ๋กœ, ๋นˆ ๋ชฉ๋ก ๋˜๋Š” None ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

Snowflake๋Š” ์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์‹œ๊ฐ„ ์ œํ•œ์ด ์กฐ์ •๋œ ๋Œ€ํ˜• ํŒŒํ‹ฐ์…˜์„ ์ง€์›ํ•˜์ง€๋งŒ, ํŠนํžˆ ๋Œ€ํ˜• ํŒŒํ‹ฐ์…˜์œผ๋กœ ์ธํ•ด ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์ดˆ๊ณผ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: end_partition ์ด ์™„๋ฃŒํ•˜๋Š” ๋ฐ ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ). ํŠน์ • ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋งž๊ฒŒ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์ž„๊ณ„๊ฐ’์„ ์กฐ์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ Snowflake ์ง€์› ์— ๋ฌธ์˜ํ•˜์‹ญ์‹œ์˜ค.

ํŒŒํ‹ฐ์…˜ ์ฒ˜๋ฆฌ์˜ ์˜ˆยถ

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด (process ๋ฉ”์„œ๋“œ์—์„œ) ๋จผ์ € ๋งค์ž…๋‹น ๋น„์šฉ์„ ๊ณ„์‚ฐํ•ด ๋งค์ž… ๊ธˆ์•ก์„ ํ•จ๊ป˜ ๋”ํ•จ์œผ๋กœ์จ ์–ด๋–ค ์ฃผ์‹์˜ ๋งค์ž…์„ ์œ„ํ•ด ์ง€๋ถˆํ•œ ์ด๋น„์šฉ์ด ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” end_partition ๋ฉ”์„œ๋“œ์—์„œ ์ด๊ณ„๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

UDTF ํ˜ธ์ถœ๊ณผ ํ•จ๊ป˜ ์ด ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ํฌํ•จํ•˜๋Š” UDTF์˜ ์˜ˆ๋Š” ์ฒ˜๋ฆฌ๊ธฐ ํด๋ž˜์Šค์˜ ์˜ˆ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

class StockSaleSum:
  def __init__(self):
    self._cost_total = 0
    self._symbol = ""

  def process(self, symbol, quantity, price):
    self._symbol = symbol
    cost = quantity * price
    self._cost_total += cost
    yield (symbol, cost)

  def end_partition(self):
    yield (self._symbol, self._cost_total)
Copy

ํŒŒํ‹ฐ์…˜์„ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” ๋‹ค์Œ ์‚ฌํ•ญ์„ ์œ ๋…ํ•˜์‹ญ์‹œ์˜ค.

  • ์‚ฌ์šฉ์ž์˜ ์ฝ”๋“œ๊ฐ€ UDTF์— ๋Œ€ํ•œ ํ˜ธ์ถœ์— ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •๋˜์ง€ ์•Š์€ ํŒŒํ‹ฐ์…˜์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UDTF์— ๋Œ€ํ•œ ํ˜ธ์ถœ์— PARTITION BY ์ ˆ์ด ํฌํ•จ๋˜์ง€ ์•Š๋”๋ผ๋„ Snowflake๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์•”์‹œ์ ์œผ๋กœ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.

  • process ๋ฉ”์„œ๋“œ๋Š” ํŒŒํ‹ฐ์…˜์˜ ORDER BY ์ ˆ์— ์ง€์ •๋œ ์ˆœ์„œ๋Œ€๋กœ ํ–‰ ๋ฐ์ดํ„ฐ(์žˆ๋Š” ๊ฒฝ์šฐ)๋ฅผ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆยถ

๊ฐ€์ ธ์˜จ ํŒจํ‚ค์ง€ ์‚ฌ์šฉํ•˜๊ธฐยถ

Snowflake์—์„œ ์ œ๊ณต๋˜๋Š” Anaconda์˜ ์„ ๋ณ„๋œ ์„œ๋“œ ํŒŒํ‹ฐ ํŒจํ‚ค์ง€ ๋ชฉ๋ก์— ํฌํ•จ๋œ Python ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํŒจํ‚ค์ง€๋ฅผ UDTF์˜ ์ข…์† ํ•ญ๋ชฉ์œผ๋กœ ์ง€์ •ํ•˜๋ ค๋ฉด CREATE FUNCTION์—์„œ PACKAGES ์ ˆ์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

Snowflake์—์„œ ๋‹ค์Œ SQL์„ ์‹คํ–‰ํ•˜์—ฌ ํฌํ•จ๋œ ํŒจํ‚ค์ง€์˜ ๋ชฉ๋ก์„ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'python';
Copy

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์„œ๋“œ ํŒŒํ‹ฐ ํŒจํ‚ค์ง€ ์‚ฌ์šฉํ•˜๊ธฐ ๋ฐ Python UDF ๋งŒ๋“ค๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ์—์„œ๋Š” NumPy(Numerical Python) ํŒจํ‚ค์ง€์˜ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ๊ฐ ์ฃผ๋‹น ๊ฐ€๊ฒฉ์ด ๋‹ค๋ฅธ ์ฃผ์‹ ๋งค์ž… ๋ฐฐ์—ด์—์„œ ์ฃผ๋‹น ํ‰๊ท  ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION stock_sale_average(symbol VARCHAR, quantity NUMBER, price NUMBER(10,2))
  RETURNS TABLE (symbol VARCHAR, total NUMBER(10,2))
  LANGUAGE PYTHON
  RUNTIME_VERSION = 3.9
  PACKAGES = ('numpy')
  HANDLER = 'StockSaleAverage'
AS $$
import numpy as np

class StockSaleAverage:
    def __init__(self):
      self._price_array = []
      self._quantity_total = 0
      self._symbol = ""

    def process(self, symbol, quantity, price):
      self._symbol = symbol
      self._price_array.append(float(price))
      cost = quantity * price
      yield (symbol, cost)

    def end_partition(self):
      np_array = np.array(self._price_array)
      avg = np.average(np_array)
      yield (self._symbol, avg)
$$;
Copy

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ์—์„œ๋Š” ์ด์ „ UDF๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ stocks_table ํ…Œ์ด๋ธ”์˜ symbol, quantity ๋ฐ price ์—ด์˜ ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. UDTF ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ UDF ์‹คํ–‰ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

SELECT stock_sale_average.symbol, total
  FROM stocks_table,
  TABLE(stock_sale_average(symbol, quantity, price)
    OVER (PARTITION BY symbol));
Copy

์›Œ์ปค ํ”„๋กœ์„ธ์Šค๋กœ ๋™์‹œ ์ž‘์—… ์‹คํ–‰ํ•˜๊ธฐยถ

Python ์ž‘์—…์ž ํ”„๋กœ์„ธ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์‹œ ์ž‘์—…์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›จ์–ดํ•˜์šฐ์Šค ๋…ธ๋“œ์—์„œ ์—ฌ๋Ÿฌ CPU ์ฝ”์–ด๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ณ‘๋ ฌ ์ž‘์—…์„ ์‹คํ–‰ํ•ด์•ผ ํ•  ๋•Œ ์ด ๊ธฐ๋Šฅ์ด ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

๊ธฐ๋ณธ ์ œ๊ณต๋œ Python ๋‹ค์ค‘ ์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

Python Global Interpreter Lock ์œผ๋กœ ์ธํ•ด ๋ฉ€ํ‹ฐํƒœ์Šคํ‚น ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋ชจ๋“  CPU ์ฝ”์–ด์—์„œ ํ™•์žฅ๋˜์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์Šค๋ ˆ๋“œ๊ฐ€ ์•„๋‹Œ ๋ณ„๋„์˜ ์ž‘์—…์ž ํ”„๋กœ์„ธ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์‹œ ์ž‘์—…์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์—์„œ์ฒ˜๋Ÿผ joblib ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ Parallel ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Snowflake ์›จ์–ดํ•˜์šฐ์Šค์—์„œ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION joblib_multiprocessing_udtf(i INT)
  RETURNS TABLE (result INT)
  LANGUAGE PYTHON
  RUNTIME_VERSION = 3.9
  HANDLER = 'JoblibMultiprocessing'
  PACKAGES = ('joblib')
AS $$
import joblib
from math import sqrt

class JoblibMultiprocessing:
  def process(self, i):
    pass

  def end_partition(self):
    result = joblib.Parallel(n_jobs=-1)(joblib.delayed(sqrt)(i ** 2) for i in range(10))
    for r in result:
      yield (r, )
$$;
Copy

์ฐธ๊ณ 

joblib.Parallel ์— ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋ณธ ๋ฐฑ์—”๋“œ๋Š” Snowflake ํ‘œ์ค€๊ณผ Snowpark ์ตœ์ ํ™” ์›จ์–ดํ•˜์šฐ์Šค ๊ฐ„์— ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

  • ํ‘œ์ค€ ์›จ์–ดํ•˜์šฐ์Šค ๊ธฐ๋ณธ๊ฐ’: threading

  • Snowpark ์ตœ์ ํ™” ์›จ์–ดํ•˜์šฐ์Šค ๊ธฐ๋ณธ๊ฐ’: loky (๋‹ค์ค‘ ์ฒ˜๋ฆฌ)

๋‹ค์Œ ์˜ˆ์—์„œ์ฒ˜๋Ÿผ joblib.parallel_backend ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ธฐ๋ณธ ๋ฐฑ์—”๋“œ ์„ค์ •์„ ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import joblib
joblib.parallel_backend('loky')
Copy

CREATE FUNCTION ์œผ๋กœ UDTF ๋งŒ๋“ค๊ธฐยถ

CREATE FUNCTION ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ SQL๋กœ UDTF๋ฅผ ๋งŒ๋“ค์–ด ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ํ•ธ๋“ค๋Ÿฌ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ช…๋ น ์ฐธ์กฐ๋Š” CREATE FUNCTION ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

UDTF๋ฅผ ๋งŒ๋“ค ๋•Œ ๋‹ค์Œ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

CREATE OR REPLACE FUNCTION <name> ( [ <arguments> ] )
  RETURNS TABLE ( <output_column_name> <output_column_type> [, <output_column_name> <output_column_type> ... ] )
  LANGUAGE PYTHON
  [ IMPORTS = ( '<imports>' ) ]
  RUNTIME_VERSION = 3.9
  [ PACKAGES = ( '<package_name>' [, '<package_name>' . . .] ) ]
  [ TARGET_PATH = '<stage_path_and_file_name_to_write>' ]
  HANDLER = '<handler_class>'
  [ AS '<python_code>' ]
Copy

์ž‘์„ฑํ•œ ํ•ธ๋“ค๋Ÿฌ ์ฝ”๋“œ๋ฅผ UDTF์™€ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด CREATE FUNCTION ์‹คํ–‰ ์‹œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜์‹ญ์‹œ์˜ค.

  • RETURNS TABLE์—์„œ ์—ด ์ด๋ฆ„ ๋ฐ ํ˜•์‹ ์Œ์— ์ถœ๋ ฅ ์—ด์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  • LANGUAGE๋ฅผ PYTHON์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

  • ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค๊ฐ€ ์Šคํ…Œ์ด์ง€์™€ ๊ฐ™์€ ์™ธ๋ถ€ ์œ„์น˜์— ์žˆ๋Š” ๊ฒฝ์šฐ IMPORTS ์ ˆ ๊ฐ’์„ ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์˜ ๊ฒฝ๋กœ์™€ ์ด๋ฆ„์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    ์ž์„ธํ•œ ๋‚ด์šฉ์€ Python UDF ๋งŒ๋“ค๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  • RUNTIME_VERSION์„ ์ฝ”๋“œ์—์„œ ํ•„์š”ํ•œ Python ๋Ÿฐํƒ€์ž„ ๋ฒ„์ „์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ง€์›๋˜๋Š” Python ๋ฒ„์ „์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

    • 3.9

    • 3.10

    • 3.11

    • 3.12

  • PACKAGES ์ ˆ ๊ฐ’์„ ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์— ํ•„์š”ํ•œ ํ•˜๋‚˜ ์ด์ƒ์˜ ํŒจํ‚ค์ง€(์žˆ๋Š” ๊ฒฝ์šฐ)์˜ ์ด๋ฆ„์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์„œ๋“œ ํŒŒํ‹ฐ ํŒจํ‚ค์ง€ ์‚ฌ์šฉํ•˜๊ธฐ ๋ฐ Python UDF ๋งŒ๋“ค๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  • HANDLER ์ ˆ ๊ฐ’์„ ํ•ธ๋“ค๋Ÿฌ ํด๋ž˜์Šค์˜ ์ด๋ฆ„์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    Python ํ•ธ๋“ค๋Ÿฌ ์ฝ”๋“œ๋ฅผ UDTF์™€ ์—ฐ๊ฒฐํ•  ๋•Œ ์ฝ”๋“œ๋ฅผ ์ธ๋ผ์ธ์œผ๋กœ ํฌํ•จํ•˜๊ฑฐ๋‚˜ Snowflake ์Šคํ…Œ์ด์ง€์ƒ์˜ ํ•œ ์œ„์น˜์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. HANDLER ๊ฐ’์€ ๋Œ€/์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜๋ฉฐ Python ํด๋ž˜์Šค์˜ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ธ๋ผ์ธ ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” UDFs์™€ ์Šคํ…Œ์ด์ง€์—์„œ ์—…๋กœ๋“œ๋œ ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” UDFs ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

    ์ค‘์š”

    ์Šค์นผ๋ผ Python UDF์˜ ๊ฒฝ์šฐ HANDLER ์ ˆ ๊ฐ’์—๋Š” ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

    Python UDTF์˜ ๊ฒฝ์šฐ HANDLER ์ ˆ ๊ฐ’์—๋Š” ํด๋ž˜์Šค ์ด๋ฆ„์ด ํฌํ•จ๋˜์ง€๋งŒ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์€ ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    ์ด๋Ÿฐ ์ฐจ์ด๊ฐ€ ๋‚˜๋Š” ์ด์œ ๋Š” ์Šค์นผ๋ผ Python UDF์˜ ๊ฒฝ์šฐ, ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•˜๋ฏ€๋กœ Snowflake๊ฐ€ ๋ฏธ๋ฆฌ ์•Œ์ง€ ๋ชปํ•˜์ง€๋งŒ, Python UDTF์˜ ๊ฒฝ์šฐ ๋ฉ”์„œ๋“œ(์˜ˆ: end_partition ๋ฉ”์„œ๋“œ)์˜ ์ด๋ฆ„์€ Snowflake์— ์˜ํ•ด ์ง€์ •๋œ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์•Œ๋ ค์ ธ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

  • ํ•ธ๋“ค๋Ÿฌ ์ฝ”๋“œ๊ฐ€ CREATE FUNCTION๊ณผ ํ•จ๊ป˜ ์ธ๋ผ์ธ์œผ๋กœ ์ง€์ •๋œ ๊ฒฝ์šฐ AS '<python_code>' ์ ˆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.