define GEOGRAPHY point variable in snowflake - snowflake-cloud-data-platform

I tried:
point GEOGRAPHY
point := ST_MAKEPOINT(-2.6661587, 53.368992)
without success. Is this possible? To be used in SQL further down.
PS:
This:
BEGIN
LET point GEOGRAPHY := ST_MAKEPOINT(-2.6661587, 53);
RETURN point;
END;
SELECT
st_distance(:point, POLYGON) AS some_distance
FROM "bla"."di"."bla"
WHERE
1=1
ORDER BY st_distance(:point, POLYGON) ASC
throws:
Error: Bind variable :point not set. (line 7)

Using Snowflake Scripting block:
BEGIN
LET point GEOGRAPHY := ST_MAKEPOINT(-2.6661587, 53.368992);
RETURN point;
END;
Output:
{"coordinates":[-2.666158700000000e+00,5.336899200000000e+01],"type":"Point"}
EDIT:
CREATE OR REPLACE TABLE some_table
AS
SELECT ST_MAKEPOINT(-2.6661587, 54) POLYGON;
DECLARE
point GEOGRAPHY;
rs RESULTSET;
BEGIN
point := ST_MAKEPOINT(-2.6661587, 53);
rs := (EXECUTE IMMEDIATE
$$SELECT st_distance(TO_GEOGRAPHY(?), POLYGON) AS distance
FROM some_table
WHERE 1=1
ORDER BY distance ASC$$
USING (point));
RETURN TABLE(rs);
END;
Output:
DISTANCE
111,195.101177485

Related

Snowflake sql stored procedure bind variable not set

I try to run in snowflake the following sql stored procedure with a declare variable inside but i got the following error : Error: Bind variable for object MYTABLE AS MYTABLE not set (line 13).
when I hard code the value into the function identifier it's work by the way...
CREATE OR REPLACE PROCEDURE DBNAME.SCHEMANAME."SP_test"()
RETURNS varchar
LANGUAGE SQL
EXECUTE AS CALLER
AS
declare
MYTABLE varchar := 'DBNAME.SCHEMANAME.TABLENAME';
--MYRESULT varchar;
BEGIN
-- let MYTABLE varchar := 'DBNAME.SCHEMANAME.TABLENAME';
-- MYTABLE := 'DBNAME.SCHEMANAME.TABLENAME';
--filter to return 1 row
-- let MYRESULT varchar := ( select col1 from IDENTIFIER( 'DBNAME.SCHEMANAME.TABLENAME' ) where col2=2 ) ;
--ko
let MYRESULT varchar := ( select col1 from IDENTIFIER( :MYTABLE ) where col2=2 ) ;
return :MYRESULT;
end;
This works for me:
CREATE OR REPLACE PROCEDURE "SP_test"()
RETURNS varchar
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
declare
MYRESULT resultset;
MYTABLE varchar := 'SERGIU_TESTDB.PUBLIC.CITIBIKE_TRIPS';
MYVAL varchar;
BEGIN
MYRESULT := (select ride_id from identifier(:MYTABLE) limit 1);
let c1 cursor for MYRESULT;
for row_variable in c1 do
MYVAL := row_variable.ride_id;
end for;
return MYVAL;
END
$$;
Calling it:
CALL "SP_test"();
I get a value out of it:
B1CE81D802D68DF8

Snowflake - While Loop using Current_Date

