Iterate over rows in Stored Procedure BigQuery - stored-procedures

Is there a way to fetch a query result inside a stored procedure and then iterate over the rows in BigQuery? Having something like a cursor with each row.
This is my stored procedure, it takes 6 parameters and I take those parameters from a table. I would like to call the procedure X times (X being the number of rows of my input table). So something like :
FOR device_id, nb_measures, delta_t_min, delta_t_last_rec, date_cr, frame IN (SELECT device_id, nb_measures, delta_t_min, delta_t_last_rec, date_cr, frame FROM my_project.my_dataset.my_table)
BEGIN
DECLARE count INT64 DEFAULT 0;
SET temp_list = [];
WHILE count < nb_measures DO
SET temperature = `bdz-dts-datascience-dev.fonctions.hexStringToInt`(frame, 5 + count, 1, 0, 8);
IF temperature != 127 THEN
IF count = 0 THEN
SET measure_time = TIMESTAMP_SUB(date_cr, INTERVAL delta_t_last_rec MINUTE);
ELSE
SET measure_time = TIMESTAMP_SUB(date_cr, INTERVAL delta_t_last_rec + count * delta_t_min MINUTE);
END IF;
INSERT `20191218_temperature_repeteurs.step_2`(device_id, measure_time, temperature)
VALUES(measure_time, temperature);
END IF;
END WHILE;
END;
Or on the other hand is there a way to execute a stored procedure inside a SELECT query to iterate through the results ?
SELECT
device_id, nb_mesures, delta_t_min, delta_t_last_rec, date_cr, frame
CALL `my-dataset.my_procedure`(device_id, nb_mesures, delta_t_min, delta_t_last_rec, date_cr, frame)
FROM `my_project.my_dataset.my_table`)

Here is an example of how you can iterate over rows using store procedure taken from here
-- The input variable is employee’s employee_id (target_employee_id)
-- The output variable (OUT) is employee_hierarchy which lists
-- the employee_id of the employee’s manager
CREATE PROCEDURE dataset.GetEmployeeHierarchy(
target_employee_id INT64, OUT employee_hierarchy ARRAY<INT64>)
BEGIN
-- Iteratively search for this employee's manager, then the manager's
-- manager, etc. until reaching the CEO, who has no manager.
DECLARE current_employee_id INT64 DEFAULT target_employee_id;
SET employee_hierarchy = [];
WHILE current_employee_id IS NOT NULL DO
-- Add the current ID to the array.
SET employee_hierarchy =
ARRAY_CONCAT(employee_hierarchy, [current_employee_id]);
-- Get the next employee ID by querying the Employees table.
SET current_employee_id = (
SELECT manager_id FROM dataset.Employees
WHERE employee_id = current_employee_id
);
END WHILE;
END;

It's not possible to call a stored procedure within a SELECT. What you can do it's to create a User Defined Function[1], e.g:
CREATE TEMP FUNCTION my_udf(a INT64, b INT64, c INT64, d INT64) AS (....);
Then:
SELECT
device_id, nb_mesures, delta_t_min, delta_t_last_rec, date_cr, frame,
my_udf(device_id, nb_mesures, delta_t_min, delta_t_last_rec, date_cr, frame)
FROM `my_project.my_dataset.my_table`)
[1] https://cloud.google.com/bigquery/docs/reference/standard-sql/user-defined-functions

Related

why does Stored procedure not return the 'Out' parameter in bigquery?

I am writing below Stored Procedure in BigQuery but not sure where did I do wrong.
The 'Out' Parameter does not return anything.
Stored Procedure:
create procedure if not exists test.GetNumTherapistSessions(
in LastNamee string,
out NumSessione int64
)
begin
select count(s.SessionNum) as NumSession from test.Session s
inner join test.Therapist t on s.TherapistID=t.TherapistID
where t.LastName=LastNamee;
end
and Here is how I declare the output parameter and how to call it:
declare NumSession int64;
call test.GetNumTherapistSessions("Risk",NumSession);
Here is the output:
So far everything seems right, but when I select the NumSession, it returns Null:
select NumSession;
Output:
Try SET NumSessione = (select count...);
create procedure if not exists test.GetNumTherapistSessions(
in LastNamee string,
out NumSessione int64
)
begin
SET NumSessione = (
select count(s.SessionNum) as NumSession from test.Session s
inner join test.Therapist t on s.TherapistID=t.TherapistID
where t.LastName=LastNamee
);
end
As shown in this docs of bigquery Link you can use SET to assign any values you need to a specific Variables.
Note that bigquery don't have SELECT INTO statement inside procedure.
Examples:
SET (value1, value2, value3) = (SELECT AS STRUCT c1, c2, c3 FROM table_name WHERE condition)
From this answer:select-into-with-bigquery
Another Example:
UPDATE dataset.Inventory
SET quantity = quantity +
(SELECT quantity FROM dataset.NewArrivals
WHERE Inventory.product = NewArrivals.product),
supply_constrained = false
WHERE product IN (SELECT product FROM dataset.NewArrivals)
Another examples can be found here: Link

