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

Snowflake๋Š” 0๊ฐœ, 1๊ฐœ ๋˜๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ–‰(๊ฐ๊ฐ 1๊ฐœ ์ด์ƒ์˜ ์—ด ํฌํ•จ)์œผ๋กœ ๊ตฌ์„ฑ๋œ ํ–‰ ์„ธํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” SQL UDF๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ UDF๋ฅผ ํ…Œ์ด๋ธ” ํ˜•์‹ UDF, ํ…Œ์ด๋ธ” UDF ๋˜๋Š” ๊ฐ€์žฅ ํ”ํ•˜๊ฒŒ๋Š” UDTF (์‚ฌ์šฉ์ž ์ •์˜ ํ…Œ์ด๋ธ” ํ•จ์ˆ˜)๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ฟผ๋ฆฌ์˜ FROM ์ ˆ์—์„œ UDTF์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

๊ตฌ๋ฌธยถ

CREATE OR REPLACE FUNCTION <name> ( [ <arguments> ] )
  RETURNS TABLE ( <output_col_name> <output_col_type> [, <output_col_name> <output_col_type> ... ] )
  AS '<sql_expression>'
Copy

SQL UDTF๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  UDF์˜ ์ผ๋ฐ˜ ๊ตฌ๋ฌธ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช…์€ CREATE FUNCTION ์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ธ์žยถ

name:

์ด๋Š” ์‹๋ณ„์ž ์š”๊ตฌ ์‚ฌํ•ญ ์— ์„ค๋ช…๋œ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋Š” ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์˜ค๋ธŒ์ ํŠธ ์ด๋ฆ„์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

arguments:

์ด๋Š” ์‹์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ์—ด ์ด๋ฆ„์ด๊ฑฐ๋‚˜, ๋ฆฌํ„ฐ๋Ÿด์ด๊ฑฐ๋‚˜, ๋‹จ์ผ ๊ฐ’์œผ๋กœ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ์‹). ์ผ๋ฐ˜์ ์œผ๋กœ ํ•จ์ˆ˜๋Š” ์—ด ์ด๋ฆ„์ธ ํ•˜๋‚˜์˜ ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๋‘˜ ์ด์ƒ์˜ ๊ฐ’(์˜ˆ: ๋‘˜ ์ด์ƒ์˜ ์—ด ์ด๋ฆ„ ๋˜๋Š” ์—ด ์ด๋ฆ„ ํ•˜๋‚˜์™€ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’ ํ•˜๋‚˜ ์ด์ƒ)์„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜ ๊ฐ’์„ ์ „ํ˜€ ์ „๋‹ฌํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ, ์ž…๋ ฅ์ด ๋งค๋ฒˆ ๊ฐ™์œผ๋ฉด ์ถœ๋ ฅ์ด ๋งค๋ฒˆ ๊ฐ™์Šต๋‹ˆ๋‹ค.

RETURNS TABLE(...)

UDF๊ฐ€ ํ…Œ์ด๋ธ”์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•จ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ด„ํ˜ธ ์•ˆ์— ๋ฐ˜ํ™˜๋œ ํ…Œ์ด๋ธ”์— ํฌํ•จํ•  ์—ด์˜ ์ด๋ฆ„-์œ ํ˜• ํŽ˜์–ด๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค(์•„๋ž˜ ์„ค๋ช…์— ๋”ฐ๋ฆ„).

output_col_name:

๋ฐ˜ํ™˜๋œ ํ…Œ์ด๋ธ”์— ํฌํ•จํ•  ์ถœ๋ ฅ ์—ด์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. ํ•˜๋‚˜ ์ด์ƒ์˜ ์ถœ๋ ฅ ์—ด์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

output_col_type:

์ถœ๋ ฅ ์—ด์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ž…๋‹ˆ๋‹ค.

sql_expression:

