ํ…Œ์ด๋ธ” ํ˜•์‹ JavaScript UDF(UDTF)ยถ

JavaScript์—์„œ ์‚ฌ์šฉ์ž ์ •์˜ ํ…Œ์ด๋ธ” ํ•จ์ˆ˜ (UDTF)์˜ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

UDTF๊ฐ€ ์œ ํšจํ•˜๋ ค๋ฉด JavaScript ์ฝ”๋“œ๊ฐ€ ๋‹ค์Œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์ฝ”๋“œ๋Š” ๋‹จ์ผ ๋ฆฌํ„ฐ๋Ÿด JavaScript ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์ •์˜๋œ ์˜ค๋ธŒ์ ํŠธ๋Š” processRow() ๋ผ๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์˜ค๋ธŒ์ ํŠธ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ค‘์š”

JavaScript ์ฝ”๋“œ๊ฐ€ ์ด๋Ÿฌํ•œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, UDTF๋Š” ๊ณ„์† ๋งŒ๋“ค์–ด์ง€์ง€๋งŒ, ์ฟผ๋ฆฌ์—์„œ ํ˜ธ์ถœ ์‹œ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

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

์˜ค๋ธŒ์ ํŠธ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ยถ

JavaScript ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด Snowflake๋Š” ์ฟผ๋ฆฌ ์‹คํ–‰ ์ค‘์— ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ UDTF์™€ ์ƒํ˜ธ ์ž‘์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์Šค์ผˆ๋ ˆํ†ค์€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ์ฝœ๋ฐฑ ํ•จ์ˆ˜์™€ ์˜ˆ์ƒ๋˜๋Š” ์„œ๋ช…์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

{
   processRow: function (row, rowWriter, context) {/*...*/},
   finalize: function (rowWriter, context) {/*...*/},
   initialize: function (argumentInfo, context) {/*...*/},
}
Copy

processRow() ๋งŒ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ•จ์ˆ˜๋Š” ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.

processRow()ยถ

์ด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ์ž…๋ ฅ ๊ด€๊ณ„์˜ ๊ฐ ํ–‰์— ๋Œ€ํ•ด ํ•œ ๋ฒˆ์”ฉ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. processRow() ์— ๋Œ€ํ•œ ์ธ์ž๋Š” row ์˜ค๋ธŒ์ ํŠธ์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. UDTF๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉ๋œ CREATE FUNCTION ๋ฌธ์— ์ •์˜๋œ ๊ฐ ์ธ์ž์˜ ๊ฒฝ์šฐ, ๋ชจ๋‘ ๋Œ€๋ฌธ์ž๋กœ ๋œ ๋™์ผํ•œ ์ด๋ฆ„์„ ๊ฐ€์ง„ row ์˜ค๋ธŒ์ ํŠธ์— ์†์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์†์„ฑ์˜ ๊ฐ’์€ ํ˜„์žฌ ํ–‰์— ๋Œ€ํ•œ ์ธ์ž ๊ฐ’์ž…๋‹ˆ๋‹ค. (๊ฐ’์€ JavaScript ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.)

rowWriter ์ธ์ž๋Š” ์‚ฌ์šฉ์ž ์ œ๊ณต ์ฝ”๋“œ์—์„œ ์ถœ๋ ฅ ํ–‰์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. rowWriter ์˜ค๋ธŒ์ ํŠธ๋Š” ๋‹จ์ผ ํ•จ์ˆ˜์ธ writeRow() ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. writeRow() ํ•จ์ˆ˜๋Š” ํ•˜๋‚˜์˜ ์ธ์ž์ธ ํ–‰ ์˜ค๋ธŒ์ ํŠธ ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ ์ด๋Š” JavaScript ์˜ค๋ธŒ์ ํŠธ๋กœ ํ‘œ์‹œ๋˜๋Š” ์ถœ๋ ฅ ํ…Œ์ด๋ธ”์˜ ๋‹จ์ผ ํ–‰์ž…๋‹ˆ๋‹ค. CREATE FUNCTION ๋ช…๋ น์˜ RETURNS ์ ˆ์— ์ •์˜๋œ ๊ฐ ์—ด์˜ ๊ฒฝ์šฐ, ํ–‰ ์˜ค๋ธŒ์ ํŠธ์— ํ•ด๋‹น ์†์„ฑ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ–‰ ์˜ค๋ธŒ์ ํŠธ์˜ ํ•ด๋‹น ์†์„ฑ ๊ฐ’์€ ์ถœ๋ ฅ ๊ด€๊ณ„์—์„œ ํ•ด๋‹น ์—ด์˜ ๊ฐ’์ด ๋ฉ๋‹ˆ๋‹ค. ํ–‰ ์˜ค๋ธŒ์ ํŠธ์— ํ•ด๋‹น ์†์„ฑ์ด ์—†๋Š” ์ถœ๋ ฅ ์—ด์€ ๊ฒฐ๊ณผ ํ…Œ์ด๋ธ”์—์„œ NULL ๊ฐ’์„ ๊ฐ–์Šต๋‹ˆ๋‹ค.

finalize()ยถ