DB2 - how to call a stored procedure that returns a result set in another user defined table function

I have a db2 stored procedure that takes in some parameters, gets some data from somewhere and then returns a result set through a cursor.
Now I want to write a table function in db2, that will call this stored procedure, read from the result set and return the data in the result set as a table (eventually I want to use this table function in a join).
I would like to know if this is permitted in db2 (we're using DB2 v10.5), i.e. execute a stored procedure in a table function and fetch and read from the result set from the stored procedure. If so, what is the right syntax for calling the stored procedure and reading the result set inside a table function in db2? Thanks!
Yes, it's possible. See the example below.
--#SET TERMINATOR #
CREATE OR REPLACE PROCEDURE TEST_PROC(P_TABSCHEMA VARCHAR(128))
DYNAMIC RESULT SETS 1
READS SQL DATA
BEGIN
DECLARE C1 CURSOR WITH HOLD WITH RETURN FOR
SELECT TABSCHEMA, TABNAME, COLCOUNT
FROM SYSCAT.TABLES
WHERE TABSCHEMA=P_TABSCHEMA;
OPEN C1;
END#
--CALL TEST_PROC('SYSCAT')#
CREATE OR REPLACE FUNCTION TEST_PROC(P_TABSCHEMA VARCHAR(128))
RETURNS TABLE (
TABSCHEMA VARCHAR(128)
, TABNAME VARCHAR(128)
, COLCOUNT INT
)
READS SQL DATA
BEGIN
DECLARE SQLSTATE CHAR(5);
DECLARE V_TABSCHEMA VARCHAR(128);
DECLARE V_TABNAME VARCHAR(128);
DECLARE V_COLCOUNT INT;
DECLARE V1 RESULT_SET_LOCATOR VARYING;
CALL TEST_PROC(P_TABSCHEMA);
ASSOCIATE RESULT SET LOCATOR (V1) WITH PROCEDURE TEST_PROC;
ALLOCATE C1 CURSOR FOR RESULT SET V1;
L1: LOOP
FETCH C1 INTO V_TABSCHEMA, V_TABNAME, V_COLCOUNT;
IF SQLSTATE<>'00000' THEN LEAVE L1; END IF;
PIPE(V_TABSCHEMA, V_TABNAME, V_COLCOUNT);
END LOOP L1;
CLOSE C1;
RETURN;
END#
SELECT * FROM TABLE(TEST_PROC('SYSCAT'))#
You need to create the DB2 table-function as follows:
CREATE FUNCTION database_schema.function_name ( IN_PARTID VARCHAR(1000) )
RETURNS TABLE ( PARTNO CHAR(25), PARTDS CHAR(30), QUANTITY INT )
BEGIN
RETURN SELECT PARTNO , PARTDS , CASE WHEN QUANTITY > 0 THEN QUANTITY ELSE 0 END QUANTITY
FROM
(
SELECT PARTNO
,MAX(PARTDS) AS PARTDS
,SUM(QUANTITY) AS QUANTITY
FROM database_schema.table_name
WHERE 1=1
AND PARTID = (CAST(IN_PARTID AS INT))
GROUP BY PARTNO
) AA;
END;
Then invoke the table-function as join or straight SQL:
SELECT partno,partds,quantity
FROM TABLE(database_schema.function_name('parameter_1'))

function or stored procedure for bulk insert in table by sets

how can i convert the following to teradata stored procedure or some function :
WHILE EXISTS (SELECT * FROM #temp_set1)
BEGIN
/*group of statemnts :delete insert etc
like */
INSERT INTO #temp_set
SELECT TOP 200 * FROM #temp_set1;
INSERT INTO Activepull
as select * from #temp_set1
and this should occur for each set of 200 untill all inserts
end;
There are more than 60000 rows in #temp_set1 so want to insert by sets.
Thanks.
Written on my phone, but I would use the over expression. It creates a kind of pseudo Identity column that you can then use to paginate. And you can even choose the order based on an order by clause.
Declare #IntervalSize int = 100
Declare #BeginSet int = 0,
#EndSet int = #IntervalSize
While #Counter < (Select Max(ROW_NUMBER() OVER (ORDER BY someColumn desc) From #temp_set1)
Begin
Insert Into #temp_set
Select *, Row_Number() Over (Order By someColumn desc) As MyIdentity
Where MyIdentity Between #BeginSet And #EndSet
Select #BeginSet = #BeginSet + #IntervalSize,
#EndSet = #EndSet + #IntervalSize
End

Handling NULL values while calculating average

I have a stored procedure which calculates the average of several values upon the parameters.
Here's a snippet of the code:
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT
#valFound = AVG(FilterSurface)
FROM
[tblVAR_FilterSurface]
WHERE
refDate >= DATEADD(DAY, -30, #refDate)
AND refDate <= DATEADD(DAY, 0, #refDate)
AND patientId = #patientId
INSERT INTO __TEMP(PatientId, RefDate, ModifyDate, FilterSurface)
VALUES(#patientId, #refDate, #modifyDate, #valFound);
SET #valFound = NULL
FETCH NEXT FROM db_cursor INTO #patientId, #refDate, #modifyDate
END
Sometimes this stored procedure fails as the system try to calculate the average of NULL values and assign the result to #valFound variable. I think the problem is the assignment of result to that variables.
How can I modify my stored procedure in order to handle correctly null values?
Either change the select to #valFound = avg(isnull(FilterSurface, 0)) or change your where clause to include and FilterSurface is not null, depending if you want to count nulls as zero or exclude them

Autoincrement dependent of other field

What is the best way to get an autoincrement "counter" thath depends of other field?.
Imagine this table
CUSTOMER - COUNTER
1 - 1
1 - 2
1 - 3
2 - 1
2 - 2
I need Counter increments 1 every record I add for every customer.
Regards.
Create additional table to hold counters for every customer:
CREATE TABLE customer_counter
(
customer_id INTEGER NOT NULL,
counter INTEGER NOT NULL,
PRIMARY KEY (customer_id)
)
Use following procedure to obtain next counter number for given customer:
CREATE PROCEDURE get_customer_counter (customer_id INTEGER)
RETURNS (counter INTEGER)
AS
BEGIN
SELECT SUM(counter) FROM customer_counter
WHERE customer_id = :customer_id
INTO :counter;
counter = COALESCE(:counter, 0);
EXECUTE STATEMENT
'INSERT INTO customer_counter (customer_id, counter) ' ||
'VALUES (' || :customer_id || ', 1)'
WITH AUTONOMOUS TRANSACTION;
END
See how we use sum and insert delta values instead of updating single record. Thus we prevent deadlocks.
On regular basis we need to clear counter table by merging delta records into one with a total value:
CREATE TRIGGER on_disconnect_database
ACTIVE
ON DISCONNECT
AS
DECLARE VARIABLE sm INTEGER;
DECLARE VARIABLE cnt INTEGER;
DECLARE VARIABLE customer_id INTEGER;
BEGIN
FOR
SELECT customer_id, SUM(counter), COUNT(counter)
FROM customer_counter
GROUP BY customer_id
INTO :customer_id, :sm, :cnt
DO BEGIN
IF (:cnt > 1) THEN
BEGIN
DELETE FROM customer_counter WHERE customer_id = :customer_id;
INSERT INTO customer_counter (customer_id, counter)
VALUES (:customer_id, :sm);
WHEN ANY DO
BEGIN
END
END
END
END
It's hard to really tell what you exactly want of what you have given, but I assume you want to loop through each X field and it's Y sub-field, use it for a counter, or whatever else.
<?php
$x = array("customer1","customer2","customer3");
$y = array("name","address","phone");
$counter;
foreach($x as $eachX):
$counter = 1;
foreach($y as $eachY):
//do stuff here
$counter++;
endforeach;
endforeach;
?>
I think this stored procedure will be ok!
create procedure New_Procedure
returns (
o_customer integer,
o_counter integer)
as
declare variable v_oldcostomer integer;
begin
for select customer
from customers
order by 1
into o_customer
do begin
if (v_oldcostomer is null or (v_oldcostomer is not null and v_oldcostomer <> o_customer) ) then
o_counter = 0;
o_counter = o_counter + 1;
v_oldcostomer = o_customer;
suspend;
end
end

Resources