How to escape single quote while dynamically creating sql in a snowflake stored procedure? - dynamic-sql

CREATE OR REPLACE PROCEDURE DEPARTMENT_STORED_PROC(table_name VARCHAR)
returns variant not null
language javascript
as
$$
var sql_cmd_ingest = "copy into DEPARTMENT.CLASSES." + TABLE_NAME + "from '#DEPARTMENT.CLASSES.CLASSES_PREPROD_STAGE/" + TABLE_NAME + "';";
snowflake.execute({sqlText: sql_cmd_ingest} );
$$
The above is just a code snippet. I am getting error for single quote before '#DEPARTMENT.CLASSES.CLASSES_PREPROD_STAGE/"
Execution error in store procedure DEPARTMENT_STORED_PROC: SQL compilation error: syntax error line 1 at position 70 unexpected ''#DEPARTMENT.CLASSES.CLASSES_PREPROD_STAGE/CLASSES_TABLE''. At Snowflake.execute, line 11 position 10

You need a space after TABLE_NAME and before "from
var sql_cmd_ingest = "copy into DEPARTMENT.CLASSES." + TABLE_NAME + " from '#DEPARTMENT.CLASSES.CLASSES_PREPROD_STAGE/" + TABLE_NAME + "';";
Also I highly recommend using JavaScript replacement variables when building SQL statements inside stored procedures. You can use three different types of quotes to define strings in JavaScript, double " single ' and back tick `.
If you use the back tick to start and end your strings, you can then use any JavaScript variable inside that string without terminating the string and concatenating with +. For example:
var sql_cmd_ingest = `copy into DEPARTMENT.CLASSES.${TABLE_NAME} from '#DEPARTMENT.CLASSES.CLASSES_PREPROD_STAGE/${TABLE_NAME}';`;
It makes the SQL much more readable, especially when the SQL is long and multi-line.
Also, the stage name should not be in quotes in the SQL. You should remove them:
var sql_cmd_ingest = `copy into DEPARTMENT.CLASSES.${TABLE_NAME} from
#DEPARTMENT.CLASSES.CLASSES_PREPROD_STAGE/${TABLE_NAME};`;

Related

How to build dynamic query argument for external query?

I want to build dynamic query parameter based on declared variable for EXTERNAL_QUERY.
So I declare STRING variable via concatenation 2 strings:
DECLARE str STRING DEFAULT "SELECT * FROM public.stats WHERE import_date >= "||"'2021-11-29'";
Then trying to set this variable as argument into EXTERNAL_QUERY:
SELECT * FROM EXTERNAL_QUERY("dataset.location.conn_name", str);
It's returns an error:
Query error: Invalid table-valued function EXTERNAL_QUERY Connection argument in EXTERNAL_QUERY must be a literal string or query parameter
What I do wrong?
Not perfect, but works for me.
DECLARE str STRING DEFAULT '''"SELECT * FROM public.stats WHERE import_date >= "'''||"'2021-11-29'";
EXECUTE IMMEDIATE """
SELECT * FROM EXTERNAL_QUERY(\"dataset.location.conn_name\",?);
"""
USING str
Thanks #Timogavk for your answer, I spent a while looking for a solution.
Though in my case I had to change the DECLARE for something more like this, without the additional quotations for it to work (I was working on dynamically querying different tables):
DECLARE str STRING DEFAULT 'SELECT * FROM ' || <table_name> || ' Limit 10';

SSIS Precedence Constraint Not Working Due to Path Formatting