finalize() ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ๋ชจ๋“  ํ–‰์ด processRow() ์— ์ „๋‹ฌ๋œ ํ›„ ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. (๋ฐ์ดํ„ฐ๊ฐ€ ํŒŒํ‹ฐ์…˜ ์œผ๋กœ ๊ทธ๋ฃนํ™”๋˜๋ฉด ํ•ด๋‹น ํŒŒํ‹ฐ์…˜์˜ ๋ชจ๋“  ํ–‰์ด processRow() ์— ์ „๋‹ฌ๋œ ํ›„ ๊ฐ ํŒŒํ‹ฐ์…˜์— ๋Œ€ํ•ด finalize() ๊ฐ€ ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.)

์ด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” processRow() ์— ์ „๋‹ฌ๋œ ๊ฒƒ๊ณผ ๋™์ผํ•œ ํ–‰ rowWriter ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ processRow() ์—์„œ ์ง‘๊ณ„๋˜์—ˆ์„ ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ƒํƒœ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

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

initialize()ยถ

์ด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” processRow() ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— ๊ฐ ํŒŒํ‹ฐ์…˜์— ๋Œ€ํ•ด ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ ๊ณ„์‚ฐ ์ค‘์— ํ•„์š”ํ•œ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•˜๋ ค๋ฉด initialize() ๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

initialize() ํ•จ์ˆ˜์˜ argumentInfo ๋งค๊ฐœ ๋ณ€์ˆ˜์—๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์ธ์ž์— ๊ด€ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, UDF๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜๋œ ๊ฒฝ์šฐ:

CREATE FUNCTION f(argument_1 INTEGER, argument_2 VARCHAR) ...
Copy

๊ทธ๋Ÿฌ๋ฉด argumentInfo ๋Š” argument_1 ๋ฐ argument_2 ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

argumentInfo ์—๋Š” ์ด๋Ÿฌํ•œ ๊ฐ ์ธ์ž์— ๋Œ€ํ•œ ์†์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์†์„ฑ์€ ๋‹ค์Œ ๊ฐ’์„ ๊ฐ€์ง„ ์˜ค๋ธŒ์ ํŠธ์ž…๋‹ˆ๋‹ค.

  • type: ๋ฌธ์ž์—ด์ž…๋‹ˆ๋‹ค. ์ด ์ธ์ž์˜ ํ˜•์‹์ž…๋‹ˆ๋‹ค.

  • isConst: ๋ถ€์šธ์ž…๋‹ˆ๋‹ค. true์ด๋ฉด ์ด ์ธ์ž์˜ ๊ฐ’์€ ์ƒ์ˆ˜์ž…๋‹ˆ๋‹ค(์ฆ‰, ๋ชจ๋“  ํ–‰์— ๋Œ€ํ•ด ๋™์ผํ•จ).

  • constValue: isConst (์œ„์— ์ •์˜๋จ)๊ฐ€ true์ด๋ฉด ์ด ํ•ญ๋ชฉ์—๋Š” ์ธ์ž์˜ ์ƒ์ˆ˜ ๊ฐ’์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ด ํ•„๋“œ๋Š” undefined ์ž…๋‹ˆ๋‹ค.

initialize() ํ•จ์ˆ˜๋Š” ์ถœ๋ ฅ ํ–‰์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ฝœ๋ฐฑ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์ผ๋ฐ˜ ์‚ฌ์šฉ๋ฒ• ๋…ธํŠธยถ

  • ์„ธ ๊ฐ€์ง€ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ๋ชจ๋‘ context ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ–ฅํ›„ ์‚ฌ์šฉ์„ ์œ„ํ•ด ์˜ˆ์•ฝ๋˜๋ฉฐ ํ˜„์žฌ ๋น„์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

    ์กฐ์‹ฌ

    context ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์ •์˜๋˜์ง€ ์•Š์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํ•„์š”์— ๋”ฐ๋ผ UDTF์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์˜ค๋ธŒ์ ํŠธ์— ์ถ”๊ฐ€ ํ•จ์ˆ˜ ๋ฐ ์†์„ฑ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ฝœ๋ฐฑ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์ธ์ž๋Š” ์œ„์น˜ ์ธ์ž์ด๋ฉฐ, ์–ด๋–ค ์ด๋ฆ„์œผ๋กœ๋“  ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ํ•ญ๋ชฉ์˜ ๋ชฉ์ ์„ ์œ„ํ•ด ์œ„์˜ ์ด๋ฆ„์ด ๋‚˜๋จธ์ง€ ์„ค๋ช… ๋ฐ ์˜ˆ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

ํŒŒํ‹ฐ์…˜ยถ

