Scala๋กœ ์Šค์นผ๋ผ UDF ์ž‘์„ฑํ•˜๊ธฐยถ

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

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

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

  1. UDF๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ Snowflake๊ฐ€ ํ˜ธ์ถœํ•  ๋ฉ”์„œ๋“œ๋กœ Scala ์˜ค๋ธŒ์ ํŠธ ๋˜๋Š” ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

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

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

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

    • UDF ๋ฐ˜ํ™˜ ๊ฐ’์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž….

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

    • ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ์ž‘์„ฑ๋˜๋Š” ์–ธ์–ด.

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

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

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

์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๋กœ ์˜ค๋ธŒ์ ํŠธ ๋˜๋Š” ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ UDF ์ธ์ž ๊ฐ’์„ UDF์˜ ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  • ์ฒ˜๋ฆฌ๊ธฐ๋กœ ์ง€์ •ํ•  ๊ณต์šฉ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” ๊ณต์šฉ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

    ์ด๊ฒƒ์€ SQL์—์„œ UDF๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ Snowflake๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

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

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

  • ์„ ํƒ์ ์œผ๋กœ, Snowflake์—์„œ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด ํ˜ธ์ถœํ•  ์ธ์ž ์—†๋Š” ์ƒ์„ฑ์ž๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

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

์ฒ˜๋ฆฌ๊ธฐ์˜ ์˜ˆยถ

๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ์—๋Š” ๋ฌธ์ž์—ด์„ ์ˆ˜์‹ ํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•˜๋Š” MyHandler.echoVarchar ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. Snowflake๋Š” UDF์—์„œ ์ˆ˜์‹ ํ•œ ๊ฐ’(VARCHAR)์„ ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ ๋ณ€์ˆ˜ ์œ ํ˜•์ธ ๋ฌธ์ž์—ด์— ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.

CREATE OR REPLACE FUNCTION echo_varchar(x VARCHAR)
  RETURNS VARCHAR
  LANGUAGE SCALA
  RUNTIME_VERSION = 2.12
  HANDLER='MyHandler.echoVarchar'
  AS
  $$
  class MyHandler {
    def echoVarchar(x : String): String = {
      return x
    }
  }
  $$;
Copy

UDF ํ˜ธ์ถœํ•˜๊ธฐ

SELECT echo_varchar('Hello');
Copy

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

์ธ์ž ์—†๋Š” ์ƒ์„ฑ์ž๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ์„ฑ์ž์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ, ์ด ์˜ค๋ฅ˜๋Š” ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ์ž ์˜ค๋ฅ˜๋กœ์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

def this() = {
  // Initialize here.
}
Copy

ํ•จ์ˆ˜ ์ธ์ž ์ฒ˜๋ฆฌํ•˜๊ธฐยถ

