Control Flow
NuoDB SQL supports the following execution control flow statements for use in SQL stored procedures, user defined functions (UDFs), or triggers.
EXECUTE ...;
CALL ...;
FOR SELECT ...; sql_code_block END_FOR;
FOR INSERT INTO ...; sql_code_block END_FOR;
FOR EXECUTE ...; sql_code_block END_FOR;
BREAK;
CONTINUE;
RETURN [ variable_expression | ( sql_query_statement | dynamic_sql_statement ) ];
THROW message |
variable_expression |
( sql_query_statement | dynamic_sql_statement );
Description
The FOR SELECT statement loop executes the SELECT statement (see SELECT) and then invokes the inner sql_code_block once per each returned row. In the SELECT column_list, each column name must be unique in the scope of the sql_code_block of the FOR SELECT loop. This ensures a set of unique variable names. If a column name is not unique in this scope, then you must assign a unique alias to it by using AS. The variable name for this column will be its assigned alias. See Example 2.
The FOR INSERT statement executes the INSERT statement (see INSERT) and retrieves the list of the IDs the database automatically generated for the inserted rows. Then, it invokes the inner statements once per each ID. Use of the FOR INSERT statement is only appropriate with tables that have a column defined as GENERATED ALWAYS AS IDENTITY. Statements within the FOR INSERT statement loop execute only upon the return of the system generated sequence number.
The FOR EXECUTE statement invokes the specified stored procedure, and then invokes the inner sql_code_block once for each row in the returned table.
The BREAK statement can be used to terminate a loop early, and continues the execution from the statement following the closest END_IF, END_FOR, or END_WHILE.
The CONTINUE statement can be used within a WHILE, FOR SELECT, FOR INSERT, or FOR EXECUTE statement. It will continue with the next iteration through the WHILE or FOR loop, that is, it will transfer program control to the next iteration of the loop without executing any statements after the CONTINUE statement.
The RETURN statement can be used to terminate a stored procedure, UDF, or trigger early, with a success exit code. Statements after the RETURN statement do not execute. For UDFs only, the RETURN statement can optionally return the function RETURNS value as defined by the function definition. The UDF RETURN statement returns a variable, a scalar query, or a query result set, depending on the UDF RETURNS type definition.
The THROW statement can be used to terminate the stored procedure, UDF, or trigger early with a failure exit code. Statements after the THROW statement do not execute. The THROW statement can return a value or expression of either string or numeric data type. The THROW statement can also return the results of a scalar query, which is a SQL SELECT statement that returns a single row. The selected result can be from one column or a concatenation of multiple columns.
Parameters
sql_code_block-
A grouping of statements, such as processing control statements, SQL commands, etc., that execute inside a stored procedure, user-defined function, or trigger.
variable_expression-
A valid expression to be returned by a UDF or thrown by a stored procedure, UDF or trigger.
sql_query_statement-
A string representing a valid SQL query statement to be assigned to a SQL procedure variable. See
SELECTfor syntax of a validSELECTstatement.
TheSELECTstatement select list will be assigned to eachvar_namein the order listed in theSELECTlist. The number ofvar_names to the left of the equation must match the number of items in theSELECTlist. dynamic_sql_statement-
An
EXECUTE IMMEDIATEstatement. See SQL Procedural Dynamic SQL. If theEXECUTE IMMEDIATEstatement is a query returning a result set, then the result set will be assigned to eachvar_namein the order listed in theSELECTlist of the query. The number ofvar_names to the left of the equation must match the number of items in the querySELECTlist. IfEXECUTE IMMEDIATEis used in variable assignment, an error will incur if it also specifiesINTO target_var. message-
A string literal or numeric value that will be displayed when invoked with the
THROWstatement. A string literal is required to be enclosed in single quotes. If themessageitself contains a single quote character then the single quote character needs to be escaped by using another single quote character (see Example 8).
Examples
- Example 1: Using
FOR SELECTwithin a stored procedure -
CREATE TABLE teams_update (year INTEGER, teamid VARCHAR(3), name VARCHAR(48), conferenceid VARCHAR(2), divisionid VARCHAR(2)); SET DELIMITER @ CREATE PROCEDURE prc_copy_year(IN copy_year INTEGER) AS VAR new_year INTEGER; FOR SELECT * FROM hockey.teams WHERE year=copy_year; new_year=year+1; INSERT INTO teams_update VALUES (new_year, teamid, name, conferenceid, divisionid); END_FOR; END_PROCEDURE @ SET DELIMITER ; CALL prc_copy_year(2011); SELECT * FROM teams_update; YEAR TEAMID NAME CONFERENCEID DIVISIONID ----- ------- --------------------- ------------- ----------- 2012 AND Anaheim Ducks WC PC 2012 BOS Boston Bruins EC NE 2012 BUF Buffalo Sabres EC NE 2012 CAL Calgary Flames WC NW 2012 CAR Carolina Hurricanes EC SE .... - Example 2: Using
FOR SELECTwithin a stored procedure with an ambiguous column name -
This example uses the table, teams_update, created in Example 1.
The teams_update table includes a column named teamid. This column is included in the
column_listof theFOR SELECTstatement. By default, this selected column value is assigned a variable name, teamid, which is the same name as the table column name. This variable is passed to the inner sql_code_block and can be referred to by this name.The inner sql_code_block contains an
INSERTstatement with values that are provided by aSELECTstatement from the players_update table. ThisSELECTstatement is only selecting rows where the players_update.teamid is equal to this variable named teamid that has been passed to the innersql_code_blockby theFOR SELECTstatement. For theINSERTstatement to correctly distinguish this variable teamid from the column named teamid in the players_update table, you must assign the teamid an alias name in thecolumn_listof theFOR SELECTstatement. This makes the variable name unique that represents the teams_update.teamid column.CREATE TABLE players_update (playerid VARCHAR(10), year INTEGER, teamid VARCHAR(3), position VARCHAR(4)); SET DELIMITER @ CREATE PROCEDURE prc_copy_players(IN i_prior_year INTEGER, IN i_current_year INTEGER) AS FOR SELECT ht.year,ht.teamid AS HT_TEAM_ID FROM hockey.teams_update ht WHERE ht.year = i_current_year; INSERT INTO players_update SELECT distinct hs.playerid, i_current_year, hs.teamid, hs.position FROM hockey.scoring hs WHERE hs.teamid = HT_TEAM_ID AND hs.year = i_prior_year; END_FOR; END_PROCEDURE @ SET DELIMITER ; CALL prc_copy_players(2011,2012); SELECT * FROM players_update; PLAYERID YEAR TEAMID POSITION --------- ----- ------- --------- beaucfr01 2012 AND D belesma01 2012 AND L bellma01 2012 AND L blakeja01 2012 AND C boninni01 2012 AND C brooksh01 2012 AND D .... - Example 3: Using
FOR INSERT INTOwithin a stored procedure -
DROP PROCEDURE IF EXISTS test; DROP TABLE IF EXISTS city_table; DROP TABLE IF EXISTS country_table; CREATE TABLE country_table ( id INTEGER GENERATED ALWAYS AS IDENTITY, /* MUST be auto-generated */ value STRING); CREATE TABLE city_table ( id INTEGER GENERATED ALWAYS AS IDENTITY, /* Auto-generated is optional */ country_id INTEGER, value STRING); SET DELIMITER @ CREATE PROCEDURE test(_country STRING, _city STRING) AS FOR INSERT INTO country_table (value) VALUES (_country); /* note: all references to field "id" within this FOR INSERT loop refer */ /* to the country_table.id. This INSERT statement below will not execute */ /* if COUNTRY_TABLE.ID is not defined as system generated */ INSERT INTO city_table (country_id, value) VALUES (id, _city); END_FOR; END_PROCEDURE @ SET DELIMITER ; EXECUTE test('Ireland', 'Dublin'); SELECT country.value AS country, city.value AS city FROM country_table AS country, city_table AS city WHERE country.id = city.country_id; COUNTRY CITY -------- ------ Ireland Dublin DROP PROCEDURE IF EXISTS test; DROP TABLE IF EXISTS city_table; DROP TABLE IF EXISTS country_table; DROP TABLE IF EXISTS postcode_table; CREATE TABLE country_table (id INTEGER GENERATED ALWAYS AS IDENTITY, value STRING); /* create sub-table of country_table named city_table */ CREATE TABLE city_table (id INTEGER GENERATED ALWAYS AS IDENTITY, country_id INTEGER, value STRING); /* create sub-table of city_table named postcode_table */ CREATE TABLE postcode_table (id INTEGER GENERATED ALWAYS AS IDENTITY, country_id INTEGER, value STRING); SET DELIMITER @ CREATE PROCEDURE test(_country STRING, _city STRING, _postcode STRING) AS FOR INSERT INTO country_table(value) VALUES (_country); /* note: all references to field "id" within this FOR loop refers to */ /* the country_table.id */ FOR INSERT INTO city_table (country_id, value) VALUES (id, _city); /* note: all references to field "id" within this FOR loop refers */ /* to the city_table.id */ INSERT INTO postcode_table (country_id, value) VALUES (id, _postcode); END_FOR; END_FOR; END_PROCEDURE @ SET DELIMITER ; EXECUTE test('Ireland', 'Dublin', 'D02 XH98'); SELECT country.value AS country, city.value AS city, postcode.value as postcode FROM country_table AS country, city_table AS city, postcode_table as postcode WHERE country.id = city.country_id AND city.id = postcode.country_id; COUNTRY CITY POSTCODE -------- ------ --------- Ireland Dublin D02 XH98 - Example 4: Using
FOR EXECUTEwithin a stored procedure -
DROP PROCEDURE IF EXISTS display_winners; DROP PROCEDURE IF EXISTS get_winning_teams; DROP TABLE IF EXISTS team_wins; CREATE TABLE team_wins (team_name STRING, win_year NUMBER); SET DELIMITER @ CREATE PROCEDURE get_winning_teams RETURNS winning_teams (year INTEGER, name STRING) AS INSERT INTO winning_teams SELECT year, name FROM hockey.teams WHERE playoff = 'F'; END_PROCEDURE @ CREATE PROCEDURE display_winners AS VAR total INTEGER = 0; FOR EXECUTE get_winning_teams; INSERT INTO team_wins VALUES (NAME, YEAR); END_FOR; END_PROCEDURE @ SET DELIMITER ; CALL display_winners; SELECT team_name, count(win_year) as years_won FROM team_wins GROUP BY team_name ORDER BY team_name ; TEAM_NAME YEARS_WON ----------------------- ---------- Boston Bruins 12 Buffalo Sabres 2 Calgary Flames 2 Calgary Tigers 1 Carolina Hurricanes 1 Chicago Black Hawks 6 Chicago Blackhawks 1 ... - Example 5: Using
FOR EXECUTEto process multiple result sets from a procedure. -
CREATE PROCEDURE p2(first_value int) RETURNS TABLE one(field string), TABLE two(field string), TABLE three (field string, num decimal(5,4)) AS RETURN (SELECT value FROM storage WHERE ID = first_value), (SELECT value FROM storage WHERE ID = first_value+1), (SELECT value, id FROM storage WHERE ID = first_value+2); END_PROCEDURE;…
CREATE PROCEDURE p4 RETURNS TABLE total(field string) AS FOR EXECUTE p2(1) IF (CURRENT_RESULT = 1) INSERT INTO total VALUES (field); END_IF; IF (CURRENT_RESULT = 2) INSERT INTO total VALUES (field); END_IF; IF (CURRENT_RESULT = 3) INSERT INTO total VALUES (num); END_IF; END_FOR; END_PROCEDURE; - Example 6: Using
BREAKto exit loop processing within a stored procedure -
DROP PROCEDURE IF EXISTS prc_players_by_year; SET DELIMITER @ CREATE PROCEDURE prc_players_by_year(begyr INTEGER, endyr INTEGER) RETURNS temptab (year INTEGER, player_ct INTEGER) AS IF ( (begyr < 1) OR (endyr > YEAR(current_date)) ) THROW 'Error 10: Invalid Year'; END_IF; WHILE ( begyr <= endyr ) INSERT INTO temptab SELECT birthyear,count(*) FROM players WHERE birthyear=begyr GROUP BY birthyear; begyr=begyr+1; END_WHILE; END_PROCEDURE @ SET DELIMITER ; CALL prc_players_by_year(1970,1973); YEAR PLAYER_CT ----- ---------- 1970 127 1971 125 1972 126 1973 119 - Example 7: Using
CONTINUEto pass control flow to the next iteration of the loop. -
CREATE TABLE player (pid BIGINT GENERATED ALWAYS AS IDENTITY, pname STRING); CREATE TABLE id (pid BIGINT); SET DELIMITER @ CREATE PROCEDURE forins1() AS FOR INSERT INTO player VALUES (DEFAULT, 'Messi'), (DEFAULT, 'Ronaldo'), (DEFAULT, 'Bale'), (DEFAULT, 'Neymar'), (DEFAULT, 'Aguero'), (DEFAULT, 'Saurez'); IF (pid % 2 = 0) CONTINUE; END_IF; INSERT INTO id VALUES ( pid ); END_FOR ; END_PROCEDURE; @ SET DELIMITER ; CALL forins1(); SELECT pid, pname FROM player; PID PNAME ---- ------- 1 Messi 2 Ronaldo 3 Bale 4 Neymar 5 Aguero 6 Saurez SELECT pid FROM id; PID ---- 1 3 5 - Example 8: Using
RETURNto return from a stored procedure -
This example creates a string by concatenating a character by a specified number of times, but avoids exceeding the capacity of the target string.
DROP PROCEDURE IF EXISTS test2; DROP PROCEDURE IF EXISTS test; SET DELIMITER @ CREATE PROCEDURE test(length INTEGER, ch CHAR(1), OUT str CHAR(16)) AS VAR idx INTEGER = 0; str = ''; WHILE (idx < length) str = str || ch; IF (CHAR_LENGTH(str) = 16) RETURN; END_IF; idx = idx + 1; END_WHILE; END_PROCEDURE @ CREATE PROCEDURE test2(length INTEGER, ch CHAR(1)) AS VAR str CHAR(16) = ''; CALL test(length, ch, str); THROW str; END_PROCEDURE @ SET DELIMITER ; CALL test2(3, 'x'); Procedure HOCKEY.TEST2, xxx CALL test2(16, 'x'); Procedure HOCKEY.TEST2, xxxxxxxxxxxxxxxx CALL test2(17, 'x'); Procedure HOCKEY.TEST2, xxxxxxxxxxxxxxxx CALL test2(100, 'x'); Procedure HOCKEY.TEST2, xxxxxxxxxxxxxxxx - Example 9: Using
THROWto display a message and exit a stored procedure -
DROP PROCEDURE IF EXISTS prc_hockey_maint; DROP TABLE IF EXISTS tmp_hockey; CREATE TABLE tmp_hockey (number INTEGER, name STRING, position STRING, team STRING); /* THROW a message */ SET DELIMITER @ CREATE PROCEDURE prc_hockey_maint (num INTEGER, nam STRING, pos STRING, team STRING) AS IF ( pos NOT IN ('Forward','Defense','Goalie','Fan') ) THROW 'Error 101: Invalid Position'; END_IF; INSERT INTO tmp_hockey (number,name,position,team) VALUES (num, nam, pos, team); END_PROCEDURE @ SET DELIMITER ; CALL prc_hockey_maint(77,'TOM JONES','Backup','Bruins'); Procedure HOCKEY.PRC_HOCKEY_MAINT, Error 101: Invalid Position CALL prc_hockey_maint(77,'TOM JONES','Goalie','Bruins'); SELECT * FROM tmp_hockey WHERE number = 77; NUMBER NAME POSITION TEAM ------- --------- --------- ------ 77 TOM JONES Goalie Bruins /* THROW query result set - one column, one row */ DROP PROCEDURE IF EXISTS prc_throw_query; SET DELIMITER @ CREATE PROCEDURE prc_throw_query (IN inumber INTEGER) AS THROW ( SELECT NAME||' plays '||position||' for '||TEAM FROM hockey WHERE number = inumber ); END_PROCEDURE; @ SET DELIMITER ; EXECUTE prc_throw_query(40); Procedure HOCKEY.PRC_THROW_QUERY, TUUKKA RASK plays Goalie for Bruins /* THROW numeric value */ DROP PROCEDURE IF EXISTS prc_throw_msg; SET DELIMITER @ CREATE PROCEDURE prc_throw_msg (IN i_error BOOLEAN) AS IF (i_error) THROW 5001; END_IF; END_PROCEDURE; @ SET DELIMITER ; EXECUTE prc_throw_msg(0); EXECUTE prc_throw_msg(1); Procedure HOCKEY.PRC_THROW_MSG, 5001 /* THROW text message with single quote */ DROP PROCEDURE IF EXISTS prc_throw_msg; SET DELIMITER @ CREATE PROCEDURE prc_throw_msg (IN inumber INTEGER) AS THROW ( SELECT number||' was '||name||'''s NUMBER WHEN PLAYING FOR '||team FROM hockey WHERE number = inumber ); END_PROCEDURE; @ SET DELIMITER ; EXECUTE prc_throw_msg(40); Procedure HOCKEY.PRC_THROW_MSG, 40 was TUUKKA RASK's NUMBER WHEN PLAYING FOR Bruins - Example 10: Invoking another stored procedure. Also using
CASEstatement within a stored procedure -
It is possible to invoke another procedure, using either the
CALLorEXECUTEstatement, from within a stored procedure. Also, an example of aCASEstatement within a stored procedure.SET DELIMITER @ CREATE PROCEDURE get_stats(IN in_stat STRING, IN in_year INTEGER, IN in_teamid STRING, OUT out_stat1 STRING, OUT out_stat2 STRING, OUT out_stat3 STRING) AS out_stat1,out_stat2,out_stat3 = CASE in_stat WHEN 'Conference' THEN (SELECT conferenceid, divisionid, rank FROM hockey.teams WHERE year = in_year AND teamid = in_teamid) WHEN 'Record' THEN (SELECT wins, losses, ties FROM hockey.teams WHERE year = in_year AND teamid = in_teamid) END; END_PROCEDURE @ CREATE PROCEDURE get_team_stats(IN in_stat STRING, IN in_year INTEGER, IN in_teamid STRING) AS VAR out1 STRING, out2 STRING, out3 STRING; EXECUTE get_stats(in_stat,in_year,in_teamid,out1,out2,out3); VAR display_out STRING; display_out=CASE in_stat WHEN 'Conference' THEN 'Conference='||out1||' Division='||out2||' Rank='||out3 WHEN 'Record' THEN 'Wins='||out1||' Loses='||out2||' Ties='||out3 END; THROW display_out; END_PROCEDURE @ SET DELIMITER ; EXECUTE get_team_stats('Conference',2011,'BOS'); Procedure HOCKEY.GET_TEAM_STATS, Conference=EC Division=NE Rank=1 EXECUTE get_team_stats('Record',2011,'BOS'); Procedure HOCKEY.GET_TEAM_STATS, Wins=49 Loses=29 Ties=0