๊ฐ๊ฐ ํ•˜๋‚˜ ์ด์ƒ์˜ ์—ด์ด ์žˆ๋Š” 0๊ฐœ ๋˜๋Š” ๊ทธ ์ด์ƒ์˜ ํ–‰์„ ๊ฐ€์ง„ ํ…Œ์ด๋ธ”์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์œ ํšจํ•œ SQL ์‹ ๋˜๋Š” ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ถœ๋ ฅ์€ RETURNS ์ ˆ์— ์ง€์ •๋œ ์ˆ˜ ๋ฐ ๋ฐ์ดํ„ฐ ํƒ€์ž…๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ• ๋…ธํŠธยถ

  • SQL UDTF์˜ ๋ณธ๋ฌธ(์ผ๋ช… โ€œ์ •์˜โ€)์€ SELECT ์‹์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • sql_expression ์ฃผ์œ„์˜ ๊ตฌ๋ถ„ ๊ธฐํ˜ธ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ž‘์€๋”ฐ์˜ดํ‘œ์ด์ง€๋งŒ, ํ•œ ์Œ์˜ ๋‹ฌ๋Ÿฌ ๊ธฐํ˜ธ $$ ๋ฅผ ๊ตฌ๋ถ„ ๊ธฐํ˜ธ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ซ๋Š” ๊ตฌ๋ถ„ ๊ธฐํ˜ธ๋Š” ์—ฌ๋Š” ๊ตฌ๋ถ„ ๊ธฐํ˜ธ์™€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. sql_expression ์— ์ž‘์€๋”ฐ์˜ดํ‘œ๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ, ํ•œ ์Œ์˜ ๋‹ฌ๋Ÿฌ ๊ธฐํ˜ธ๊ฐ€ ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํ•œ ์Œ์˜ ๋‹ฌ๋Ÿฌ ๊ธฐํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ๋Š” ์•„๋ž˜์˜ ์˜ˆ ์„น์…˜์— ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

    ๊ตฌ๋ถ„ ๊ธฐํ˜ธ๊ฐ€ ์ž‘์€๋”ฐ์˜ดํ‘œ์ด๊ณ  ๋ณธ๋ฌธ์— ์ž‘์€๋”ฐ์˜ดํ‘œ๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ, ๋ฐฑ์Šฌ๋ž˜์‹œ ๋ฌธ์ž \ ๋ฅผ ์ด์Šค์ผ€์ดํ”„ ๋ฌธ์ž๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๋ณธ๋ฌธ์—์„œ ์ž‘์€๋”ฐ์˜ดํ‘œ๋ฅผ ์ด์Šค์ผ€์ดํ”„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ์˜ˆ ์„น์…˜์— ์˜ˆ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • UDTF์— ์ •์˜๋œ ์—ด์€ ์ผ๋ฐ˜ ํ…Œ์ด๋ธ” ์—ด์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์œ„์น˜์— ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • RETURNS ์ ˆ์— ์ง€์ •๋œ ๋ฐ˜ํ™˜ ํ˜•์‹์€ ํ…Œ์ด๋ธ” ํ˜•์‹ ๊ฒฐ๊ณผ์— ์žˆ๋Š” ์—ด์˜ ์ด๋ฆ„๊ณผ ํ˜•์‹์„ ๊ฒฐ์ •ํ•˜๋ฉฐ, ํ•จ์ˆ˜ ๋ณธ๋ฌธ์—์„œ SELECT ๋ฌธ์˜ ํ•ด๋‹น ์œ„์น˜์— ์žˆ๋Š” ์‹์˜ ํ˜•์‹๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • UDTF๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ TABLE ํ‚ค์›Œ๋“œ ๋‹ค์Œ์— ์˜ค๋Š” ๊ด„ํ˜ธ ์•ˆ์— UDTF ์ด๋ฆ„๊ณผ ์ธ์ž๋ฅผ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ SQL UDTF ํ˜ธ์ถœํ•˜๊ธฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ฐธ๊ณ 

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

SQL UDTF ํ˜ธ์ถœํ•˜๊ธฐยถ

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

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

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

์ƒ˜ํ”Œ SQL UDTFยถ

๊ธฐ๋ณธ ์˜ˆ์ œยถ

์ด๋Š” ์ถœ๋ ฅ์„ ํ•˜๋“œ ์ฝ”๋”ฉํ•˜๋Š” ์ธ์œ„์ ์œผ๋กœ ๊ฐ„๋‹จํ•œ UDTF์˜ ์˜ˆ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋˜ํ•œ $$ ๋ฅผ ๊ตฌ๋ถ„ ๊ธฐํ˜ธ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

CREATE FUNCTION t()
    RETURNS TABLE(msg VARCHAR)
    AS
    $$
        SELECT 'Hello'
        UNION
        SELECT 'World'
    $$;
Copy
SELECT msg 
    FROM TABLE(t())
    ORDER BY msg;
+-------+
| MSG   |
|-------|
| Hello |
| World |
+-------+
Copy

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

CREATE FUNCTION t()
    RETURNS TABLE(msg VARCHAR)
    AS
    '
        SELECT \'Hello\'
        UNION
        SELECT \'World\'
    ';
Copy
SELECT msg
    FROM TABLE(t())
    ORDER BY msg;
+-------+
| MSG   |
|-------|
| Hello |
| World |
+-------+
Copy

์ด๋Š” UDTF์˜ ๋˜ ๋‹ค๋ฅธ ๊ธฐ๋ณธ ์˜ˆ์ž…๋‹ˆ๋‹ค. ํ…Œ์ด๋ธ”์„ ์ฟผ๋ฆฌํ•˜๊ณ  ํ•ด๋‹น ํ…Œ์ด๋ธ”์—์„œ ๋‘ ๊ฐœ์˜ ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

create or replace table orders (
    product_id varchar, 
    quantity_sold numeric(11, 2)
    );

insert into orders (product_id, quantity_sold) values 
    ('compostable bags', 2000),
    ('re-usable cups',  1000);
Copy
create or replace function orders_for_product(PROD_ID varchar)
    returns table (Product_ID varchar, Quantity_Sold numeric(11, 2))
    as
    $$
        select product_ID, quantity_sold 
            from orders 
            where product_ID = PROD_ID
    $$
    ;