๋งŽ์€ ์ƒํ™ฉ์—์„œ ํ–‰์„ ํŒŒํ‹ฐ์…˜ ์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ„ํ• ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ณตํ†ต ํŠน์„ฑ์— ๋”ฐ๋ผ ํ–‰์„ ๊ทธ๋ฃนํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ทธ๋ฃน ๋‚ด์˜ ๋ชจ๋“  ํ–‰์„ ํ•จ๊ป˜ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ฐ ๊ทธ๋ฃน์„ ๋…๋ฆฝ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด๋ฅผ ํ†ตํ•ด Snowflake๋Š” ์›Œํฌ๋กœ๋“œ๋ฅผ ๋ถ„ํ• ํ•˜์—ฌ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๋ฐ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ์‹๋‹น ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์œผ๋กœ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•œ ๊ฐœ๋ณ„ ํšŒ์‚ฌ์˜ ๋ชจ๋“  ์ฃผ๊ฐ€๋Š” ํ•จ๊ป˜ ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์„œ๋กœ ๋‹ค๋ฅธ ํšŒ์‚ฌ์˜ ๊ทธ๋ฃน์€ ๋…๋ฆฝ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ฌธ์€ ๊ฐœ๋ณ„ ํŒŒํ‹ฐ์…˜์—์„œ js_udtf() ๋ผ๋Š” UDTF๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ํŒŒํ‹ฐ์…˜์—๋Š” PARTITION BY ์‹์ด ๋™์ผ ๊ฐ’(์˜ˆ: ๋™์ผํ•œ ์ฃผ์‹ ๊ธฐํ˜ธ)์œผ๋กœ ํ‰๊ฐ€๋˜๋Š” ๋ชจ๋“  ํ–‰์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY <expression>)) ...;
Copy

UDTF์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ํŒŒํ‹ฐ์…˜ ์‹์„ ์ง€์ •ํ•˜๋ฉด Snowflake๋Š” ๋‹ค์Œ์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

  • initialize(): ๊ฐ ํŒŒํ‹ฐ์…˜์— ๋Œ€ํ•ด ํ•œ ๋ฒˆ์”ฉ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

  • processRow(): ํ•ด๋‹น ํŒŒํ‹ฐ์…˜์˜ ๊ฐ ๊ฐœ๋ณ„ ํ–‰์— ๋Œ€ํ•ด ํ•œ ๋ฒˆ์”ฉ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

  • finalize(): ๊ฐ ํŒŒํ‹ฐ์…˜์— ๋Œ€ํ•ด ํ•œ ๋ฒˆ์”ฉ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค(ํ•ด๋‹น ํŒŒํ‹ฐ์…˜์˜ ๋งˆ์ง€๋ง‰ ํ–‰์„ ์ฒ˜๋ฆฌํ•œ ํ›„).

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

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY <expression> ORDER BY <expression>)) ...;
Copy

ORDER BY ์ ˆ์„ ์ง€์ •ํ•˜๋Š” ๊ฒฝ์šฐ, ORDER BY ์‹์—์„œ ์ •์˜ํ•œ ์ˆœ์„œ๋Œ€๋กœ ํ–‰์ด ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ํŠนํžˆ, ํ–‰์€ ORDER BY ์‹์— ์˜ํ•ด ์ •์˜๋œ ์ˆœ์„œ๋Œ€๋กœ processRow() ์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ, ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„ํ• ํ•˜๋ฉด ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๊ธฐํšŒ๊ฐ€ ๊ฑฐ์˜ ์ž๋™์œผ๋กœ ํ–ฅ์ƒ๋˜์–ด ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค. Snowflake๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์—ฌ๋Ÿฌ UDTF ์ธ์Šคํ„ด์Šค ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. (์ด ๋…ผ์˜์—์„œ JavaScript UDTF์˜ ์ธ์Šคํ„ด์Šค๋Š” Snowflake์—์„œ ํ•จ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” JavaScript ์˜ค๋ธŒ์ ํŠธ์˜ ํ•œ ์ธ์Šคํ„ด์Šค๋กœ ์ •์˜๋ฉ๋‹ˆ๋‹ค.) ํ–‰์˜ ๊ฐ ํŒŒํ‹ฐ์…˜์€ UDTF์˜ ๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

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

๊ฒฐ๊ณผ ์—ดยถ

์œ„์˜ ๋ถ„ํ•  ์˜ˆ์ œ์ฒ˜๋Ÿผ ํ…Œ์ด๋ธ”์ด ํ…Œ์ด๋ธ” ํ•จ์ˆ˜์— ์กฐ์ธ๋˜๋ฉด ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์—๋Š” ์„ ํƒํ•œ ๋‚ด์šฉ์— ๋”ฐ๋ผ ๋‹ค์Œ์ด ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • CREATE FUNCTION ๋ช…๋ น์˜ RETURNS ์ ˆ์— ์ •์˜๋œ ์—ด์ž…๋‹ˆ๋‹ค.

  • ํ…Œ์ด๋ธ”์˜ ์—ด์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„ํ• ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์—ด๊ณผ UDTF์˜ ์ž…๋ ฅ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ๋‹ค๋ฅธ ์—ด์„ ๋ชจ๋‘ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

processRow ์ฝœ๋ฐฑ์—์„œ ์ƒ์„ฑ๋˜๋Š” ํ–‰๊ณผ finalize ์—์„œ ์ƒ์„ฑ๋˜๋Š” ํ–‰์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

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

    processRow ์—์„œ ์ƒ์„ฑ๋œ ํ–‰์˜ ๊ฒฝ์šฐ ๋ชจ๋“  ์ž…๋ ฅ ์—ด์„ ์ถœ๋ ฅ ๊ด€๊ณ„์— ์กฐ์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

