Function in Snowflake - snowflake-cloud-data-platform

create or replace function MMfGetPropValChar(propvalue varbinary(518))
RETURNS varchar(255)
as
$$
select
(CASE
WHEN substr(${PROPVALUE}, 1, 3) = '0x00E700' Then CAST(substr(${PROPVALUE}, 9, 510) As Varchar(255))
WHEN substr(${PROPVALUE}, 1, 3) = '0x01E700') Then CAST(substr(${PROPVALUE}, 9, CAST(substr(${PROPVALUE}, 6, 1) as integer)) As Varchar(64))
)
ELSE (select current_timestamp)
End as test
return test;
$$
Below is the error I am getting:
Compilation of SQL UDF failed: SQL compilation error: syntax error line 2 at position 0 unexpected 'select'. syntax error line 3 at position 1 unexpected 'CASE'. syntax error line 4 at position 45 unexpected 'Then'.

The following example might help:
CREATE OR REPLACE FUNCTION MMfGetPropValChar(p1 varbinary)
RETURNS varchar
as
$$
select
CASE
WHEN substr(p1::varchar, 1, 3) = '0x00E700'
then substr(p1::varchar, 9, 510)::varchar
WHEN substr(p1::varchar, 1, 3) = '0x01E700'
then substr(p1::varchar, 9, substr(p1::varchar, 6, 1))::varchar
ELSE current_timestamp()::varchar
End as test
$$
;
SELECT MMfGetPropValChar(to_binary(hex_encode('Snowflake'), 'HEX')) as res;
RES
2021-10-05 05:14:52.369 -0700
Note: I cast to varchar using the :: notation, just to make the SQL easier to read.

Related

Snowflake case statement to return multiple columns value in procedure

Below is the snowflake procedure in am trying to run.
CREATE OR REPLACE PROCEDURE test()
RETURNS VARCHAR(16777216)
LANGUAGE SQL
AS
$$
DECLARE
V_LAT varchar;
V_LNG varchar;
BEGIN
INSERT INTO test.crs_compact.case_test
(
c_address,
c_comment
)
SELECT
(CASE WHEN c_nationkey = 0 then (:V_LAT=a.c_address, :V_LNG=c_comment)
END)
from "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF1"."CUSTOMER" as a;
return 'Data Loaded Successfully';
END;
$$
while calling the procedure getting below error.
Uncaught exception of type 'STATEMENT_ERROR' on line 11 at position 1 : SQL compilation error: error line 7 at position 3 Invalid argument types for function 'IFF': (BOOLEAN, ROW(BOOLEAN, BOOLEAN), NULL)
(a.c_address, c_comment) or (:V_LAT=a.c_address, :V_LNG=c_comment) is a ROW
you cannot put a ROW inside a CASE (which is also an IFF)..
Where you have typed looks like you are trying to do this:
SELECT
a.c_address, c_comment
from "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF1"."CUSTOMER" as a
WHERE c_nationkey = 0;
Or more to the point:
INSERT INTO test.crs_compact.case_test
(
c_address,
c_comment
)
SELECT
a.c_address, c_comment
from "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF1"."CUSTOMER" as a
WHERE c_nationkey = 0;
Now if you want those null values instead of skipping them:
INSERT INTO test.crs_compact.case_test
(
c_address,
c_comment
)
SELECT
iff(c_nationkey = 0, a.c_address, NULL)
,iff(c_nationkey = 0, c_comment, NULL)
from "SNOWFLAKE_SAMPLE_DATA"."TPCH_SF1"."CUSTOMER" as a;

Issue creating function with a "with"