In Snowflake I'm attempting to run a while loop where there is a start date, and the code will loop until it equals today's date.
I'm fairly new to snowflake itself.
First idea was in the while loop check if the variable date was less than the current date. I couldn't seem to get this working even with the Current_Date on it's own or attributed to a variable
execute immediate $$
declare
opdate := '2022-04-29';
currdate := Current_Date;
begin
while (opdate <=currdate) do
opdate :=dateadd(day,1,opdate);
end while;
end;
$$
;
'CURRDATE' cannot have its type inferred from initializer
The second option was to use a datediff to see if the difference between the variable and current_date was equal to zero
execute immediate $$
declare
opdate := '2022-04-29';
currndate := 1;
begin
while (currndate<=0) do
currndate := datediff(day, opdate, CURRENT_DATE);
opdate :=dateadd(day,1,opdate);
end while;
end;
$$
;
error line 7 at position 39 invalid identifier 'CURRENT_DATE'
I've tried many different variants of this code, even use for each and if loops with no luck. I'm seemingly unable to check if a variable is less than the current date. Any help/pointers would be much appreciated.
Without having Current_Date, and having a set start and end date, it appears to run fine
execute immediate $$
declare
opdate := '2022-04-29';
currdate := '2022-05-03';
begin
while (opdate <=currdate) do
opdate :=dateadd(day,1,opdate);
end while;
end;
$$
;
Can you try CURRENT_DATE() with the open and close parens? see block below
execute immediate $$
declare
opdate := '2022-04-29';
currdate := Current_Date();
begin
while (opdate <=currdate) do
opdate :=dateadd(day,1,opdate);
end while;
return opdate; --added this return
end;
$$
;

PL/SQL How to convert a record to an assoc array

I am trying to fetch data from a table(employee) with cursor and save to asso. array. However, fetching data with cursor to a record is more straight-forward and it is troublesome to convert a record to an assoc array(arr). Code below is what I am trying to fetch data to a assoc array and improvement is needed. Or any approaches other than cursor? Thank you.
DECLARE
TYPE AssoArray IS TABLE OF varchar2(30) INDEX BY varchar2(30);
arr AssoArray;
table_rec employee%rowtype;
CURSOR cur IS
SELECT * FROM employee;
BEGIN
OPEN cur;
LOOP
FETCH cur into table_rec;
EXIT WHEN cur%notfound;
-- how to improve the code in the section below,
arr('col1') := table_rec.col1;
arr('col2') := table_rec.col2;
arr('col3') := table_rec.col3;
...
arr('col50') := table_rec.col50;
-- end of section
-- do sth
END LOOP;
END;
Could you explain what do you want to get. But I see in your code arr AssoArray will contain only one last fetched row and will be rewrite each cycle. Are you realy need it? My suggestion is you want to get some rows of table as associated array. If its true, you may create the array as table of rowtype and (for example) indexed it by id(if you ID-s is integer).
TYPE AssoArray IS TABLE OF employee%rowtype INDEX BY PLS_INTEGER;
For example
create table tmp_table_01 as select * from all_objects where rownum < 11;
DECLARE
TYPE AssoArray IS TABLE OF tmp_table_01%rowtype INDEX BY varchar2(30);
arr AssoArray;
table_rec tmp_table_01%rowtype;
CURSOR cur IS
SELECT * FROM all_objects where rownum < 20;
BEGIN
OPEN cur;
LOOP
FETCH cur into table_rec;
EXIT WHEN cur%notfound;
-- how to improve the code in the section below,
arr(table_rec.object_name) := table_rec;
-- end of section
dbms_output.put_line(table_rec.object_name ||' '||arr(table_rec.object_name).object_id );
-- do sth
END LOOP;
END;
EDIT:
If you want to make comraisons by table structure you may use Dynamic SQL. NExt code get data from table TMP_TABLE_01 sort it by object_id, compare neighbour rows, and return count of difference row.
DECLARE
l_sql varchar2(32767);
TYPE t_cols_type IS table of varchar2(30);
l_cols t_cols_type ;
l_result number;
BEGIN
SELECT c.COLUMN_NAME
BULK COLLECt INTO l_cols
FROM user_tab_cols c
WHERE c.TABLE_NAME = 'TMP_TABLE_01';
l_sql := 'WITH s AS (SELECT t.*, row_number() OVER (ORDER BY object_id) AS rn FROM TMP_TABLE_01 t)
SELECT count(*)
FROM s
JOIN s sub_s ON s.rn = sub_s.rn - 1
where 1=0 ';
FOR i IN 1 .. l_cols.last LOOP
l_sql := l_sql || ' OR decode(s.'||l_cols(i)||',sub_s.'||l_cols(i)||',1,0) = 0';
END LOOP;
dbms_output.put_line(l_sql);
EXECUTE IMMEDIATE l_sql
INTO l_result ;
dbms_output.put_line('count of conseuence different rows ' ||l_result);
END;