finalize ์ฝœ๋ฐฑ์—์„œ Snowflake๋Š” ์ƒ๊ด€ ๊ด€๊ณ„๋ฅผ ์ง€์ •ํ•  โ€œํ˜„์žฌ ํ–‰โ€์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์ผ ํ–‰์— ๋Œ€ํ•ด ์ƒ๊ด€ ๊ด€๊ณ„๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

  • finalize ์ฝœ๋ฐฑ์—์„œ ์ƒ์„ฑ๋œ ํ–‰์˜ ๊ฒฝ์šฐ, PARTITION BY ์ ˆ์— ์‚ฌ์šฉ๋œ ์—ด๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ด๋Ÿฌํ•œ ํ•ญ๋ชฉ์€ ํ˜„์žฌ ํŒŒํ‹ฐ์…˜์˜ ๋ชจ๋“  ํ–‰์— ๋Œ€ํ•ด ๋™์ผํ•˜๋ฏ€๋กœ). ๊ธฐํƒ€ ๋ชจ๋“  ์†์„ฑ์€ NULL์ž…๋‹ˆ๋‹ค. PARTITION BY ์ ˆ์ด ์ง€์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ชจ๋“  ํ•ด๋‹น ํŠน์„ฑ์€ NULL์ž…๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ์—์„œ JavaScript UDTF ํ˜ธ์ถœํ•˜๊ธฐยถ

์ฟผ๋ฆฌ์˜ FROM ์ ˆ์—์„œ UDTF๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ TABLE ํ‚ค์›Œ๋“œ ๋‹ค์Œ์— ์˜ค๋Š” ๊ด„ํ˜ธ ์•ˆ์— UDTF์˜ ์ด๋ฆ„๊ณผ ์ธ์ˆ˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, UDTF๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ TABLE ํ‚ค์›Œ๋“œ์— ๋Œ€ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์–‘์‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

SELECT ...
  FROM TABLE ( udtf_name (udtf_arguments) )
Copy

์ฐธ๊ณ 

UDF ๋ฐ UDTF ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ UDF ์‹คํ–‰ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๋ถ„ํ•  ์—†์Œยถ

์ด ๊ฐ„๋‹จํ•œ ์˜ˆ๋Š” UDTF๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด ์˜ˆ๋Š” ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. UDTF๋Š” ์ „๋‹ฌ๋œ ์ˆœ์„œ์˜ ์—ญ์ˆœ์œผ๋กœ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋ฟ์ž…๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ๋ถ„ํ• ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

SELECT * FROM TABLE(js_udtf(10.0::FLOAT, 20.0::FLOAT));
+----+----+
|  Y |  X |
|----+----|
| 20 | 10 |
+----+----+
Copy

์ด ์˜ˆ๋Š” UDTF๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์˜ ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ js_udtf ๋ผ๋Š” UDTF๋Š” tab1 ์ด๋ผ๋Š” ํ…Œ์ด๋ธ”์˜ ๊ฐ ํ–‰์— ๋Œ€ํ•ด ํ•œ ๋ฒˆ์”ฉ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ํ˜„์žฌ ํ–‰์˜ c1 ๋ฐ c2 ์—ด์—์„œ ๊ฐ’์ด ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ์œ„์™€ ๊ฐ™์ด UDTF๋Š” PARTITION BY ์ ˆ ์—†์ด ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2)) ;
Copy

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

๋ช…์‹œ์  ๋ถ„ํ• ยถ

JavaScript UDTF๋Š” ํŒŒํ‹ฐ์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜ธ์ถœํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ:

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY tab1.c3 ORDER BY tab1.c1));
Copy

๋นˆ OVER ์ ˆ์„ ์‚ฌ์šฉํ•œ ๋ช…์‹œ์  ๋ถ„ํ• ยถ

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER ());
Copy

๋นˆ OVER ์ ˆ์€ ๋ชจ๋“  ํ–‰์ด ๋™์ผ ํŒŒํ‹ฐ์…˜์— ์†ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค(์ฆ‰, ์ „์ฒด ์ž…๋ ฅ ๊ด€๊ณ„๊ฐ€ ํ•˜๋‚˜์˜ ํŒŒํ‹ฐ์…˜์ž„).

์ฐธ๊ณ 

๋นˆ OVER ์ ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ JavaScript UDTF๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋Š” ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” Snowflake๊ฐ€ ํ•จ์ˆ˜์˜ ์ธ์Šคํ„ด์Šค ํ•˜๋‚˜๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ์ œํ•œํ•˜๋ฏ€๋กœ Snowflake๊ฐ€ ๊ณ„์‚ฐ์„ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ƒ˜ํ”Œ JavaScript UDTFยถ

์ด ์„น์…˜์—๋Š” ์—ฌ๋Ÿฌ ์ƒ˜ํ”Œ JavaScript UDTF๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์ธ Hello World ์˜ˆยถ