I am trying to create this function(variable names changed so allow naming mistakes as when tested original without function works fine),
CREATE FUNCTION [dbo].[GetQuality](#FruitID VARCHAR(200))
RETURNS varchar(200)
AS
BEGIN
DECLARE #Result varchar(200)
SET #Result = (
WITH
latest AS
(
SELECT * FROM (SELECT TOP 1 * FROM Fruits_Crate WHERE FruitID like #FruitID ORDER BY ExpiryDate DESC) a
),
result AS
(
SELECT
latest.ExpiryDate as LatestExpiryDate, latest.Size as LatestSize, latest.Weight as LatestWeight,
previous.ExpiryDate as PreviousExpiryDate, previous.Size as PreviousSize, previous.Weight as PreviousWeight,
CASE SIGN((latest.Weight * latest.Size) - (previous.Weight * previous.Size))
WHEN 1 THEN 'Increased'
WHEN 0 THEN 'Static'
WHEN -1 THEN 'Decreased'
ELSE 'No Previous Data'
END AS Movement
FROM (SELECT TOP 1 * FROM (SELECT TOP 2 * FROM Fruits_Crate WHERE FruitID like #FruitID ORDER BY ExpiryDate DESC) x ORDER BY ExpiryDate) previous
FULL OUTER JOIN latest ON previous.FruitID = latest.FruitID
)
SELECT result.Movement AS FruitMovement from result;
)
RETURN #Result
END
Error
Msg 156, Level 15, State 1, Procedure GetQuality, Line 10
Incorrect syntax near the keyword 'WITH'.
Msg 319, Level 15, State 1, Procedure GetQuality, Line 10
Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon.
Msg 102, Level 15, State 1, Procedure GetQuality, Line 14
Incorrect syntax near ','. Msg 102, Level 15, State 1, Procedure GetQuality, Line 31 Incorrect syntax near ')'.
I think you can move the assignment down, and put a semi colon before the with, and all should be good.
CREATE FUNCTION [dbo].[GetQuality](#FruitID VARCHAR(200))
RETURNS varchar(200)
AS
BEGIN
DECLARE #Result varchar(200);
WITH
latest AS
(
SELECT * FROM (SELECT TOP 1 * FROM Fruits_Crate WHERE FruitID like #FruitID ORDER BY ExpiryDate DESC) a
),
result AS
(
SELECT
latest.ExpiryDate as LatestExpiryDate, latest.Size as LatestSize, latest.Weight as LatestWeight,
previous.ExpiryDate as PreviousExpiryDate, previous.Size as PreviousSize, previous.Weight as PreviousWeight,
CASE SIGN((latest.Weight * latest.Size) - (previous.Weight * previous.Size))
WHEN 1 THEN 'Increased'
WHEN 0 THEN 'Static'
WHEN -1 THEN 'Decreased'
ELSE 'No Previous Data'
END AS Movement
FROM (SELECT TOP 1 * FROM (SELECT TOP 2 * FROM Fruits_Crate WHERE FruitID like #FruitID ORDER BY ExpiryDate DESC) x ORDER BY ExpiryDate) previous
FULL OUTER JOIN latest ON previous.FruitID = latest.FruitID
)
SELECT #RESULT = result.Movement AS FruitMovement from result;
RETURN #Result
END

SQL Server : Invalid length parameter passed to the LEFT or SUBSTRING function

I'm trying to execute a SQL Server SUBSTRING query but its throwing error.
The query is:
select
substring(testcasename, 2, 5) val1,
substring(testcasename, 2, CHARINDEX(',', testcasename, 2) - 2) val2
from
tce_lineno
where
testcasename <> '' and project='proj001'
Error is:
Msg 537, Level 16, State 5, Line 1
Invalid length parameter passed to the LEFT or SUBSTRING function.
Is anything wrong in this query?
As I mentioned in comments When testcasename does not have , then CHARINDEX(',',testcasename,2) will return 0 so Substring will fail.
To fix that use case statement, When , is not found use length of testcasename
SELECT Substring(testcasename, 2, 5) val1,
Substring(testcasename, 2, CASE
WHEN Charindex(',', testcasename, 2) > 2 THEN Charindex(',', testcasename, 2) - 2
ELSE Len(testcasename)
END) val2
FROM tce_lineno
WHERE testcasename <> ''
AND project = 'proj001'
Add this clause to where
And testcasename like '%,%'
Error happens because some of the records does not have comma (,) in it

SQL server can not accept median function

I wrote this Median function but it is executing with errors.. Can someone guide me what's wrong in the code?
BEGIN
CREATE TABLE #ITEMORDERDETAILS
(
ITEM CHAR(15),
QTYSHP DECIMAL(21, 6),
RQDATE DATETIME
)
DECLARE #Median FLOAT
DECLARE #ITEM CHAR(15)
DECLARE #ORDERCNT INT
SET #ITEM=#ITEMN
INSERT #ITEMORDERDETAILS
SELECT ITEM,
QTYSHP,
RQDATE
FROM tbl123456
WHERE PRICE != '0'
AND SALESMN != 'WB'
AND RQDATE > ( getdate () - 180 )
AND ITEM = #ITEM
UNION
SELECT ITEM,
QTYSHP,
RQDATE
FROM tbl123
WHERE PRICE != '0'
AND SALESMN != 'WB'
AND RQDATE > ( getdate () - 180 )
AND ITEM = #ITEM
SELECT #ORDERCNT = count (1)
FROM #ITEMORDERDETAILS
--SELECT #ORDERCNT
SELECT #Median = ( sum(QTYSHP) / #ORDERCNT )
FROM #ITEMORDERDETAILS
SELECT #Median AS 'Median'
--SELECT * from #ITEMORDERDETAILS
DROP TABLE #ITEMORDERDETAILS
RETURN #Median
END
ERRORS
Msg 2772, Level 16, State 1, Procedure
f_Get_Average_Order_Size_Median, Line 34 Cannot access temporary
tables from within a function.
Msg 2772, Level 16, State 1, Procedure
f_Get_Average_Order_Size_Median, Line 35 Cannot access temporary
tables from within a function.
Msg 2772, Level 16, State 1, Procedure
f_Get_Average_Order_Size_Median, Line 42 Cannot access temporary
tables from within a function.
Msg 156, Level 15, State 1, Procedure
f_Get_Average_Order_Size_Median, Line 46 Incorrect syntax near the
keyword 'SELECT'.
The reason is in your error message:
Line 34 Cannot access temporary tables from within a function
If you make a function, there are limits to what you can access.
However, if you use SQL Server 2012, you do not need to write your own median function but you can use PERCENTILE_DISC
PERCENTILE_DISC (0.5) WITHIN GROUP (ORDER BY XXXX)
OVER (PARTITION BY YYYY) AS Median

SQL Server 2008 R2 bug or what?

I am observing a strange behaviour. The following code that is related to conversion of a substring of nvarchar to tinyint works fine:
DECLARE #s nvarchar(50) = N'Line 1'
DECLARE #n tinyint = SUBSTRING(#s, 6, 10)
SELECT
N'Line 1' AS explicit_nvarchar,
#s AS nvarchar_variable,
SUBSTRING(#s, 6, 10) AS nvarchar_substring
, #n AS tinyint_var_converted_earlier
, CAST(SUBSTRING(#s, 6, 10) AS tinyint) AS cast_sub_var
, CAST(SUBSTRING(N'Line 1', 6, 10) AS tinyint) AS cast_nvarchar_substr
I can observe the following output in the Microsoft SQL Server Management Studio:
.
However, the code that tries to do with the SELECT from the tables fails if the commented line is uncommented:
Select
[order].[identifier] As [order_identifier],
[plan_data].[starts_on] As [from],
[plan_data].[ends_on] As [to],
[workplace].[identifier] AS line_identifier,
SUBSTRING([workplace].[identifier], 6, 10) AS line_no_str
--, CAST(SUBSTRING([workplace].[identifier], 6, 10) AS int) AS line_no
From
...
With commented line...
but with uncommented line (both for conversion to tinyint or int)...
Update: Strange. I have added the last line to the WHERE to check whether all lines
contain the 'Line...
Where
[plan].[identifier] = #lPlanIdentifier And
(
[plan_data].[starts_on] Between #fromUTC And #toUTC Or
[plan_data].[ends_on] Between #fromUTC And #toUTC)
AND LEFT([workplace].[identifier], 4) = N'Line'
... and all of a sudden it works also with the uncommented line. How can I discover what causes the problem?
Update2: I should have followed the Hamlet Hakobyan's answer exactly as it was published. When looking inside the table, I can see:
You have value in identifier column for which SUBSTRING([workplace].[identifier], 6, 10) returns ined.
Try this query:
SELECT * FROM [workplace]
WHERE SUBSTRING([identifier], 6, 10) = N'ined'
As indicated by the comments and other answer(s), you have a value in [workplace].[identifier] for one or more rows that fails to cast to an INT. When you added the following to the WHERE clause, you eliminated the row(s) that has this data before it was converted in the SELECT clause:
LEFT([workplace].[identifier], 4) = N'Line'
Run the following to get a distinct list of values in [workplace].[identifier]
SELECT DISTINCT [workplace].[identifier]
FROM [workplace]
Whenever you have an error converting a varchar to a number the easiest way to find the data is using IsNumeric. However IsNumeric doesn't mean IsInteger so its worth removing scientific or decimal formats as described by G Mastros in this answer
SELECT
[order].[identifier] As [order_identifier],
[plan_data].[starts_on] As [from],
[plan_data].[ends_on] As [to],
[workplace].[identifier] AS line_identifier,
SUBSTRING([workplace].[identifier], 6, 10) AS line_no_str
--, CAST(SUBSTRING([workplace].[identifier], 6, 10) AS int) AS line_no
FROM
...
WHERE
IsNumeric(SUBSTRING([workplace].[identifier], 6, 10) + '.0e0') = 0
If you can't fix it but you can use a default value than you could use a case statement to do the conversion only when its legal to do so.
SELECT
[order].[identifier] As [order_identifier],
[plan_data].[starts_on] As [from],
[plan_data].[ends_on] As [to],
[workplace].[identifier] AS line_identifier,
CASE WHEN IsNumeric(SUBSTRING([workplace].[identifier], 6, 10) + '.0e0') = 1
THEN CAST(SUBSTRING([workplace].[identifier], 6, 10) AS int)
ELSE Null --Default to null in this example
END as line_no

Resources