How to escape comma character in a javascript procedure in wherescape, snowflake? - snowflake-cloud-data-platform

I have a table with 2 columns:
Tablename
Fieldname
tbl
name
tbl
LastName
I need to display one row, having Fieldname values concatenated:
tbl --> name, lastName
So, I am using Listagg fct to concatenate the values :
listagg (fieldname,',') as newCol.
Running this command in Snowflake, works fine and displays want i need.
But, because we are using Wherescape to create procedures, adding this function in a javascript procedure, fails, because of COMMA separator used in listagg fct.
I tried with different escape characters, but nothing worked. (/,,/[])..
If I change the character in &,* it works, but I have to use comma.
Creating the newCol with listagg with & instead of ",", works and if I use a replace fct to change it to ",", it fails again :(
What should I use to have COMMA between the values for the newCol.
It is mandatory to have COMMA, because the column will be used in an update statement.
EDIT:
CREATE OR REPLACE PROCEDURE sp_myprocedure_test()
RETURNS VARCHAR(16777216)
language javascript
execute as CALLER
as
$$
const generateUpdStmt = ` SELECT 'UPDATE tbl SET ' || c.col_update AS STMT_SQL
,c.col_update
FROM (
select TABLE_NAME
, listagg (concat(field_name, ' = ''#tst''' ), ' , ') within group (order by TABLE_NAME, field_name asc) as col_update
from tbl
group by TABLE_NAME
) c
;`;
try {
var return_value = "Init: ";
var stmt_update = snowflake.createStatement({sqlText: generateUpdStmt});
var result_stmt = stmt_update.execute();
while (result_stmt.next())
{
var result_stmt_value_2 = result_stmt.getColumnValue(2);
return_value += result_stmt_value_2 + "\n";
}
result = " Status: Succeeded. \n";
}
catch (err) {
result = "Failed: Code:" + err.code + "\n State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
return result;
}
return return_value;
$$
EDIT 2:
Returning result in wherescape with "&" instead of "," delimiter:
enter image description here
Returning result in wherescape with "," delimiter:
enter image description here

I created a minimal tbl table and when I run this:
SELECT 'UPDATE tbl SET ' || c.col_update AS STMT_SQL
,c.col_update
FROM (
select TABLE_NAME
, listagg (concat(field_name, ' = ''#tst''' ), ' , ') within group (order by TABLE_NAME, field_name asc) as col_update
from tbl
group by TABLE_NAME
) c ;
I get the following result:
+--------------------------------------------------+-----------------------------------+
| STMT_SQL | COL_UPDATE |
+--------------------------------------------------+-----------------------------------+
| UPDATE tbl SET LastName = '#tst' , name = '#tst' | LastName = '#tst' , name = '#tst' |
+--------------------------------------------------+-----------------------------------+
I see your JS returns the same values. I edited it to return both columns as one string:
CREATE OR REPLACE PROCEDURE sp_myprocedure_test()
RETURNS VARCHAR(16777216)
language javascript
execute as CALLER
as
$$
const generateUpdStmt = ` SELECT 'UPDATE tbl SET ' || c.col_update AS STMT_SQL
,c.col_update
FROM (
select TABLE_NAME
, listagg (concat(field_name, ' = ''#tst''' ), ' , ') within group (order by TABLE_NAME, field_name asc) as col_update
from tbl
group by TABLE_NAME
) c
;`;
try {
var return_value = "";
var stmt_update = snowflake.createStatement({sqlText: generateUpdStmt});
var result_stmt = stmt_update.execute();
while (result_stmt.next())
{
return_value += result_stmt.getColumnValue(1) + " | " + result_stmt.getColumnValue(2);
}
}
catch (err) {
result = "Failed: Code:" + err.code + "\n State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
return result;
}
return return_value;
$$
call sp_myprocedure_test();
+--------------------------------------------------------------------------------------+
| SP_MYPROCEDURE_TEST |
+--------------------------------------------------------------------------------------+
| UPDATE tbl SET LastName = '#tst' , name = '#tst' | LastName = '#tst' , name = '#tst' |
+--------------------------------------------------------------------------------------+
Can you tell me what is expected?

Related

Concatenating variable with integer returning NULL value in Snowflake Stored Procedure

I am trying to fetch values from Information schema and load them to a table based on conditions by fetching dynamically. However, the DEFAULT_VAL seem to be NULL because of which am unable to construct the query properly to insert into the table.
Please advise how I can concatenate variable with a value that was returned from SQL execute.
var tbl_qry = snowflake.execute({ sqlText: `select TABLE_NAME, ORDINAL_POSITION, case WHEN DATA_TYPE LIKE '%INT%' THEN 'INT'
WHEN DATA_TYPE LIKE '%TIME%' OR DATA_TYPE LIKE '%DATE%'THEN 'DATE' AS ACTION
from INFORMATION_SCHEMA.COLUMNS;`})
while tbl_query.next()
{
if name == 'EMP'
{
DEFAULT_VAL = -1
}
else if name == 'SAL'
{
DEFAULT_VAL = -2
}
else
{
DEFAULT_VAL=-3
}
var COL_LIST = ' (' ;
var VAL_LIST = ' VALUES (' ;
var TABLE_NAME = tbl_query.getColumnValue(1);
var ORDINAL_POSITION = tbl_query.getColumnValue(2);
var ACTION = tbl_query.getColumnValue(3);
if (ORDINAL_POSITION > 1)
{
var COL_LIST = COL_LIST + ',' ;
var VAL_LIST = VAL_LIST + ',' ;
}
if (ACTION_FLAG == 'DATE')
{
VAL_LIST = VAL_LIST + `CONCAT('"','9999-12-31','"')`;
}
else
{
VAL_LIST = VAL_LIST + `DEFAULT_VAL`;
}
var COL_LIST = COL_LIST + COLUMN_NAME;
INSERT_SQL_CMD := 'INSERT INTO VAL_TBL'|| COL_LIST || VAL_LIST ;
snowflake.execute ({sqlText: INSERT_SQL_CMD});
You're not collecting the value of the name column. You need to get that from the ResultSet object. Also, the if statement comparisons need to be inside parenthesis. Finally, you need to collect the ordinal_position the same way this shows how to collect name (keeping in mind column names are case sensitive in Snowflake):
var tbl_qry = snowflake.execute({ sqlText: `select TABLE_NAME, ORDINAL_POSITION, case WHEN DATA_TYPE LIKE '%INT%' THEN 'INT'
WHEN DATA_TYPE LIKE '%TIME%' OR DATA_TYPE LIKE '%DATE%'THEN 'DATE' AS ACTION
from INFORMATION_SCHEMA.COLUMNS;`})
var name;
while tbl_query.next()
{
name = tbl_qry.getColumnValue("name");
if (name == 'EMP')
{
DEFAULT_VAL = -1
}
else if (name == 'SAL')
{
DEFAULT_VAL = -2
}
else
{
DEFAULT_VAL=-3
}
...
Edit: The DEFAULT_VAL is being concatenated as a string literal.
VAL_LIST = VAL_LIST + `DEFAULT_VAL`;
The above is concatenating VAL_LIST with the string literal "DEFAULT_VAL". You can simply remove the backticks to concatenate them. If the DEFAULT_VAL is numeric and concatenation causes problems, you can change to using a template literal:
VAL_LIST = VAL_LIST + `${DEFAULT_VAL}`;

SnowFlake StoredProcedure Code not working when creating dynamic statement for information_schema.columns

I am new to Snowflake, trying to create a small sp to get the row and col count of any table
SP Name :
"GET_ROW_COUNT_TESTSP"("DATABASE_NAME" VARCHAR(16777216), "SCHEMA_NAME" VARCHAR(16777216), "TABLE_NAME" VARCHAR(16777216))
Snippets from body :
var sql_command_columncount = "select COUNT (DISTINCT COLUMN_NAME) AS COLCOUNT from "+DATABASE_NAME+"."+"information_schema.columns where TABLE_CATALOG ="+''+DATABASE_NAME+''+ " and table_schema = " + ''+SCHEMA_NAME+''+ "and table_name = " + '' +TABLE_NAME+ ''+ "and column_name <> ''TESTCOLUMNNAME'' ";
var stmt2 = snowflake.createStatement(
{
sqlText: sql_command_columncount
}
);
var res2 = stmt2.execute();
res2.next();
COLCOUNT = res2.getColumnValue(1);
return COLCOUNT;
This is throwing error, tried all different single quote, not working, hard-coding of dbname,schemaname,tablename is working.
Any help is appreciated.
There is a problem with quote encapsulation on that line. The best way to address this is to use backticks to enclose your SQL string. This will allow use of single and double quotes and line breaks without having to concatenate strings. It will also allow use of template literals, which means for any variable X you can replace its literal value in a string using the syntax ${X}. Your SQL statement would look something like this:
var sql_command_columncount =
`select COUNT (DISTINCT COLUMN_NAME) AS COLCOUNT
from ${DATABASE_NAME}.information_schema.columns
where TABLE_CATALOG = '${DATABASE_NAME}
and table_schema = '${SCHEMA_NAME}'
and table_name = ${TABLE_NAME}
and column_name <> 'TESTCOLUMNNAME'
`;
I agree with the above answer, there were a few missing quotes above so corrected it and below is the working code as tested.
CREATE OR REPLACE PROCEDURE "GET_ROW_COUNT_TESTSP"("DATABASE_NAME"
VARCHAR(16777216), "SCHEMA_NAME" VARCHAR(16777216), "TABLE_NAME"
VARCHAR(16777216))
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS
$$
var sql_command_columncount = `select COUNT (DISTINCT COLUMN_NAME)
AS COLCOUNT from ${DATABASE_NAME}.information_schema.columns where
TABLE_CATALOG = '${DATABASE_NAME}' and table_schema =
'${SCHEMA_NAME}'
and table_name = '${TABLE_NAME}' and column_name <>
'TESTCOLUMNNAME'
`
var stmt2 = snowflake.createStatement(
{
sqlText: sql_command_columncount
}
);
var res2 = stmt2.execute();
res2.next();
COLCOUNT = res2.getColumnValue(1);
return COLCOUNT;
$$;
Cleaner version without using ticks and used double quote and parameterized ...
create or replace procedure getrowcounts_1(DATABASE_NAME VARCHAR, SCHEMA_NAME VARCHAR, TABLE_NAME VARCHAR)
RETURNS VARCHAR LANGUAGE JAVASCRIPT AS $$
var sql_command="Select row_count from "+DATABASE_NAME +"."+SCHEMA_NAME + "."+ "TABLES WHERE TABLE_NAME="+"'"+TABLE_NAME+"'";
var stmt = snowflake.createStatement(
{
sqlText: sql_command
}
);
var res = stmt.execute();
res.next();
COLCOUNT = res.getColumnValue(1);
return COLCOUNT;
$$;
Output:

Snowflake Database: Want to use value from a column in a table as a column name in select statement for another table

I am using snowflake
CREATE OR REPLACE PROCEDURE test1(TBLNM varchar)
RETURNS VARCHAR(16777216) not null
LANGUAGE JAVASCRIPT
$$
var sql_cmd = "SELECT col1 FROM TBLNM ;
var ins = `insert into tabl2 (column1) SELECT :1
FROM table1 `
;
var stmt = snowflake.createStatement(
{
sqlText: sql_cmd
}
);
var res = stmt.execute();
while (res.next()) {
var column1 = res.getColumnValue(1);
ins_stmt=snowflake.execute(
{
sqlText: ins,
binds: [column1]
}
);
}
$$
for eg in TBLNM i have 3 rows values like
col1
name
lastname
Grade
and what i am trying here is , the value 'name' from TBLNM should be treated as column name in select statement of table 1 to fetch the values
insert into tabl2 (column1) SELECT name FROM table1.
this should insert name of person in table like :
column1
Peter
Parker
5th
but it is inserting 'name' as a value. It is treating 'name' as a text.
column1
Name
Lastname
Grade
Please help me out to treat bind variable as column name rather then a string.
You can use Javascript concatenation rather than bind variables.
This works for me:
CREATE OR REPLACE PROCEDURE test1(TBL_NAME varchar, COL_NAME varchar)
RETURNS VARCHAR(16777216) not null
LANGUAGE JAVASCRIPT
AS
$$
var stmt = snowflake.createStatement(
{
sqlText: `SELECT ` + COL_NAME + ` FROM ` + TBL_NAME
});
var res = stmt.execute();
while (res.next()) {
var column1 = res.getColumnValue(1);
var ins_stmt = snowflake.createStatement(
{
sqlText: `INSERT INTO table2 SELECT ` + column1 + ` FROM table1`
});
var res_ins = ins_stmt.execute();
}
$$;
call test1('store', 'col1');
Some examples also present here:
https://community.snowflake.com/s/article/Snowflake-Introduces-Javascript-Stored-Procedures
Regards,

Equivalent of LINQ let clause in SQL Server

I have the following code in linq:
(from emp in db.EMPLOYEES
from tab in db.CATEGORY.Where(o => o.Id == 19).DefaultIfEmpty()
on emp.Id = tab.EMP_ID
let url = (!tab.PAGE.StartsWith("/") && !tab.PAGE.StartsWith("#"))
? tab.PAGE + "(" + emp.EMP_VALUE + ")"
: tab.PAGE.Contains("?")
? tab.PAGE + "&Id=" + emp.EMP_VALUE + "&fromSSR=" + BooleanStr.True
: tab.PAGE + "?Id=" + emp.EMP_VALUE + "&fromSSR=" + BooleanStr.True
select new EmployyeModel
{
Id = emp.Id,
RedirectURL = tab.NOT_CH_APPROVAL == BooleanStr.True ? url + "&userCanApprove=1" : url,
}
I need to write the above query in sql server and need help concerning the "let url" part.
How do I translate let in sql server and use it in the select?
Thanks for any help.
Create a user-defined function:
CREATE FUNCTION fnBuildUrl (
#page varchar(100),
#emp_value varchar(100)
)
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #url varchar(1000)
SELECT #url = CASE WHEN LEFT(#page, 1) = '/' THEN
#page + '(' + #emp_value + ')'
ELSE
CASE WHEN CHARINDEX('?', #page) > 0 THEN
#page + '&Id=' + #emp_value + '&fromSSR=T'
ELSE #page + '?Id=' + #emp_value + '&fromSSR=T' END
END
RETURN #url
END
GO
Then call it in your SELECT:
SELECT emp.Id,
CASE WHEN tab.NOT_CH_APPROVAL = 'T' THEN dbo.fnBuildUrl(tab.PAGE, emp.EMP_VALUE) + '&userCanApprove=1'
ELSE dbo.fnBuildUrl(tab.PAGE, emp.EMP_VALUE) END AS RedirectURL
FROM EMPLOYEES emp
LEFT OUTER JOIN CATEGORY tab
ON tab.EMP_ID = emp.Id
AND tab.Id = 19

how to use more than one string in like expression in sql

I have a list of 20 (or more) words (string) and want to select the rows that have these word in 3 of their column. I should use like expression of sql. but I don't know how to use more than one string in like expression.
(I do it with union now, but I have at least 60 select statement and think it reduced the performance, Is it really reduce the performance?)
//get the advertise that have similar keywords
foreach (string str in keywords)
{
if (str != "")
{
if (!string.IsNullOrEmpty(sqlQuery)) sqlQuery += " union";
sqlQuery = "select * from AD_Advertise where (AdKeyWords like N'%" + str + "%'"
+ " OR AdTitle like N'%" + str + "%' "
+ " OR AdDescription like N'%" + str + "%' "
+ " OR AdGroupItemCode=" + adinfo.AdGroupItemCode + ")"
+ " AND AdSiteID=" + CMSContext.CurrentSiteID
+ " AND AdShow='True' "
+ " AND ItemID != " + ADId;
}
}
ds = cn.ExecuteQuery(sqlQuery,null);//("AD.Advertise.selectall", null, where, "ItemModifiedWhen");
Answer:
At last I used below code:
if object_id('tempdb..#WordList') is not null
drop table #WordList
CREATE TABLE #WordList ( KeyWord nvarchar(100))
insert into #WordList values (N'حقوقی'),(N'وکیل');
SELECT DISTINCT *
FROM AD_ADvertise a
LEFT JOIN #WordList k
ON a.AdKeywords LIKE '%' + k.KeyWord + '%'
OR a.AdTitle LIKE '%' + k.KeyWord + '%'
OR a.AdDescription LIKE '%' + k.KeyWord + '%'
WHERE
(k.KeyWord IS NOT NULL OR a.AdGroupItemCode = #AdGroupItemCode)
AND a.AdSiteId = #AdSiteId
AND a.AdShow = 'True'
AND a.ItemId != #ItemId
;drop table #WordList
Create a stored procedure with a table valued parameter that takes your list of strings.
Have a join between the table valued parameter and your tables AD_Advertise on the like.
Heres how to do the Table type + the stored procedure:
CREATE TYPE WordList AS TABLE (Word NVARCHAR(50));
GO
CREATE PROCEDURE GetAddsMatchingKeywords
#KeywordList WordList READONLY,
#AdGroupItemCode VARCHAR(50),
#AdSiteId INT,
#ItemId INT
AS
SELECT DISTINCT
a.AdTitle,
a.ItemId -- extend to the full column list
FROM AD_ADvertise a
LEFT JOIN #KeywordList k ON a.AdKeywords LIKE '%' + k.Word + '%' OR a.AdTitle LIKE '%' + k.Word + '%' OR a.AdDescription LIKE '%' + k.Word + '%'
WHERE
(k.Word IS NOT NULL OR a.AdGroupItemCode = #AdGroupItemCode)
AND a.AdSiteId = #AdSiteId
AND a.AdShow = 'True'
AND a.ItemId = #ItemId
GO
edit - misread the original question - thought you wanted to match 3 or more words.
This version matches any that have a single word in any of the 3 columns - like you wanted I think.
Don't ever build up a SQL string like that. Read about SQL injection before you go any further.
Ideally, you would have a table of keywords, rather than a string, and a join table to link between the item and the keywords.
If you persist in doing it this way, have a look at Full Text Search
//get the advertise that have similar keywords
foreach (string str in keywords)
{
if (str != "")
{
if (!string.IsNullOrEmpty(sqlQuery)) sqlQuery += " union";
sqlQuery = "select * from AD_Advertise where (AdKeyWords + AdTitle + AdDescription like N'%" + str + "%'"
+ " OR AdGroupItemCode=" + adinfo.AdGroupItemCode + ")"
+ " AND AdSiteID=" + CMSContext.CurrentSiteID
+ " AND AdShow='True' "
+ " AND ItemID != " + ADId;
}
}
ds = cn.ExecuteQuery(sqlQuery,null);//("AD.Advertise.selectall", null, where, "ItemModifiedWhen");

Resources