๋‹ค์Œ JavaScript UDTF๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋™์ผํ•œ ๊ฐ’์„ ํ•ญ์ƒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ฃผ๋กœ ์„ค๋ช… ๋ชฉ์ ์œผ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION HelloWorld0()
    RETURNS TABLE (OUTPUT_COL VARCHAR)
    LANGUAGE JAVASCRIPT
    AS '{
        processRow: function f(row, rowWriter, context){
           rowWriter.writeRow({OUTPUT_COL: "Hello"});
           rowWriter.writeRow({OUTPUT_COL: "World"});
           }
        }';

SELECT output_col FROM TABLE(HelloWorld0());
Copy

์ถœ๋ ฅ:

+------------+
| OUTPUT_COL |
+============+
| Hello      |
+------------+
| World      |
+------------+
Copy

๋‹ค์Œ JavaScript UDTF๋„ ์„ค๋ช…์„ ์œ„ํ•œ ๊ฒƒ์ด์ง€๋งŒ, ์ž…๋ ฅ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. JavaScript๋Š” ๋Œ€/์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€๋งŒ, SQL์€ ์‹๋ณ„์ž๋ฅผ ๋Œ€๋ฌธ์ž๋กœ ๊ฐ•์ œํ•˜๋ฏ€๋กœ JavaScript ์ฝ”๋“œ๊ฐ€ SQL ๋งค๊ฐœ ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ์ฐธ์กฐํ•  ๋•Œ JavaScript ์ฝ”๋“œ๋Š” ๋Œ€๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ, ํ•จ์ˆ˜ ๋งค๊ฐœ ๋ณ€์ˆ˜๋Š” get_params() ํ•จ์ˆ˜์—์„œ row ๋ผ๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด ์•ก์„ธ์Šค๋ฉ๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION HelloHuman(First_Name VARCHAR, Last_Name VARCHAR)
    RETURNS TABLE (V VARCHAR)
    LANGUAGE JAVASCRIPT
    AS '{
        processRow: function get_params(row, rowWriter, context){
           rowWriter.writeRow({V: "Hello"});
           rowWriter.writeRow({V: row.FIRST_NAME});  // Note the capitalization and the use of "row."!
           rowWriter.writeRow({V: row.LAST_NAME});   // Note the capitalization and the use of "row."!
           }
        }';

SELECT V AS Greeting FROM TABLE(HelloHuman('James', 'Kirk'));
Copy

์ถœ๋ ฅ:

+------------+
|  GREETING  |
+============+
| Hello      |
+------------+
| James      |
+------------+
| Kirk       |
+------------+
Copy

์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋ณธ ์˜ˆยถ

๋‹ค์Œ JavaScript UDTF๋Š” ๋ชจ๋“  API ์ฝœ๋ฐฑ ํ•จ์ˆ˜์™€ ๋‹ค์–‘ํ•œ ์ถœ๋ ฅ ์—ด์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ๋ชจ๋“  ํ–‰์„ ์žˆ๋Š” ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ๊ฐ ํŒŒํ‹ฐ์…˜์— ํ‘œ์‹œ๋œ ๋ฌธ์ž ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, THIS ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒํ‹ฐ์…˜์—์„œ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” initialize() ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์นด์šดํ„ฐ๋ฅผ 0์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ฃผ์–ด์ง„ ํ•จ์ˆ˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์—ฌ๋Ÿฌ ํŒŒํ‹ฐ์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

-- set up for the sample
CREATE TABLE parts (p FLOAT, s STRING);

INSERT INTO parts VALUES (1, 'michael'), (1, 'kelly'), (1, 'brian');
INSERT INTO parts VALUES (2, 'clara'), (2, 'maggie'), (2, 'reagan');

-- creation of the UDTF
CREATE OR REPLACE FUNCTION "CHAR_SUM"(INS STRING)
    RETURNS TABLE (NUM FLOAT)
    LANGUAGE JAVASCRIPT
    AS '{
    processRow: function (row, rowWriter, context) {
      this.ccount = this.ccount + 1;
      this.csum = this.csum + row.INS.length;
      rowWriter.writeRow({NUM: row.INS.length});
    },
    finalize: function (rowWriter, context) {
     rowWriter.writeRow({NUM: this.csum});
    },
    initialize: function(argumentInfo, context) {
     this.ccount = 0;
     this.csum = 0;
    }}';
Copy

๋‹ค์Œ ์ฟผ๋ฆฌ๋Š” ๋ถ„ํ•  ์—†์ด parts ํ…Œ์ด๋ธ”์—์„œ CHAR_SUM UDTF๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

SELECT * FROM parts, TABLE(char_sum(s));
Copy

์ถœ๋ ฅ:

+--------+---------+-----+
| P      | S       | NUM |
+--------+---------+-----+
| 1      | michael | 7   |
| 1      | kelly   | 5   |
| 1      | brian   | 5   |
| 2      | clara   | 5   |
| 2      | maggie  | 6   |
| 2      | reagan  | 6   |
| [NULL] | [NULL]  | 34  |
+--------+---------+-----+
Copy