Copy
select product_id, quantity_sold
    from table(orders_for_product('compostable bags'))
    order by product_id;
+------------------+---------------+
| PRODUCT_ID       | QUANTITY_SOLD |
|------------------+---------------|
| compostable bags |       2000.00 |
+------------------+---------------+
Copy

์ด ๋™์ผํ•œ ๊ธฐ๋Šฅ์€ ๋ทฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์กฐ์ธ์ด ์žˆ๋Š” ์˜ˆยถ

์ง€์ •๋œ ์‚ฌ์šฉ์ž ID์— ๋Œ€ํ•œ ๊ตญ๊ฐ€ ์ •๋ณด(COUNTRY_CODE ๋ฐ COUNTRY_NAME)๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” SQL UDTF๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งŒ๋“ค๊ณ  ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

create or replace table countries (country_code char(2), country_name varchar);
insert into countries (country_code, country_name) values 
    ('FR', 'FRANCE'),
    ('US', 'UNITED STATES'),
    ('ES', 'SPAIN');

create or replace table user_addresses (user_ID integer, country_code char(2));
insert into user_addresses (user_id, country_code) values 
    (100, 'ES'),
    (123, 'FR'),
    (123, 'US');
Copy
CREATE OR REPLACE FUNCTION get_countries_for_user ( id number )
  RETURNS TABLE (country_code char, country_name varchar)
  AS 'select distinct c.country_code, c.country_name
      from user_addresses a, countries c
      where a.user_id = id
      and c.country_code = a.country_code';
Copy
select *
    from table(get_countries_for_user(123)) cc
    where cc.country_code in ('US','FR','CA')
    order by country_code;
+--------------+---------------+
| COUNTRY_CODE | COUNTRY_NAME  |
|--------------+---------------|
| FR           | FRANCE        |
| US           | UNITED STATES |
+--------------+---------------+
Copy

์ง€์ •๋œ ์—ฐ๋„์— ๋Œ€ํ•ด ์„ ํ˜ธํ•˜๋Š” ์ƒ‰์ƒ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” SQL UDTF๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งŒ๋“œ์‹ญ์‹œ์˜ค.

create or replace table favorite_years as
    select 2016 year
    UNION ALL
    select 2017
    UNION ALL
    select 2018
    UNION ALL
    select 2019;

 create or replace table colors as
    select 2017 year, 'red' color, true favorite
    UNION ALL
    select 2017 year, 'orange' color, true favorite
    UNION ALL
    select 2017 year, 'green' color, false favorite
    UNION ALL
    select 2018 year, 'blue' color, true favorite
    UNION ALL
    select 2018 year, 'violet' color, true favorite
    UNION ALL
    select 2018 year, 'brown' color, false favorite;

create or replace table fashion as
    select 2017 year, 'red' fashion_color
    UNION ALL
    select 2018 year, 'black' fashion_color
    UNION ALL
    select 2019 year, 'orange' fashion_color;
Copy
create or replace function favorite_colors(the_year int)
    returns table(color string) as
    'select color from colors where year=the_year and favorite=true';
Copy

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฟผ๋ฆฌ์—์„œ UDTF๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

select color
    from table(favorite_colors(2017))
    order by color;
+--------+
| COLOR  |
|--------|
| orange |
| red    |
+--------+
Copy

๋‹ค๋ฅธ ํ…Œ์ด๋ธ”๊ณผ์˜ ์กฐ์ธ์—์„œ UDTF๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. ํ…Œ์ด๋ธ”์˜ ์กฐ์ธ ์—ด์ด ํ•จ์ˆ˜์— ์ธ์ž๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

select * 
    from favorite_years y join table(favorite_colors(y.year)) c
    order by year, color;
+------+--------+
| YEAR | COLOR  |
|------+--------|
| 2017 | orange |
| 2017 | red    |
| 2018 | blue   |
| 2018 | violet |
+------+--------+
Copy

์ถ”๊ฐ€ ์กฐ๊ฑด์ž์— ๋Œ€ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ON ๋Œ€์‹  WHERE ์ ˆ์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

select * 
    from fashion f join table(favorite_colors(f.year)) fav
    where fav.color = f.fashion_color ;
+------+---------------+-------+
| YEAR | FASHION_COLOR | COLOR |
|------+---------------+-------|
| 2017 | red           | red   |
+------+---------------+-------+
Copy

์กฐ์ธ ์‹์—์„œ ์ƒ์ˆ˜์™€ ํ•จ๊ป˜ UDTF๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. ์ถ”๊ฐ€ ์กฐ์ธ ์กฐ๊ฑด์—๋Š” ON ๋Œ€์‹  WHERE ์ ˆ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

select fav.color as favorite_2017, f.*
    from fashion f JOIN table(favorite_colors(2017)) fav
    where fav.color = f.fashion_color
    order by year;
+---------------+------+---------------+
| FAVORITE_2017 | YEAR | FASHION_COLOR |
|---------------+------+---------------|
| red           | 2017 | red           |
| orange        | 2019 | orange        |
+---------------+------+---------------+
Copy