- Informix name qualification
- Informix transaction management statements
- Informix using CONNECT, COMMIT, DISCONNECT, and ROLLBACK
- Informix performance and locking
- Informix non-cursor statements
- Informix DELETE, INSERT, and UPDATE
- Informix SELECT
- Informix cursor statements
- Informix retrieval using cursors
- Informix nonupdatable cursors
- Informix updatable cursors
- Informix FETCH statements
- Informix FETCH NEXT
- Informix FETCH FIRST, FETCH PRIOR, and FETCH LAST
- Informix CLOSE for cursors
- Informix database stored procedures
- Informix retrieval using database stored procedures
- Informix DECLARE and EXECUTE
- Informix FETCH
- Informix CLOSE
- Informix update using database stored procedures
- Informix using database stored procedures in DataWindow objects
- Informix database stored procedure summary
About this chapter
When you create scripts for a PowerBuilder application, you can use embedded SQL statements in the script to perform operations on the database. The features supported when you use embedded SQL depend on the DBMS to which your application connects.
Overview
When you use the Informix IN9 database interface to connect to a database, you can use embedded SQL in your scripts. You can embed the following types of SQL statements in scripts and user-defined functions:
-
Transaction management statements
-
Non-cursor statements
-
Cursor statements
-
Database stored procedures
Informix API
The Informix database interfaces use the Informix application programming interface (API) to interact with the database.
When you use embedded SQL, PowerBuilder makes the required calls to the API. Therefore, you do not need to know anything about the Informix API in order to use embedded SQL in PowerBuilder.
See also
Informix transaction management statements
Informix non-cursor statements
Informix database stored procedures
Since PowerBuilder does not inspect all SQL statement syntax, you can qualify Informix catalog entities as necessary.
For example, these qualifications are all acceptable:
-
emp_name
-
employee.emp_name
-
Informix.employee.emp_name
Functions
You can use any function that Informix supports (such as aggregate or mathematical functions) in SQL statements.
This example illustrates how to call the Informix function HEX in a SELECT statement:
SELECT HEX(emp_num) INTO :emp_name_var FROM employee;DBMS=ODB
Qualification
You can use the following transaction management statements with transaction objects to manage connections and transactions for Informix databases:
For example, these qualifications are all acceptable:
-
CONNECT
-
COMMIT
-
DISCONNECT
-
ROLLBACK
See also
This table lists each transaction management statement and describes how it works when you use the Informix IN9 interface to connect to a database:
Statement |
Description |
---|---|
CONNECT |
Establishes the database connection. After you assign values to the required properties of the transaction object, you can execute a CONNECT. After this call completes successfully, PowerBuilder issues a BEGIN WORK to start a logical unit of work for the transaction. |
COMMIT |
Terminates the logical unit of work, guarantees that all changes made to the database since the beginning of the current unit of work become permanent, and starts a new logical unit of work. |
DISCONNECT |
Terminates a successful connection. DISCONNECT automatically executes a COMMIT to guarantee that all changes made to the database since the beginning of the current unit of work are committed. |
ROLLBACK |
Terminates a logical unit of work, undoes all changes made to the database since the beginning of the logical unit of work, and starts a new logical unit of work. |
See also
An important consideration when designing a database application is deciding when connect and commit statements should occur to maximize performance and limit locking and resource use. A connect takes a certain amount of time and can tie up resources during the life of the connection. If this time is significant, then limiting the number of connects is desirable.
After a connection is established, SQL statements can cause locks to be placed on database entities. The more locks there are in place at a given moment in time, the more likely it is that the locks will hold up another transaction.
Rules
No set of rules for designing a database application is totally comprehensive. However, when you design a PowerBuilder application, you should do the following:
-
Long-running connections
Determine whether you can afford to have long-running connections. If not, your application should connect to the database only when absolutely necessary. After all the work for that connection is complete, the transaction should be disconnected.
If long-running connections are acceptable, then commits should be issued as often as possible to guarantee that all changes do in fact occur. More importantly, COMMITs should be issued to release any locks that may have been placed on database entities as a result of the statements executed using the connection.
-
SetTrans or SetTransObject function
SetTrans or SetTransObject function Determine whether you want to use default DataWindow transaction processing (the SetTrans function) or control the transaction in a script (the SetTransObject function).
If you cannot afford to have long-running connections and therefore have many short-lived transactions, use the default DataWindow transaction processing. If you want to keep connections open and issue periodic COMMITs, use the SetTransObject function and control the transaction yourself.
Isolation feature
Informix-OnLine databases use the isolation feature to support assorted database lock options. In PowerBuilder, you can use the Lock property of the transaction object to set the isolation level when you connect to the database.
The following example shows how to set the Lock property to Committed read:
// Set the lock property to committed read // in the default transaction object SQLCA. SQLCA.Lock = "Committed read"
Informix-SE databases do not support Lock
The Lock property applies only to Informix-OnLine databases. Informix-SE (Standard Edition) databases do not support the use of lock values and isolation levels.
Example 1
This script uses embedded SQL to connect to a database and insert a row in the ORDER_HEADER table and a row in the ORDER_ITEM table. Depending on the success of the statements in the script, the script executes a COMMIT or ROLLBACK:
// Set the SQLCA connection properties. SQLCA.DBMS = "IN9" SQLCA.database = "ORDERS"// Connect to the database. CONNECT USING SQLCA; // Insert a row into the ORDER_HEADER table. // A ROLLBACK is required only if the first row // was inserted successfully. INSERT INTO ORDER_HEADER (ORDER_ID,CUSTOMER_ID) VALUES ( 7891, 129 ); // Test return code for ORDER_HEADER insertion if SQLCA.sqlcode = 0 then // Since the ORDER_HEADER is inserted, // try to insert ORDER_ITEM INSERT INTO ORDER_ITEM (ORDER_ID, ITEM_NBR, PART_NBR, QTY) VALUES ( 7891, 1, '991PLS', 456 ); // Test return code for ORDER_ITEM insertion. if SQLCA.sqlcode = -1 then // If insert failed // ROLLBACK insertion of ORDER_HEADER. ROLLBACK USING SQLCA; end if end if // Disconnect from the database. DISCONNECT USING SQLCA;
Error checking
Although you should test the SQLCode after every SQL statement, these examples show statements to test the SQLCode only to illustrate a specific point.
Example 2
This example uses the scripts for the Open and Close events in a window and the Clicked event in a CommandButton to illustrate how you can manage transactions in a DataWindow control. Assume the window contains a DataWindow control dw_1 and the user enters data in dw_1 and then clicks the Cb_Update button to send the data to the database.
Since this script uses SetTransObject to connect to the database, the programmer is responsible for managing the transaction.
Window Open event script
// Set the transaction object properties // and connect to the database. // Set the SQLCA connection properties. SQLCA.DBMS = "IN9" SQLCA.database = "ORDERS" // Connect to the database. CONNECT USING SQLCA; // Tell the DataWindow which transaction object // to use. SetTransObject( dw_1, SQLCA )
CommandButton Clicked event script
// Declare ReturnValue an integer. // integer ReturnValue ReturnValue = Update( dw_1 ) // Test to see if updates were successful. if ReturnValue = -1 then // Updates were not successful. Since we used // SetTransObject, rollback any changes made // to the database. ROLLBACK USING SQLCA; else // Updates were successful. Since we used // SetTransObject, commit any changes made // to the database. COMMIT USING SQLCA; end if
Window Close event script
// Disconnect from the database. DISCONNECT USING SQLCA;
The statements that do not involve cursors or stored procedures are:
-
DELETE
-
INSERT
-
UPDATE
-
SELECT (singleton)
See also
Internally, PowerBuilder processes DELETE, INSERT, and UPDATE statements the same way. PowerBuilder inspects them for any PowerScript variable references and replaces all such references with a constant that conforms to Informix rules for that data type.
Row serial number
The serial number of the row is stored in the SQLReturnData property of the transaction object after an INSERT statement executes. (The SQLReturnData property is updated after embedded SQL only; it is not updated after a DataWindow operation.)
Example
Assume you enter the following statement:
DELETE FROM employee WHERE emp_id = :emp_id_var;
In this example, emp_id_var is a PowerScript variable with the data type of integer that has been defined within the scope of the script that contains the DELETE statement. Before the DELETE statement is executed, emp_id_var is assigned a value (for example, 691) so that when the DELETE statement executes, the database receives the following statement:
DELETE FROM employee WHERE emp_id = 691;
When is this substitution technique used?
This variable substitution technique is used for all PowerScript variable types. When you use embedded SQL, precede all PowerScript variables with a colon ( : ).
See also
The SELECT statement contains input variables and output variables. Input variables are passed to the database as part of the execution and the substitution as described above for DELETE, INSERT, and UPDATE. Output variables are used to return values based on the result of the SELECT statement.
Example 1
Assume you enter the following statement:
SELECT emp_name, emp_salary INTO :emp_name_var, :emp_salary_var FROM employee WHERE emp_id = :emp_id_var;
Here emp_id_var, emp_salary_var, and emp_name_var are PowerScript variables defined within the scope of the script that contains the SELECT statement, emp_id_var is processed as described in the DELETE example above.
Both emp_name_var and emp_salary_var are output variables that will be used to return values from the database. The data types of emp_name_var and emp_salary_var should be the PowerScript data types that best match the Informix data type. When the data types do not match perfectly, PowerBuilder converts them.
How big should numeric output variables be?
For numeric data, the output variable must be large enough to hold any value that may come from the database.
Assume the value for emp_id_var is 691 as in the previous example. When the SELECT statement executes, the database receives the following statement:
SELECT emp_name, emp_salary FROM employee WHERE emp_id = 691;
If no errors are returned on the execution, data locations are internally bound for the result fields. The data returned into these locations is converted if necessary and the appropriate PowerScript variables are set to those values.
Example 2
This example assumes the default transaction object (SQLCA) has been assigned valid values and a successful CONNECT has executed. It also assumes the data type of the emp_id column in the employee table is CHARACTER[10].
The user enters an employee ID into the line edit sle_Emp and clicks the button Cb_Delete to delete the employee.
The script for the Clicked event in the CommandButton Cb_Delete is:
// Make sure we have a value. if sle_Emp.text <> "" then // Since we have a value, let's try to delete it. DELETE FROM employee WHERE emp_id = :sle_Emp.text; // Test to see if the DELETE worked. if SQLCA.sqlcode = 0 then // It seems to have worked, let user know. MessageBox( "Delete",& "The delete has been successfully processed!") else // It didn't work. MessageBox( "Error", & "The delete failed. Employee ID is not valid.") end if else // No input value. Prompt user. MessageBox( "Error", & "An employee ID is required for "+"delete!" ) end if
Error checking
Although you should test the SQLCode after every SQL statement, these examples show statements to test the SQLCode only to illustrate a specific point.
Example 3
This example assumes the default transaction object (SQLCA) has been assigned valid values and a successful CONNECT has executed. The user wants to extract rows from the employee table and insert them into the table named extract_employees.
The extraction occurs when the user clicks the button Cb_Extract. The boolean variable YoungWorkers is set to TRUE or FALSE elsewhere in the application.
The script for the Clicked event for the CommandButton Cb_Extract is:
integer EmployeeAgeLowerLimit integer EmployeeAgeUpperLimit // Do they have young workers? if ( YoungWorkers = TRUE ) then // Yes - set the age limit in the YOUNG range. // Assume no employee is under legal working age. EmployeeAgeLowerLimit = 16 // Pick an upper limit. EmployeeAgeUpperLimit = 42 else // No - set the age limit in the OLDER range. EmployeeAgeLowerLimit = 43 // Pick an upper limit that includes all // employees. EmployeeAgeUpperLimit = 200 end if INSERT INTO extract_employees(emp_id,emp_name) SELECT emp_id, emp_name FROM employee WHERE emp_age >= :EmployeeAgeLowerLimit AND emp_age <= :EmployeeAgeUpperLimit;
In embedded SQL, statements that retrieve data and statements that update data can both involve cursors.
Retrieval statements
The retrieval statements that involve cursors are:
-
DECLARE cursor_name CURSOR FOR . . .
-
OPEN cursor_name
-
FETCH cursor_name INTO . . .
-
CLOSE cursor_name
Update statements
The update statements that involve cursors are:
-
UPDATE . . . WHERE CURRENT OF cursor_name
-
DELETE . . . WHERE CURRENT OF cursor_name
See also
Retrieval using cursors is conceptually similar to the singleton SELECT discussed earlier. The main difference is that there can be multiple rows in a result set when you use a cursor and you control when the next row is fetched into PowerScript variables.
If you expect only a single row to exist in the employee table with the specified emp_id, use the singleton SELECT. In a singleton SELECT, you specify the SELECT statement and destination variables in one concise SQL statement:
SELECT emp_name, emp_salary INTO :emp_name_var, :emp_salary_var FROM employee WHERE emp_id = :emp_id_var;
However, when a SELECT may return multiple rows, you must:
-
Declare a cursor.
-
Open it (which effectively executes the SELECT).
-
Fetch rows as needed.
-
Close the cursor.
Declaring and opening a cursor
Declaring a cursor is tightly coupled with the OPEN statement. The DECLARE specifies the SELECT statement to be executed, and the OPEN actually executes it.
Scroll cursors
When you fetch rows in an Informix database table, using a scroll cursor allows you to fetch rows in the active set in any sequence. That is, you can fetch the next row, previous row, last row, or first row.
To specify that you want to use a scroll cursor when connecting to an Informix database, set the Scroll DBParm parameter to 1. By default, PowerBuilder does not use scroll cursors in an Informix connection (the Scroll parameter is set to 0).
You cannot update scroll cursors
Scroll cursors are not updatable. If you try to declare a scroll cursor and make it updatable, it will fail.
See also
Declaring a cursor is similar to declaring a variable; a cursor is a nonexecutable statement just like a variable declaration. The first step in declaring a nonupdatable cursor is to define how the result set looks. To do this, you need a SELECT statement. You must associate the result set with a logical name so you can refer to it in subsequent SQL statements.
Example
Assume the SingleLineEdit control sle_1 contains the state code for the retrieval:
The script for the Clicked event for the CommandButton Cb_Extract is:
// Declare cursor emp_curs for employee table. // retrieval DECLARE emp_curs CURSOR FOR SELECT emp_id, emp_name FROM Employee WHERE emp_state = :sle_1.text; // Declare local variables for retrieval. string emp_id_var string emp_name_var // Execute the SELECT statement with // the current value of sle_1.text. OPEN emp_curs; // At this point, if there are no errors, // the cursor is available for further processing.
To declare an updatable cursor, use the FOR UPDATE keywords in the declaration.
Example
This statement uses the FOR UPDATE syntax to declare an updatable cursor:
DECLARE emp_curs CURSOR FOR SELECT emp_id, emp_name FROM Employee WHERE emp_state = :sle_1.text FOR UPDATE;
Qualification
The Informix database interfaces support the following FETCH statements:
-
FETCH NEXT
-
FETCH FIRST
-
FETCH PRIOR
-
FETCH LAST
See also
In the singleton SELECT, you specify variables to hold the values for the columns within the selected row. The FETCH statement syntax is similar to the syntax of the singleton SELECT. Values are returned INTO a specified list of variables.
// Go get the first row from the result set FETCH emp_curs INTO :emp_id_var, :emp_name_var;
If at least one row can be retrieved, this FETCH places the values of the emp_id and emp_name columns from the first row in the result set into the PowerScript variables emp_id_var and emp_name_var. Executing another FETCH statement will place the variables from the next row into specified variables.
FETCH statements typically occur in a loop that processes several rows from a result set (one row at a time); fetch the row, process the variables, and then fetch the next row.
What happens when the result set is exhausted?
When a result set has been exhausted, FETCH returns +100 (not found) in the SQLCode property within the referenced transaction object. This is an informational return code; -1 in SQLCode indicates an error.
See also
In addition to the conventional FETCH NEXT, the Informix interface supports FETCH FIRST, FETCH PRIOR, and FETCH LAST statements.
What if you only enter FETCH?
If you only enter FETCH, PowerBuilder assumes FETCH NEXT.
Example
This cursor example illustrates how you can loop through a result set. Assume the default transaction object (SQLCA) has been assigned valid values and a successful CONNECT has been executed.
The statements retrieve rows from the employee table and then display a message box with the employee name in each row that is found.
// Declare the emp_curs DECLARE emp_curs CURSOR FOR SELECT emp_name FROM EMPLOYEE WHERE emp_state = :sle_1.text; // Declare a destination variable for employee // names. string emp_name_var // Get current value of sle_1.text. OPEN emp_curs; // Fetch the first row from the result set. FETCH emp_curs INTO :emp_name_var; // Loop through result set until exhausted DO WHILE SQLCA.sqlcode = 0 // Pop up a message box with the employee name MessageBox("Found an employee!",emp_name_var) // Fetch the next row from the result set FETCH emp_curs INTO :emp_name_var; LOOP // All done, so close the cursor CLOSE emp_curs;
Error checking
Although you should test the SQLCode after every SQL statement, these examples show statements to test the SQLCode only to illustrate a specific point.
See also
The CLOSE statement terminates processing for the specified cursor. CLOSE releases resources associated with the cursor, and subsequent references to that cursor are allowed only if another OPEN is executed. Although you can have multiple cursors open at the same time, you should close the cursors as soon as possible for efficiency reasons.
Qualification
One of the most significant features of Informix is support for database stored procedures. You can use database stored procedures for:
-
Retrieval only
-
Update only
-
Update and retrieval
PowerBuilder supports all of these uses in embedded SQL.
See also
Informix retrieval using database stored procedures
Informix update using database stored procedures
Qualification
PowerBuilder uses a construct that is very similar to cursors to support retrieval using database stored procedures. In PowerBuilder embedded SQL, there are four commands that involve database stored procedures:
-
DECLARE procedure_name PROCEDURE FOR . . .
-
EXECUTE PROCEDURE procedure_name
-
FETCH procedure_name INTO . . .
-
CLOSE procedure_nameRetrieval only
See also
PowerBuilder requires a declarative statement to identify the database stored procedure that is being used and specify a logical name. The logical name is used to reference the procedure in subsequent SQL statements. The general syntax for declaring a procedure is:
DECLARE logical_procedure_name PROCEDURE FOR Informix_procedure_name ({:arg1,:arg2 , ...}) {USING transaction_object};
where logical_procedure_name can be any valid PowerScript identifier and Informix_procedure_name is the name of the stored procedure in the Informix database. The parentheses after Informix_procedure_name are required even if the procedure has no parameters.
Creating a stored procedure
The default SQL terminator character for the Database painter is a semicolon (;). Informix also uses a semicolon in its stored procedure syntax. Therefore, to create a stored procedure in the Database painter, you must change the SQL terminator character to something other than a semicolon, such as a backquote (`).
To change the Database painter's SQL terminator character, type the character you want in the SQL Terminator Character box in the Database Preferences dialog box.
The parameter references can take the form of any valid parameter string that Informix accepts. PowerBuilder does not inspect the parameter list format except for purposes of variable substitution. The USING clause is required only if you are using a transaction object other than the default transaction object (SQLCA).
Example
Assume a stored procedure proc1 is defined as:
CREATE PROCEDURE proc1 AS SELECT emp_name FROM employee
To declare that procedure for processing within PowerBuilder, enter:
DECLARE emp_proc PROCEDURE FOR proc1;
Note that this declaration is a nonexecutable statement, just like a cursor declaration. Where cursors have an OPEN statement, procedures have an EXECUTE statement.
When an EXECUTE statement is executed, the procedure is invoked. The EXECUTE refers to the logical procedure name:
EXECUTE emp_proc;
Error checking
Although you should test the SQLCode after every SQL statement, these examples show statements to test the SQLCode only to illustrate a specific point.
Issuing EXECUTE statements
Use PowerBuilder embedded SQL syntax when you enter an embedded EXECUTE statement in a script; do not enter the PROCEDURE keyword. Use this syntax:
EXECUTE procedure_name;
Specify the EXECUTE statement the same way whether or not a stored procedure takes arguments. The arguments used in the DECLARE statement get passed automatically, without your having to state them in the EXECUTE statement.
To access rows returned in a result set, you use the FETCH statement the same way you use it for cursors. The FETCH statement can be executed after any EXECUTE statement that refers to a procedure that returns a result set.
Informix syntax
PowerBuilder supports Informix syntax; however, the default syntax displayed in the DataWindow painter is the most general syntax. You can leave the syntax unchanged or edit the displayed syntax to conform to the Informix syntax rules. If you do not change the syntax, PowerBuilder converts it to Informix syntax before passing it to the Informix database.
Example 1
FETCH emp_proc INTO :emp_name_var;
You can use this FETCH statement only to access values produced with a SELECT statement in a database stored procedure. You cannot use the FETCH statement to access computed rows.
The result sets that will be returned when a database stored procedure executes cannot be determined at compile time. Therefore, you must code FETCH statements that exactly match the format of a result set returned by the stored procedure when it executes.
Example 2
Assume you changed the second fetch statement in the preceding statement to:
FETCH emp_proc2 INTO :part_var1,:part_var2,:part_var3;
The code would compile without errors, but an execution error would occur because the number of columns in the FETCH statement does not match the number of columns in the current result set. The second result set returns values from only one column.
If a database stored procedure returns a result set, it must be closed when processing is complete.
Do you have to retrieve all the rows?
You do not have to retrieve all rows in a result set to close a request or procedure.
Closing a procedure looks the same as closing a cursor:
CLOSE emp_proc;
As with cursors, if a procedure executes successfully and returns at least one result set and is not closed, a result set is pending and no SQL commands other than the FETCH can be executed. Procedures with result sets should be closed as soon as possible.
The procedure remains open until you close it, execute a COMMIT or a ROLLBACK, or end the database connection.
Database stored procedures that only perform updates and do not return a result set are handled in much the same way as procedures that return a result set. The only difference is that after the EXECUTE procedure_name statement executes, no result set is pending and no CLOSE statement is required.
Using the SQLCode property
If you know that a particular procedure can never return a result set, only the EXECUTE statement is required. If there is a procedure that may or may not return a result set, you can test the SQLCode property of the referenced transaction object for +100 (the code for not found) after the EXECUTE.
This table shows all possible values for SQLCode after an EXECUTE:
Return code |
Means |
---|---|
0 |
The EXECUTE PROCEDURE was successful and a result set is pending. Regardless of the number of FETCH statements executed, the procedure must be explicitly closed with a CLOSE statement. This code is returned even if the result set is empty. |
+100 |
Fetched row not found |
-1 |
The EXECUTE was not successful and no result set was returned. |
Example 1
This example illustrates how to execute a database stored procedure that does not return a result set:
// good_employee is an Informix stored procedure. // Declare the procedure. DECLARE good_emp_p 1roc PROCEDURE FOR good_employee; EXECUTE good_emp_proc; // Test return code. Allow for +100 since you do // not expect a result set. if SQLCA.sqlcode = -1 then // Issue error message since it failed. MessageBox("Stored Procedure Error!", & SQLCA.sqlerrtext) end if
Example 2
This example illustrates how to pass parameters to a database stored procedure that returns a result set. Emp_id_var has been set elsewhere to 691:
// Get_employee is an Informix stored procedure. // Declare the procedure. DECLARE get_emp_proc PROCEDURE FOR get_employee @emp_id_parm = :emp_id_var; // Declare a destination variable for emp_name string emp_name_var // Execute the stored procedure using the // current value for emp_id_var. EXECUTE get_emp_proc; // Test return code to see if it worked. if SQLCA.sqlcode = 0 then // We got a row, so fetch it and display it. FETCH get_emp_proc INTO :emp_name_var; // Display the employee name. MessageBox("Got my employee!",emp_name_var) // You are all done, close the procedure. CLOSE Get_emp_proc; end if
You can use database stored procedures as a data source for DataWindow objects. The following considerations apply:
-
Result set definition
You must define what the result set looks like. The DataWindow object cannot determine this information from the stored procedure definition in the database.
-
DataWindow updates
You cannot perform DataWindow updates through stored procedures (that is, you cannot update the database with changes made in the DataWindow object); only retrieval is allowed. (However, the DataWindow can have update characteristics set manually through the DataWindow painter.)
-
Result set processing
You can specify only one result set to be processed when you define the stored procedure result set in the DataWindow painter.
-
Computed rows
Computed rows cannot be processed in DataWindows.
-
Informix syntax
PowerBuilder supports Informix syntax; however, the syntax displayed in the DataWindow painter is the most general syntax. You can leave the syntax unchanged or edit the displayed syntax to conform to the Informix syntax rules. If you do not change the syntax, PowerBuilder converts it to Informix syntax before passing it to the Informix database.
When you use database stored procedures in a PowerBuilder application, keep the following points in mind:
-
Manipulating stored procedures
To manipulate database stored procedures, PowerBuilder provides SQL statements that are similar to cursor statements.
-
Retrieval and update
PowerBuilder supports retrieval, update, or a combination of retrieval and update in database stored procedures, including procedures that do not return a result set and those that return a result set.
-
Transactions and procedures without result sets
When a procedure executes using a particular connection (transaction) and the procedure does not return a result set, the procedure is no longer active. No result set is pending and, therefore, you do not execute a CLOSE statement.