ํŒŒํ‹ฐ์…˜์„ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด Snowflake๊ฐ€ ์ž๋™์œผ๋กœ ํŒŒํ‹ฐ์…˜์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์˜ˆ์—์„œ๋Š” ํ–‰ ์ˆ˜๊ฐ€ ์ ๊ธฐ ๋•Œ๋ฌธ์— ํŒŒํ‹ฐ์…˜์ด ํ•˜๋‚˜๋งŒ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค(์ฆ‰, finalize() ํ˜ธ์ถœ์ด ํ•˜๋‚˜๋งŒ ์‹คํ–‰๋จ). ๋งˆ์ง€๋ง‰ ํ–‰์˜ ์ž…๋ ฅ ์—ด์—๋Š” NULL ๊ฐ’์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋™์ผํ•œ ์ฟผ๋ฆฌ์ด์ง€๋งŒ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ช…์‹œ์  ๋ถ„ํ• ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

SELECT * FROM parts, TABLE(char_sum(s) OVER (PARTITION BY p));
Copy

์ถœ๋ ฅ:

+--------+---------+-----+
| P      | S       | NUM |
+--------+---------+-----+
| 1      | michael | 7   |
| 1      | kelly   | 5   |
| 1      | brian   | 5   |
| 1      | [NULL]  | 17  |
| 2      | clara   | 5   |
| 2      | maggie  | 6   |
| 2      | reagan  | 6   |
| 2      | [NULL]  | 17  |
+--------+---------+-----+
Copy

์ด ์˜ˆ์—์„œ๋Š” p ์—ด์— ๋Œ€ํ•ด ๋ถ„ํ• ํ•˜์—ฌ ๋‘ ๊ฐœ์˜ ํŒŒํ‹ฐ์…˜์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ํŒŒํ‹ฐ์…˜์— ๋Œ€ํ•ด finalize() ์ฝœ๋ฐฑ์—์„œ ๋‹จ์ผ ํ–‰์ด ๋ฐ˜ํ™˜๋˜์–ด ์ด ๋‘ ๊ฐœ์˜ ํ–‰์„ ์ƒ์„ฑํ•˜๋ฉฐ ์ด๋Š” s ์—ด์˜ NULL ๊ฐ’์œผ๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค. p ๋Š” PARTITION BY ์—ด์ด๋ฏ€๋กœ finalize() ์—์„œ ์ƒ์„ฑ๋œ ํ–‰์€ ํ˜„์žฌ ํŒŒํ‹ฐ์…˜์„ ์ •์˜ํ•˜๋Š” p ๊ฐ’์„ ๊ฐ–์Šต๋‹ˆ๋‹ค.

ํ…Œ์ด๋ธ” ๊ฐ’ ๋ฐ ๊ธฐํƒ€ UDTF๋ฅผ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํ™•์žฅ๋œ ์˜ˆยถ

์ด ๊ธฐ๋ณธ UDTF๋Š” IP ์ฃผ์†Œ์˜ โ€œ๋ฒ”์œ„โ€๋ฅผ ์ „์ฒด IP ์ฃผ์†Œ ๋ชฉ๋ก์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ž…๋ ฅ์€ IP ์ฃผ์†Œ์˜ ์ฒ˜์Œ 3๊ฐœ ์„ธ๊ทธ๋จผํŠธ(์˜ˆ: '192.168.1'), ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ ๋ฒ”์œ„์˜ ์‹œ์ž‘ ๋ฐ ๋(์˜ˆ: 42 ๋ฐ 45)์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION range_to_values(PREFIX VARCHAR, RANGE_START FLOAT, RANGE_END FLOAT)
    RETURNS TABLE (IP_ADDRESS VARCHAR)
    LANGUAGE JAVASCRIPT
    AS $$
      {
        processRow: function f(row, rowWriter, context)  {
          var suffix = row.RANGE_START;
          while (suffix <= row.RANGE_END)  {
            rowWriter.writeRow( {IP_ADDRESS: row.PREFIX + "." + suffix} );
            suffix = suffix + 1;
            }
          }
      }
      $$;

SELECT * FROM TABLE(range_to_values('192.168.1', 42::FLOAT, 45::FLOAT));
Copy

์ถœ๋ ฅ:

+--------------+
| IP_ADDRESS   |
+==============+
| 192.168.1.42 |
+--------------+
| 192.168.1.43 |
+--------------+
| 192.168.1.44 |
+--------------+
| 192.168.1.45 |
+--------------+
Copy

์ด์ „ ์˜ˆ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋‘˜ ์ด์ƒ์˜ ๋ฒ”์œ„์— ๋Œ€ํ•œ ๊ฐœ๋ณ„ IP ์ฃผ์†Œ๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๋ฌธ์€ ๊ฐœ๋ณ„ IP ์ฃผ์†Œ๋กœ ํ™•์žฅํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„ ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ฟผ๋ฆฌ๋Š” ํ…Œ์ด๋ธ”์˜ ํ–‰์„ range_to_values() UDTF์— ์ž…๋ ฅํ•˜์—ฌ ๊ฐœ๋ณ„ IP ์ฃผ์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

