Snowflake Stored Procedure multiple statements list parameters - javascript

I would like to create a Snowflake stored procedure with multiple SQL statements, and input multiple parameters which happen to be a list. Having trouble. Is it possible?
My attempt:
CREATE OR REPLACE PROCEDURE SP_MULTI_STATEMENT(NAME string , AGE string)
returns string not null
language javascript
as
$$
var cmd = `
update TABLE_1 set SOMETHING1 = 'OK' where NAME in (${NAME});
update TABLE_2 set SOMETHING2 = 'GREAT' where AGE in (${AGE});
`
var sql = snowflake.createStatement({sqlText: cmd});
var result = sql.execute();
return 'DONE';
$$;
When I call my SP I would like it to be:
call SP_MULTI_STATEMENT('Andy, Brian, Christa','21,23,24');

Most of it looks good, but you need to add more quoted escapes to the list of names.
This example works:
CREATE OR REPLACE PROCEDURE SP_MULTI_STATEMENT(NAMES string , AGES string)
returns string not null
language javascript
as
$$
var cmd = `
select *
from (select 'a' name, 33 age)
where NAME in (${NAMES})
and AGE in (${AGES});
`
var sql = snowflake.createStatement({sqlText: cmd});
var result = sql.execute();
if(result.next()) {
return result.NAME;
}
return 'no results'
$$;
call SP_MULTI_STATEMENT('\'a\', \'b\', \'c\'', '30, 31, 32, 33');

Related

Snowflake Stored Procedures to generate unique IDs and then insert values into a table

I am using Snowflake stored procedures to try and log errors as they occur. The current stored proc has an if/else statement, but I'm just trying to get one section working for now since the code is the more or less the same with variations to the SQL statements.
create or replace Procedure PLog(
PName varchar,
CName varchar,
PType varchar)
returns varchar
not null
language javascript
AS
$$
var cyc_id_sql = `SELECT case when count(CycleId) > 0 then max(CycleID) + 1 else 1 end as CycleId from SCHEMA.TABLE1 where CycleName='${CName}'`;
var cycle_id_create = snowflake.createStatement({ sqlText: cyc_id_sql});
var cycleid = cycle_id_create.execute();
var p_id_sql = `Select case when count(ProcessId) > 0 then max(ProcessID) + 1 else 1 end as ProcessId from SCHEMA.TABLE2 where ProcessName='${PName}'`;
var p_id_create = snowflake.createStatement({ sqlText: p_id_sql});
var processid = p_id_create.execute();
var insertValuesText = `INSERT INTO SCHEMA.TABLE
(
ProcessLogId
,CycleId
,ProcessId
,CycleName
,ProcessName
,ProcessType
,ProcessStatus
,StartTime)
VALUES
(default,${cycleid},${processid},'${value3}','${value4}','${value5}','Started',current_timestamp )`;
var insertValues = snowflake.createStatement({ sqlText: insertValuesText});
insertValues.execute();
$$
When I call this I get the error:
SQL compilation error: syntax error line 12 at position 28 unexpected 'Object'. syntax error line 12 at position 44 unexpected 'Object'. At Statement.execute, line 22 position 13
I think the issue is that the cycle and process statements create a Javascript object instead of the desired value. The desired value being the unique ID. When I run the SQL separately with an empty database I get 1, which is expected so I know the SELECT statements work. When I run and then call:
create or replace Procedure PLog(
PName varchar,
CName varchar,
PType varchar)
returns varchar
not null
language javascript
AS
$$
var cyc_id_sql = `SELECT case when count(CycleId) > 0 then max(CycleID) + 1 else 1 end as CycleId from SCHEMA.TABLE1 where CycleName='${CName}'`;
var cycle_id_create = snowflake.createStatement({ sqlText: cyc_id_sql});
var cycleid = cycle_id_create.execute();
return cycleid
$$
I get [object Object]. I'm not that familiar with JavaScript so any help would be appreciated. I think the issue is that my variables generate instances of an object instead of a usable value for the SQL statement, I just don't know how to fix the issue. Thank you!
when you execute a statement var processid = p_id_create.execute(); processid is the result set, not the actual value you want. you need to fetch it
so here is some example from the doc's
CREATE OR REPLACE PROCEDURE right_bind(TIMESTAMP_VALUE VARCHAR)
RETURNS BOOLEAN
LANGUAGE JAVASCRIPT
AS
$$
var cmd = "SELECT CURRENT_DATE() > TO_TIMESTAMP(:1, 'YYYY-MM-DD HH24:MI:SS')";
var stmt = snowflake.createStatement(
{
sqlText: cmd,
binds: [TIMESTAMP_VALUE]
}
);
var result1 = stmt.execute();
result1.next();
return result1.getColumnValue(1);
$$
;
so your small example should be:
create or replace Procedure PLog(
PName varchar,
CName varchar,
PType varchar)
returns varchar
not null
language javascript
AS
$$
var cyc_id_sql = `SELECT case when count(CycleId) > 0 then max(CycleID) + 1 else 1 end as CycleId from SCHEMA.TABLE1 where CycleName='${CName}'`;
var cycle_id_create = snowflake.createStatement({ sqlText: cyc_id_sql});
var result1 = cycle_id_create.execute();
result1.next();
var cycleid result1.getColumnValue(1);
return cycleid
$$
You are correct that it's returning a JavaScript object. As Simeon notes, this is returning a resultSet. If you're expecting a query to return a scalar (single row with a single column) you can use inline code to get it. However, it will not be modular and harder to read than modular code. This is a great example of where a helper function helps.
For example:
create or replace procedure TEST()
returns string
language javascript
as
$$
var my_scalar = getScalar("select C_NAME from SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.CUSTOMER limit 1");
return my_scalar;
function getScalar(sql){
cmd = {sqlText: sql};
stmt = snowflake.createStatement(cmd);
var rs;
rs = stmt.execute();
rs.next();
return rs.getColumnValue(1);
}
$$;
You can then use the getScalar helper function to collect scalar values you need. If you're only doing it once it won't matter too much. If you're collecting a few or dozens, then it will keep the code modular, compact, and more readable.