Using SSIS and MS-SQL Server 2012
I have a SQL Task executing:
SELECT COUNT(id) as id FROM PORG_Files WHERE filename = ?
It never returns anything except 0 because the SSIS filename looks like:
\\\\erp\\shares\\Save\\item_1168.txt
And the Filename in the Table Looks like:
\\erp\shares\Save\item_1168.txt
I don't think I want to insert the filename into the table like that, so how/where do I format so I can make the matches to get my constraint that depends on this to fire.
Thanks!
Ok, If I run this query in SQL Manager it works.
SELECT COUNT(id) as id FROM PORG_Files WHERE filename = REPLACE('\\\\erp\\shares\\Save\\item_1168.txt','\\','\')
When I put the equivilant into the SQL Task Editor for the SQLStatement, it still returns 0
SELECT COUNT(id) as id FROM PORG_Files WHERE filename = REPLACE(?,'\\','\')
Workaround - Expression
Try using expression instead of passing parameters:
In the Execute SQL Task, Go To Expression Tab, Add an expression for SQLStatementSource property as following:
"SELECT COUNT(id) as id FROM PORG_Files WHERE filename = '" + #[User::CurrentFileName] + "'"
Are you just stored the file name directly in a variable? If you store the file name as the expression of a string variable instead, the output format will what you described. The result of the 4 forward slashes (\) will will be only 2, and the 2 \ will be a single one. This is because the the forward slash must be escaped in an SSIS expression. In the Expression field of the variable, click the ellipsis and enter the text inside double quotes to make this an expression such as in the example below.
“\\\\erp\\shares\\Save\\item_1168.txt”

Using PostgreSQL parameters with booleans in EXECUTE / USING

i want to use parameters for my dynamic queries. I have a statements like so:
RETURN QUERY EXECUTE 'SELECT * FROM boards AS b WHERE b.slug = $1 AND $2'
USING filter_slug, parent_id_query;
I get a ERROR: argument of AND must be type boolean, not type text
if i do it like this:
RETURN QUERY EXECUTE 'SELECT * FROM boards AS b WHERE b.slug = ''' || filter_slug || ''' AND ' || parent_id_query;
it works though.
I feel like i am missing something / not understanding something. Please help.
What you are missing is how parameters are used. Parameters are not macros that replace arbitrary text inside a SQL statement. Instead, they are literal values assigned to "variables" inside the code. These values are typically numbers, strings, or dates.
In particular, parameters cannot be used for:
identifiers (columns names and table names)
function names
operators
SQL keywords
general expressions
So, unfortunately, you have to construct that part of the query without a generic parameter (although you can have $2 = $3)

why it doesnt accept a concated string as parameter in SP?

I wrote an SP that works perfectly. I was calling the SP with regular parameters and worked. However, now I need to call the SP like below
EXEC #v_nReturn = sp_get_next_value 'LP_' + #WhID + '_COUNTER'
The intellisense gives an error on the first '+' sign.
It says "incorrect syntax err...."
#WhID is NVARCHAR(10), so I shouldn't convert it to NVARCHAR.
what is the problem?
SQL Server doesn't do (full) expression parsing when you call a stored procedure. This is definitely an area where a small change would be highly convenient, although there are probably good reasons for the limitation.
As mentioned in a comment, use a separate variable:
DECLARE #arg varchar(256) = 'LP_' + #WhID + '_COUNTER';
EXEC #v_nReturn = sp_get_next_value #arg;
Be careful if #WhID is numeric. Then you need to convert the value to a string first.
Try with parens:
EXEC #v_nReturn = (sp_get_next_value 'LP_' + #WhID + '_COUNTER')

Fetch column from result set having expression based SELECT without alias name

This should be a Basic Question, but I was unable to answer when asked for. Sorry if it is too broad!
SELECT * FROM
(
SELECT MYGROUPKEY,MAX(MYCOLUMN)/MIN(MYCOLUMN) FROM
MYTABLE
GROUP BY MYGROUPKEY
) MYSUBQUERY;
This Query is Syntactically Valid. But how would someone fetch the second column of this resultset, if they have to attempt with some identifier? (The column was never provided with an alias). How can it be formatted in SQL*Plus. (COL <column_name> FORMAT A<num>)
Will that be first few characters of the text MAX(MYCOLUMN)/MIN(MYCOLUMN).
I understand, RDBMS concept doesnt like this. But still why would the Database execute this SQL then? It never name the resultset's columns unless the query carry it?
Assuming that the expression is less than 30 characters
SQL> column "MAX(MYCOLUMN)/MIN(MYCOLUMN)" format <<some format>>
should work. You can realistically look at the raw column header in SQL*Plus (before applying any formatting) to see what the assigned alias is. Depending on the actual expression, there can be various rules applied-- eliminating spaces, for example, and limiting the expression to 30 characters. It's generally easier to copy & paste from SQL*Plus and surround the assigned alias with double quotes.
There is an implicit alias which is the expression with any whitespace removed. But as the expression contains characters which aren't invalid for an identifier it has to be quoted, as "MAX(MYCOLUMN)/MIN(MYCOLUMN)". You can refer to that in an SQL*Plus column command too.
e.g.
SELECT MYGROUPKEY, "MAX(MYCOLUMN)/MIN(MYCOLUMN)" FROM
(
SELECT MYGROUPKEY,MAX(MYCOLUMN) / MIN(MYCOLUMN)
FROM MYTABLE
GROUP BY MYGROUPKEY
) MYSUBQUERY;
SQL Fiddle.
There is a further wrinkle though. As identifieres can only be 30 characters, if the expression is longer than that the implicit alias is also too long to be referred to within the SQL itself. This would get an ORA-00972:
SELECT MYGROUPKEY, "MAX(MYCOLUMN)/MIN(MYCOLUMN)*(1000/1000)" FROM
(
SELECT MYGROUPKEY,MAX(MYCOLUMN) / MIN(MYCOLUMN) * (1000 / 1000)
FROM MYTABLE
GROUP BY MYGROUPKEY
) MYSUBQUERY;
It's better to add your own aliases, even for shorter expressions.
But you can sometimes still use the longer alias, for example through JDBC, where it appears in the metadata (as it does here as the column label). A section of a test that accesses the same data as the SQL Fiddle:
pStmt = (OraclePreparedStatement) conn.prepareStatement(
"SELECT MYGROUPKEY,MAX(MYCOLUMN) / MIN(MYCOLUMN) * (1000 / 1000) "
+ "FROM MYTABLE GROUP BY MYGROUPKEY");
rSet = (OracleResultSet) pStmt.executeQuery();
OracleResultSetMetaData rsmd = (OracleResultSetMetaData) rSet.getMetaData();
for( int i=1; i <= rsmd.getColumnCount(); i++ ) {
System.out.println( "Column label: " + rsmd.getColumnLabel(i));
System.out.println( "Column Type: " + rsmd.getColumnTypeName(i));
System.out.println();
}
while (rSet.next())
{
System.out.println("MYGROUPKEY: " + rSet.getInt("MYGROUPKEY"));
System.out.println("MAX(MYCOLUMN)/MIN(MYCOLUMN)*(1000/1000): "
+ rSet.getInt("MAX(MYCOLUMN)/MIN(MYCOLUMN)*(1000/1000)"));
}
Produces output:
Column label: MYGROUPKEY
Column Type: NUMBER
Column label: MAX(MYCOLUMN)/MIN(MYCOLUMN)*(1000/1000)
Column Type: NUMBER
MYGROUPKEY: 1
MAX(MYCOLUMN)/MIN(MYCOLUMN)*(1000/1000): 2
Note the the column label/name reported n the metadata is the same as shown in the Fiddle, the expression with whitespace removed; and that I can use that as the argument for getInt() even though it's longer than 30 characters.