CREATE TABLE ip_address_ranges(prefix VARCHAR, range_start INTEGER, range_end INTEGER);
INSERT INTO ip_address_ranges (prefix, range_start, range_end) VALUES
    ('192.168.1', 42, 44),
    ('192.168.2', 10, 12),
    ('192.168.2', 40, 40)
    ;

SELECT rtv.ip_address
  FROM ip_address_ranges AS r, TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv;
Copy

์ถœ๋ ฅ:

+--------------+
| IP_ADDRESS   |
+==============+
| 192.168.1.42 |
+--------------+
| 192.168.1.43 |
+--------------+
| 192.168.1.44 |
+--------------+
| 192.168.2.10 |
+--------------+
| 192.168.2.11 |
+--------------+
| 192.168.2.12 |
+--------------+
| 192.168.2.40 |
+--------------+
Copy

์ฃผ์˜

์ด ์˜ˆ์—์„œ FROM ์ ˆ์— ์‚ฌ์šฉ๋œ ๊ตฌ๋ฌธ์€ ๋‚ด๋ถ€ ์กฐ์ธ์˜ ๊ตฌ๋ฌธ(์ฆ‰, FROM t1, t2)๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ˆ˜ํ–‰๋œ ์ž‘์—…์€ ์‹ค์ œ ๋‚ด๋ถ€ ์กฐ์ธ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์‹ค์ œ ๋™์ž‘์€ range_to_values() ํ•จ์ˆ˜๊ฐ€ ip_address changes ํ…Œ์ด๋ธ”์— ์žˆ๋Š” ๊ฐ ํ–‰์˜ ๊ฐ’์œผ๋กœ ํ˜ธ์ถœ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์“ฐ๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

for input_row in ip_address_ranges:
  output_row = range_to_values(input_row.prefix, input_row.range_start, input_row.range_end)
Copy

UDTF์— ๊ฐ’์„ ์ „๋‹ฌํ•œ๋‹ค๋Š” ๊ฐœ๋…์€ ์—ฌ๋Ÿฌ UDTF๋กœ ํ™•์žฅ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ๋Š” IPV4 ์ฃผ์†Œ๋ฅผ IPV6 ์ฃผ์†Œ๋กœ โ€œ๋ณ€ํ™˜โ€ํ•˜๋Š” fake_ipv4_to_ipv6() ์ด๋ผ๋Š” UDTF๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ฟผ๋ฆฌ๋Š” ๋‹ค๋ฅธ UDTF๋ฅผ ํฌํ•จํ•˜๋Š” ๋ณด๋‹ค ๋ณต์žกํ•œ ๋ฌธ์˜ ์ผ๋ถ€๋กœ์„œ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

-- Example UDTF that "converts" an IPV4 address to a range of IPV6 addresses.
-- (for illustration purposes only and is not intended for actual use)
CREATE OR REPLACE FUNCTION fake_ipv4_to_ipv6(ipv4 VARCHAR)
    RETURNS TABLE (IPV6 VARCHAR)
    LANGUAGE JAVASCRIPT
    AS $$
      {
        processRow: function f(row, rowWriter, context)  {
          rowWriter.writeRow( {IPV6: row.IPV4 + "." + "000.000.000.000"} );
          rowWriter.writeRow( {IPV6: row.IPV4 + "." + "..."} );
          rowWriter.writeRow( {IPV6: row.IPV4 + "." + "FFF.FFF.FFF.FFF"} );
          }
      }
      $$;

SELECT ipv6 FROM TABLE(fake_ipv4_to_ipv6('192.168.3.100'));
Copy

์ถœ๋ ฅ:

+-------------------------------+
| IPV6                          |
+===============================+
| 192.168.3.100.000.000.000.000 |
+-------------------------------+
| 192.168.3.100....             |
+-------------------------------+
| 192.168.3.100.FFF.FFF.FFF.FFF |
+-------------------------------+
Copy

๋‹ค์Œ ์ฟผ๋ฆฌ๋Š” ip_address changes ํ…Œ์ด๋ธ”์˜ ์ž…๋ ฅ๊ณผ ํ•จ๊ป˜, ์ด์ „์— ์ƒ์„ฑ๋œ fake_ipv4_to_ipv6 ๋ฐ range_to_values() UDTF๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, IP ์ฃผ์†Œ ๋ฒ”์œ„ ์„ธํŠธ๋กœ ์‹œ์ž‘ํ•˜์—ฌ ์ด๋ฅผ ๊ฐœ๋ณ„ IPV4 ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜ํ•œ ๋‹ค์Œ, ๊ฐ IPV4 ์ฃผ์†Œ๋ฅผ ๊ฐ€์ ธ์™€ IPV6 ์ฃผ์†Œ ๋ฒ”์œ„๋กœ โ€œ๋ณ€ํ™˜โ€ํ•ฉ๋‹ˆ๋‹ค.

SELECT rtv6.ipv6
  FROM ip_address_ranges AS r,
       TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv,
       TABLE(fake_ipv4_to_ipv6(rtv.ip_address)) AS rtv6
  WHERE r.prefix = '192.168.2'  -- limits the output for this example
  ;
Copy