How to pass and call Snowflake stored procedure date argument in underlying SELECT statement as a column

I am fairly new to snowflake working on a stored procedure having date as a parameter, the same parameter will be used in SELECT statement to work as additional column in result set.
Below is the code:
CREATE OR REPLACE PROCEDURE test1 (TEST_DATE VARCHAR)
RETURNS VARCHAR NOT NULL
LANGUAGE javascript
EXECUTE AS CALLER
AS
$$
var prod = TEST_DATE;
var sql_stmt = `select
ID
,Value
,getvariable('PROD') as TestDate
, to_varchar(CURRENT_TIMESTAMP()) CreateDate
from
Table;`;
var statement = snowflake.createStatement( {sqlText:sql_stmt} );
var rs = statement.execute();
rs.next(1);
//return rs.getColumnValue(1);
return rs.getColumnValue(3);
$$;
Calling above procedure as
CALL test1('2022-02-22');
Returning error as "NULL result in a non-nullable column".
Please suggest how can I use the argument as column name in select.
Thank You.
Binding Variables:
Binding a variable to a SQL statement allows you to use the value of the variable in the statement.
CREATE OR REPLACE PROCEDURE test1 (TEST_DATE VARCHAR)
RETURNS VARCHAR NOT NULL
LANGUAGE javascript
EXECUTE AS CALLER
AS
$$
var sql_stmt = `select
ID
,Value
, :1 as TestDate
, to_varchar(CURRENT_TIMESTAMP()) AS CreateDate
from Table;`;
var statement = snowflake.createStatement( {sqlText:sql_stmt, binds: [TEST_DATE]} );
var rs = statement.execute();
...
$$;
I think Binding like mentioned above is the more proper method, but you could also use string formatting its a bit more readable.
CREATE OR REPLACE PROCEDURE test1 (TEST_DATE VARCHAR)
RETURNS VARCHAR NOT NULL
LANGUAGE javascript
EXECUTE AS CALLER
AS
$$
var prod = TEST_DATE;
var sql_stmt = `select
ID
,Value
,'${TEST_DATE}' as TestDate
, to_varchar(CURRENT_TIMESTAMP()) CreateDate
from
Table;`;
var statement = snowflake.createStatement( {sqlText:sql_stmt} );
var rs = statement.execute();
rs.next(1);
//return rs.getColumnValue(1);
return rs.getColumnValue(3);
$$;

Converting EXECUTE Statements from Azure SQL to Snowflake