UDF์— ์ธ์ž๋กœ ์ „๋‹ฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด SQL ์ฝ”๋“œ์—์„œ UDF๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ Snowflake๊ฐ€ ํ˜ธ์ถœํ•  ๊ณต์šฉ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์‹ญ์‹œ์˜ค. CREATE FUNCTION ๋ช…๋ น์œผ๋กœ UDF๋ฅผ ๋งŒ๋“ค ๋•Œ HANDLER ์ ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ฒ˜๋ฆฌ๊ธฐ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์–ธํ•  ๋•Œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  • ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ๊ณต์šฉ์œผ๋กœ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

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

    ํด๋ž˜์Šค๋ฅผ ์Šคํ…Œ์ด์ง•๋œ ์ฒ˜๋ฆฌ๊ธฐ๋กœ JAR์— ํŒจํ‚ค์ง•ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์–ธํ•œ ์ดํ›„์— ๊ฐ๊ฐ์„ CREATE FUNCTION ๋ฌธ์˜ HANDLER ์ ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌ๊ธฐ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ…Œ์ด์ง•๋œ ์ฒ˜๋ฆฌ๊ธฐ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฒ˜๋ฆฌ๊ธฐ ์ฝ”๋“œ๋ฅผ ์ธ๋ผ์ธ ๋˜๋Š” ์Šคํ…Œ์ด์ง€์— ์œ ์ง€ํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  • UDF ์„ ์–ธ์—์„œ ์ง€์ •ํ•œ SQL ์œ ํ˜•์— ๋งคํ•‘๋˜๋Š” ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ ๋งค๊ฐœ ๋ณ€์ˆ˜ ๋ฐ ๋ฐ˜ํ™˜ ์œ ํ˜•์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

    ์ž์„ธํ•œ ๋‚ด์šฉ์€ SQL-Scala ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋งคํ•‘ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

  • ์„ ํƒ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ์—์„œ ํ˜ธ์ถœํ•  ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์ด ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ์˜ ์ฒ˜๋ฆฌ๋ฅผ ์ง€์›ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€๋กœ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

    ๋‹ค์Œ ์˜ˆ์ œ์˜ ์ฝ”๋“œ์—๋Š” ์ธ์ž๋กœ ๋ฐ›์€ ๋ฐฐ์—ด์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ๋น„ ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ concatenate ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” handleStrings ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

    CREATE OR REPLACE FUNCTION generate_greeting(greeting_words ARRAY)
      RETURNS VARCHAR
      LANGUAGE SCALA
      RUNTIME_VERSION = 2.12
      HANDLER='StringHandler.handleStrings'
      AS
      $$
      class StringHandler {
        def handleStrings(strings: Array[String]): String = {
          return concatenate(strings)
        }
        private def concatenate(strings: Array[String]): String = {
          var concatenated : String = ""
          for (newString <- strings)  {
              concatenated = concatenated + " " + newString
          }
          return concatenated
        }
      }
      $$;
    
    Copy

    ๋‹ค์Œ์€ generate_greeting ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

    SELECT generate_greeting(['Hello', 'world']);
    
    Copy

    ๋‹ค์Œ์€ ์œ„์˜ ๊ฐ’์œผ๋กœ generate_greeting ์„ ํ˜ธ์ถœํ•œ ์ถœ๋ ฅ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

    Hello world
    

์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋กœ๋“œํ•˜๊ธฐยถ

๋งค๊ฐœ ๋ณ€์ˆ˜ ์ˆ˜๊ฐ€ ๋‹ค๋ฅธ ํ•œ ๋™์ผํ•œ ํด๋ž˜์Šค ๋˜๋Š” ์˜ค๋ธŒ์ ํŠธ์—์„œ ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Scala UDF์˜ ๊ฒฝ์šฐ Snowflake๋Š” ์œ ํ˜• ์ด ์•„๋‹Œ ๋ฉ”์„œ๋“œ ์ธ์ž์˜ ๊ฐœ์ˆ˜ ๋งŒ์œผ๋กœ ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ ๋น„์‹ค์šฉ์ ์ž…๋‹ˆ๋‹ค. ์ผ๋ถ€ SQL ๋ฐ์ดํ„ฐ ํƒ€์ž…์€ ๋‘˜ ์ด์ƒ์˜ Scala ๋˜๋Š” Java ๋ฐ์ดํ„ฐ ํƒ€์ž…์— ๋งตํ•‘๋  ์ˆ˜ ์žˆ๊ณ  ๋”ฐ๋ผ์„œ ์ž ์žฌ์ ์œผ๋กœ ๋‘˜ ์ด์ƒ์˜ ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ ์„œ๋ช…์— ๋งตํ•‘๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๋‘ Scala ๋ฉ”์„œ๋“œ๊ฐ€ ๋™์ผ ์ด๋ฆ„๊ณผ ๋™์ผ ์ˆ˜์˜ ์ธ์ž๋ฅผ ๊ฐ–์ง€๋งŒ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ, ์ด๋Ÿฌํ•œ ๋ฉ”์„œ๋“œ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ฒ˜๋ฆฌ๊ธฐ๋กœ ์‚ฌ์šฉํ•˜์—ฌ UDF๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋‹ค์Œ๊ณผ ์œ ์‚ฌํ•œ ์˜ค๋ฅ˜๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

Cannot determine which implementation of handler "handler name" to invoke since there are multiple
definitions with <number of args> arguments in function <user defined function name> with
handler <class name>.<handler name>
Copy

์›จ์–ดํ•˜์šฐ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ, UDF๊ฐ€ ๋งŒ๋“ค์–ด์งˆ ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๊ฐ์ง€๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ, UDF๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.