์ถœ๋ ฅ:

+------------------------------+
| IPV6                         |
+==============================+
| 192.168.2.10.000.000.000.000 |
+------------------------------+
| 192.168.2.10....             |
+------------------------------+
| 192.168.2.10.FFF.FFF.FFF.FFF |
+------------------------------+
| 192.168.2.11.000.000.000.000 |
+------------------------------+
| 192.168.2.11....             |
+------------------------------+
| 192.168.2.11.FFF.FFF.FFF.FFF |
+------------------------------+
| 192.168.2.12.000.000.000.000 |
+------------------------------+
| 192.168.2.12....             |
+------------------------------+
| 192.168.2.12.FFF.FFF.FFF.FFF |
+------------------------------+
| 192.168.2.40.000.000.000.000 |
+------------------------------+
| 192.168.2.40....             |
+------------------------------+
| 192.168.2.40.FFF.FFF.FFF.FFF |
+------------------------------+
Copy

์ด ์˜ˆ์—์„œ๋Š” ์กฐ์ธ ๊ตฌ๋ฌธ์„ ๋‘ ๋ฒˆ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ, ๋‘ ์ž‘์—… ๋ชจ๋‘ ์‹ค์ œ ์กฐ์ธ์ด ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ํ…Œ์ด๋ธ”์˜ ์ถœ๋ ฅ์ด๋‚˜ ๋‹ค๋ฅธ UDTF๋ฅผ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” UDTF์— ๋Œ€ํ•œ ํ˜ธ์ถœ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ ๋‚ด๋ถ€ ์กฐ์ธ์€ ์ˆœ์„œ๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ ๋ฌธ์€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

table1 INNER JOIN table2 ON ...
table2 INNER JOIN table1 ON ...

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

SELECT rtv6.ipv6
  FROM ip_address_ranges AS r,
       TABLE(fake_ipv4_to_ipv6(rtv.ip_address)) AS rtv6,
       TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv
 WHERE r.prefix = '192.168.2'  -- limits the output for this example
  ;
Copy

์ฟผ๋ฆฌ๋Š” ๋‹ค์Œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

SQL compilation error: error line 3 at position 35 invalid identifier 'RTV.IP_ADDRESS'

rtv.ip_address ์‹๋ณ„์ž๋Š” ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ๋˜๊ธฐ ์ „์— ์ •์˜๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ์กฐ์ธ์—์„œ๋Š” ์ด๋Ÿฐ ์ผ์ด ์ผ์–ด๋‚˜์ง€ ์•Š์ง€๋งŒ, ์กฐ์ธ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ UDTF๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ, UDTF์— ๋Œ€ํ•œ ์ž…๋ ฅ์„ ์‹ค์ œ ์กฐ์ธ๊ณผ ํ˜ผํ•ฉํ•˜๋Š” ๋ฌธ์„ ์‚ฌ์šฉํ•ด ๋ณด์‹ญ์‹œ์˜ค. ๊ทธ๋Ÿฌ๋‚˜ UDTF์— ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ๊ณผ ๋‚ด๋ถ€ ์กฐ์ธ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์€ ๋‘˜ ๋‹ค ๋™์ผํ•œ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ํ˜ผ๋™๋  ์ˆ˜ ์žˆ์Œ์„ ๊ธฐ์–ตํ•˜์‹ญ์‹œ์˜ค.

-- First, create a small table of IP address owners.
-- This table uses only IPv4 addresses for simplicity.
DROP TABLE ip_address_owners;
CREATE TABLE ip_address_owners (ip_address VARCHAR, owner_name VARCHAR);
INSERT INTO ip_address_owners (ip_address, owner_name) VALUES
  ('192.168.2.10', 'Barbara Hart'),
  ('192.168.2.11', 'David Saugus'),
  ('192.168.2.12', 'Diego King'),
  ('192.168.2.40', 'Victoria Valencia')
  ;

-- Now join the IP address owner table to the IPv4 addresses.
SELECT rtv.ip_address, ipo.owner_name
  FROM ip_address_ranges AS r,
       TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv,
       ip_address_owners AS ipo
 WHERE ipo.ip_address = rtv.ip_address AND
      r.prefix = '192.168.2'   -- limits the output for this example
  ;
Copy

์ถœ๋ ฅ:

+--------------+-------------------+
| IP_ADDRESS   | OWNER_NAME        |
+==============+===================+
| 192.168.2.10 | Barbara Hart      |
+--------------+-------------------+
| 192.168.2.11 | David Saugus      |
+--------------+-------------------+
| 192.168.2.12 | Diego King        |
+--------------+-------------------+
| 192.168.2.40 | Victoria Valencia |
+--------------+-------------------+
Copy

์ฃผ์˜

์•ž์˜ ์˜ˆ๋Š” ์„ค๋ช…ํ•œ ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ UDTF๋ฅผ ์‹ค์ œ ์กฐ์ธ๊ณผ ๊ฒฐํ•ฉํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋น„๊ฒฐ์ •์  ๋ฐ/๋˜๋Š” ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ, ์ด ๋™์ž‘์€ ํ–ฅํ›„ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.