Stored procedure in T-SQL transform to PostgreSQL

I have a stored procedure writen in T-SQL and I want to make it for PostgreSQL but I'm not so familiar with PostgreSQL.
My stored procedure look like this:
CREATE PROCEDURE usp_insert_allocated_time
#fld_project_id INT,
#fld_allocated_time INT
AS
DECLARE #project int;
SET #project = #fld_project_id;
DECLARE #allocated int;
DECLARE #time int;
BEGIN
SET #time = (SELECT SUM(fld_allocated_time)
FROM dbo.tbl_project_timesheet
WHERE fld_project_id =#project)
SET #allocated = (SELECT fld_allocated_days FROM dbo.tbl_project where fld_id = #project);
IF #allocated > #time
BEGIN
INSERT into dbo.tbl_project_timesheet(fld_project_id,fld_allocated_time)
VALUES(#fld_project_id,#fld_allocated_time);
END
ELSE
PRINT 'Not OK';
END
And I have to do something like this, but on line 10 I get this error:
ERROR: invalid input syntax for integer: "293.00"
SQL state: 22P02
Context: PL/pgSQL function
"SA_PRJ".usp_add_timesheet_record_new(integer,integer,numeric,numeric,character varying,character varying) line 10 at assignment
CREATE OR REPLACE FUNCTION "SA_PRJ".usp_add_timesheet_record_new(p_uid integer, p_project_id integer, p_allocated_time numeric, p_achieved_time numeric, p_task_desc character varying, p_obs character varying)
RETURNS void AS
$BODY$
declare alloc_id integer;
declare project integer;
declare allocated integer;
declare allocated_time integer;
BEGIN
project := p_project_id;
allocated_time := (SELECT SUM(fld_allocated_time)
FROM "SD_PRJ".tbl_project_timesheet
WHERE fld_project_id = project);
allocated := (SELECT fld_allocated_days FROM "SD_PRJ".tbl_project where fld_id = project);
if not "SA_ADM".usp_check_permission(p_uid, 'SA_PRJ', 'usp_add_timesheet_record') then
raise exception 'User ID % no have the permission!', p_uid;
end if;
select fld_id into alloc_id from "SD_PRJ".tbl_project_allocation where fld_emp_id = p_uid and fld_project_id = p_project_id;
BEGIN
IF (allocated > allocated_time) THEN
INSERT INTO "SD_PRJ".tbl_project_timesheet(fld_emp_id, fld_project_id, fld_is_allocated,fld_allocated_time, fld_achieved_time, fld_task_desc, fld_obs)
VALUES (p_uid,p_project_id,coalesce(alloc_id,0), p_allocated_time, p_achieved_time,p_task_desc, p_obs);
ELSE
RAISE NOTICE 'Not OK!!';
END IF;
END;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
It's more complex version in PostgreSQL for what I want.
You don't really give enough information to try and fix your problem, but the error message is pretty descriptive. You are trying to put 293.00 into an integer. Here I can reproduce:
DO
$$
DECLARE
i INT;
BEGIN
i := 293.00;
RAISE NOTICE 'i=%', i;
END
$$;
This will raise:
ERROR: invalid input syntax for integer: "293.00"
SQL state: 22P02
Context: PL/pgSQL function inline_code_block line 6 at assignment
You need to change your variable to the same datatype as the data you are trying to assign to it. For example:
DO
$$
DECLARE
i NUMERIC(5, 2);
BEGIN
i := 293.00;
RAISE NOTICE 'i=%', i;
END
$$;
This works and outputs:
NOTICE: i=293.00
Query returned successfully with no result in 14 ms.

Cursor Not Returned Query if Execute SQL Statement with Return Value

I have an issue when executing query bellow with DBExpress and Delphi XE. I need to get last identity id from executed query :
function TServerDBUtils.ExecuteQueryWithIdentity(ASQLConn: TSQLConnection): Integer;
var
newSQLQuery: TSQLQuery;
begin
Result := -1;
newSQLQuery := TSQLQuery.Create(nil);
try
with newSQLQuery do
begin
SQLConnection := ASQLConn;
SQL.Clear;
SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';
SQL.Add('Select Scope_Identity()');
Open;
Result:= Fields[0].AsInteger;
end;
finally
FreeAndNil(newSQLQuery);
end;
end;
I get error "cursor not returned query". I have used same method before, using FireDac & Delphi XE5 and got no error. No, I'm wondering if "open" is not allowed to do such thing in DBExpress. What method I should use? (We have to use DBExpress in our project)
I've tried this :
function TServerDBUtils.ExecuteQueryWithIdentity(ASQLConn: TSQLConnection): Integer;
var
newSQLQuery: TSQLQuery;
begin
Result := -1;
newSQLQuery := TSQLQuery.Create(nil);
try
with newSQLQuery do
begin
SQLConnection := ASQLConn;
SQL.Clear;
SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';
ExecSQL;
SQL.Clear;
SQL.Add('Select Scope_Identity()');
Open;
Result:= Fields[0].AsInteger;
end;
finally
FreeAndNil(newSQLQuery);
end;
end;
And always got null values, maybe because different session.
Sorry for my bad english, and thanks in advance for any help.
Update :
It works if we used ##identity :
SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')';
ExecSQL;
SQL.Clear;
SQL.Add('Select ##Identity');
Open;
Result:= Fields[0].AsInteger;
But, there's problem as SQLServer told, that if there was a trigger on that table fired (on Insert), The return value is the last ID of table that trigger inserted.
The most elegant way on SQL-Server might be to use the
OUTPUT Clause
which is not only capable to return one ID but all new genered one in case of a multipart insert.
INSERT into aTable (aField)
OUTPUT Inserted.ID
Values ('SomeValue')
If you have got a trigger on your table you will have to define a destination table for your OUTPUT
DECLARE #tmp table (ID int)
INSERT into aTable (aField)
OUTPUT Inserted.ID into #tmp
Values ('SomeValue')
Select * from #tmp
Another advice would be to use parameters instead of hard coded values.
With TSQLQuery adding SET NOCOUNT ON before the statement will prevent the cursor not returned query error an deliver the expected result:
begin
SQLQuery1.SQL.text :='SET NOCOUNT ON'
+#13#10'DECLARE #tmp table (ID int)'
+#13#10'INSERT into aTable (aField)'
+#13#10'OUTPUT Inserted.ID into #tmp'
+#13#10'Values (:P)'
+#13#10'Select * from #tmp';
SQLQuery1.Params.ParamByName('P').Value := 'SomeText';
SQLQuery1.Open;
Showmessage(SQLQuery1.Fields[0].asString);
end;
I use to do this using XE2 and SQL Server
with newSQLQuery do
try
SQLConnection := ASQLConn;
SQL.Clear;
SQL.Add('Insert into SampleTable(uomname) values(' + Quotedstr('bag') +')');
ExecSQL;
SQL.Clear;
SQL.Add('Select Scope_Identity() as id');
Open;
if not Eof then
Result := FieldByName('id').asInteger;
finally
FreeAndNil(newSQLQuery);
end;
So, I introduce the as keyword on the second SQL statement. When I open the Query, just check for the field with name 'id' that was requested.

Resources