I'm trying to convert the below Azure SQL stored procedure to Snowflake. But I couldn't find an alternative for the EXEC statement yet:
CREATE PROC SAMPLE_PROC
AS
BEGIN
DECLARE #BusinessUnitKey INT=(SELECT BusinessUnitKey FROM BusinessUnit WHERE BusinessUnitName='ABC') ;
DECLARE #LoadDate DATETIME= (SELECT Cast(GETUTCDATE() as Date)) ;
DECLARE #DataLoadLogKey INT = (
SELECT MAX(DataLoadLogKey)
FROM DataLoadLog
WHERE BusinessUnitKey = #BusinessUnitKey
)
,#TableName VARCHAR(100) = 'ProductType'
,#StoredProcName VARCHAR(100) = (object_name(##procid))
,#StarDateTime DATETIME = #LoadDate
,#EndDateTime DATETIME = NULL
,#Status VARCHAR(100) = 'In Progress'
,#LoadDescription VARCHAR(1000) = 'Loading Data' ;
EXEC dbo.usp_procedure #DataLoadLogKey = #DataLoadLogKey
,#TableName = #TableName
,#StoredProcName = #StoredProcName
,#StarDateTime = #StarDateTime
,#EndDateTime = NULL
,#Status = #Status
,#LoadDescription = #LoadDescription ;
END;
Could anyone provide the corresponding execute statement in Snowflake?
Take a look at the Working with Stored Procedures in documentation.
This is a rough stub that should compile.
I cant test the code, as I dont have ddl for your tables or data to validate, but this should give you a good start.
Best Regards.
CREATE OR REPLACE PROCEDURE SAMPLE_PROC()
returns VARCHAR not null
language javascript
as
$$
var businessUnitKey , loadDate, dataLoadLogKey, sql, statement, rs, message ;
message= "FAILED";
// get business unit key
sql = `SELECT BusinessUnitKey FROM BusinessUnit WHERE BusinessUnitName='ABC'`;
statement = snowflake.createStatement({sqlText: sql});
result_set = statement.execute();
while (result_set.next()) {
businessUnitKey = result_set.getColumnValue(1);
}
// get load date
sql = `SELECT Cast(SYSDATE() as Date)`;
statement = snowflake.createStatement({sqlText: sql});
result_set = statement.execute();
while (result_set.next()) {
loadDate = result_set.getColumnValue(1);
}
//return loadDate
sql = `SELECT MAX(DataLoadLogKey)
FROM DataLoadLog
WHERE BusinessUnitKey = ` + businessUnitKey + `;`
statement = snowflake.createStatement({sqlText: sql});
result_set = statement.execute();
while (result_set.next()) {
dataLoadLogKey = result_set.getColumnValue(1);
}
var TableName = 'ProductType'
,StoredProcName = 'SAMPLE_PROC()'
,StarDateTime = LoadDate
,EndDateTime = NULL
,Status = 'In Progress'
,LoadDescription = 'Loading Data' ;
sql = `CALL dbo.usp_procedure(:1,:2,:3,:4,:5,:6,:7); `
statement = snowflake.createStatement({
sqlText: sql,
binds: [dataLoadLogKey,TableName,StoredProcName,StarDateTime,EndDateTime,Status,LoadDescription]
});
message= "SUCCESSFUL";
return message;
$$;
call SAMPLE_PROC();
Snowflake stored procedures are written in JavaScript, which can execute SQL statements by calling a JavaScript API. This API is similar to, but not identical to, the APIs in Snowflake connectors and drivers (Node.js, JDBC, Python, etc.).
Details: https://docs.snowflake.com/en/sql-reference/stored-procedures-usage.html#implementation-and-api
You would be able to find a couple of code examples:
https://docs.snowflake.com/en/sql-reference/stored-procedures-usage.html#examples

Using BQ Command line create View with UDF in BigQuery

I want to create View in BigQuery using UDF and BQ command line.
BQ command :
bq query --use_legacy_sql=false --project_id="myProject" \
'CREATE OR REPLACE FUNCTION udfFunc(str STRING) RETURNS STRING LANGUAGE js AS
"""
data = fromLib(str);
return JSON.stringify(data);
""" OPTIONS(library = "gs://<bucket>/lib_file.js");
SELECT'
col1,
col2,
udfFunc(col2) as new_col
FROM
`myProject:mySataset.table`'
I am getting an error
Invalid value: Routine name "udfFunc" missing dataset while
no default dataset is set in the request.
From your query (and the comment on the question), it seems that you only need a temp function during the query time, this is the query that you:
define a temp function which is only visible in this query
use the temp function immediately in the query
CREATE TEMP FUNCTION udfFunc(str STRING) RETURNS STRING LANGUAGE js AS
"""
data = fromLib(str);
return JSON.stringify(data);
""" OPTIONS(library = "gs://<bucket>/lib_file.js");
SELECT
col1,
col2,
udfFunc(col2) as new_col
FROM
`myProject:mySataset.table`

Inserting parameters of stored procedure into a table using Merge in snowflake

I am trying to insert the parameter values of a stored procedure into the table using merge function in sql. The parameters consists of DB and schema name. I have written a stored procedure for that but, I don't understand where I'm doing wrong.
Here is my attempt:
CREATE TABLE TABL(DBName VARCHAR, SCName VARCHAR) // creating table
REATE OR REPLACE PROCEDURE repo(DB VARCHAR,SC VARCHAR) //need to push DB, SC INTO TABL
RETURNS type
LANGUAGE JAVASCRIPT
AS
$$
//Inserting parameters into table as values but didn;t work
var sql_command = "merge TABL as t using (SELECT +"DB"+ as database,+"SC" as schema) as s on t.DBName = s.DB and t.SCName = s.schema when matched then update set t.DBName = t.DBName when not matched then insert (DBName, SCName) VALUES ('"+DB+"','"+SC +"')";
snowflake.execute({sqlText: sql_command});
return type;
$$;
You can use binds:
CREATE TABLE TABL(DBName VARCHAR, SCName VARCHAR); // creating table
CREATE OR REPLACE PROCEDURE repo(DB VARCHAR,SC VARCHAR)
RETURNS string
LANGUAGE JAVASCRIPT
AS
$$
var sql_command = `merge into TABL as t
using (SELECT :1 as database,:2 as schema) as s
on t.DBName = s.database
and t.SCName = s.schema
when matched then update
set t.DBName = t.DBName
when not matched then insert
(DBName, SCName) VALUES (:1,:2)`;
snowflake.execute({sqlText: sql_command, binds: [DB, SC]});
return 'success';
$$;
call repo('a', 'b');
See https://docs.snowflake.com/en/sql-reference/stored-procedures-usage.html#binding-variables for more info.

Categories