Stored procedure return value Db2 - db2

CREATE PROCEDURE A()
LANGUAGE SQL
RESULT SETS 1
BEGIN
DECLARE C1 CURSOR WITH RETURN FOR
SELECT id, name, dept, job FROM staff;
OPEN C1;
END
CREATE PROCEDURE B()
LANGUAGE SQL
BEGIN
Call a ;
END
In programming, they don't use stored procedure for returning value (they use function). Why does DB2 support that way?
In my above case, how does the stored procedure B know c1 cursor from stored procedure A? If I declare a cursor (c2 cursor) in stored procedure B and send to stored procedure A. Stored procedure A has a parameter (OUT c2 cursor), so why must I write 'RESULT SETS 1' (Can't I omit that phrase), because I don't return any cursor from stored procedure A (I return by parameter).

You might benefit from some education and training about SQL PL.
If you prefer a paper book, get "Db2 SQL Procedural Language for Linux, Unix, and Windows" by Paul Yip, Drew Bradstock and others. ISBN 0-13-100772-6.
Stackoverflow is not a substitute for training and education.
You should ask separate questions for different topics.
Research Db2 'table functions' to learn how to return a table from a function.
Research Db2 'strongly typed cursors', and Db2 'weakly typed cursors' and pipelined functions, to learn how to exploit cursor parameters in routines. Understand the many restrictions and rules associated with these things, which cannot be conveyed by one example in one question. In particular, realise that SQL PL cursor parameters can only be fully manipulated by SQL PL and cannot currently be passed to other languages for processing (except jdbc which has support for consuming such cursors). So if your front end client is using C, C++, Python, Javascript, .Net, PHP etc, then you won't use SQL PL cursor parameters currently. You might use them inside Db2 SQL PL code however, depending on your skills and needs.
To consume a result set from a nested stored procedure, you need extra syntax. When returning a result-set from a stored-procedure the dynamic result sets clause is required when defining the procedure. Returning a result set in this manner is understood by many different programming languages and frameworks, and by other RDBMS tools, so is the most general purpose method for returning data from an RDBMS to a 3rd Generation programming language. Other mechanisms can be built based on this technique.
All of this syntax is documented in the Db2 Knowledge Centre for your version and platform. One example cannot convey all possible syntax.
Here is an example, you will find many others online if you are competent with search.
--#SET TERMINATOR #
CREATE or replace PROCEDURE procA()
LANGUAGE SQL
RESULT SETS 1
BEGIN
DECLARE C1 CURSOR WITH RETURN TO CALLER FOR
SELECT id, name, dept, job FROM staff;
OPEN C1;
END
#
set serveroutput on#
CREATE or replace PROCEDURE B()
LANGUAGE SQL
BEGIN
declare sqlstate char(5) default '00000';
declare v_rs result_set_locator varying;
declare v_id smallint;
declare v_dept smallint;
declare v_name varchar(9);
declare v_job char(5);
Call procA() ;
associate result set locator (v_rs) with procedure procA;
allocate v_rscur cursor for result set v_rs;
fetch from v_rscur into v_id, v_name, v_dept, v_job;
while ( sqlstate = '00000') do
-- do something with the values just fetched...
-- i.e. process the data in the current row of the result-set
call dbms_output.put_line('id:'||varchar(v_id)||' name: '||v_name||' dept: '||varchar(v_dept)||' job: '||v_job);
-- in this example just write the data to the output stream
fetch from v_rscur into v_id, v_name, v_dept, v_job;
end while;
return;
END
#

Related

Anchor row data type variable from declare cursor

How can I declare variable using anchor data type (row) from a declared cursor in db2?
create or replace PROCEDURE "SP_ATUALIZA_ID_DISTRIBUIDOR_FT"
BEGIN
DECLARE C_ID_DIST_ERRADOS CURSOR FOR
SELECT DISTINCT
F.ID_MES,
F.ID_DISTRIBUIDOR_SO,
D.CNPJ_DISTRIBUIDOR_SO,
F.ID_COMPANIA
FROM
DMTLDBR.TB_FATO_VENDAS_SELLOUT F,
DMTLDBR.TB_DIM_ECDISTRIBUIDOR_SO D
WHERE ID_MES <= 201309 AND
F.ID_DISTRIBUIDOR_SO = D.ID_DISTRIBUIDOR_SO AND
F.ID_COMPANIA <> D.ID_COMPANIA
ORDER BY ID_MES;
BEGIN
DECLARE REG_C_ID_DIST_ERRADOS ANCHOR ROW OF C_ID_DIST_ERRADOS;
OPEN C_ID_DIST_ERADOS
...
END;
END;
I've a procedure that is declaring a internal cursor, which will be used to fetch results and insert into a variable 'reg_c_id_dist_errados'. I've tried to use "anchor row of" but db2 has alerted me.
You can anchor a type to a row of a table , among other things. There are different ways to use these things depending on your requirements.
Your query has a join so you can declare a type to match each row from the cursor, and you can then declare strongly-typed cursor-type for your rowtype. Later you can assign a query to a variable of that cursor-type.
You can pass cursors as parameters to stored procedures, and you can have a procedure return a cursor type, and you can declare variables of each of the types you create.
You don't show whether you want the result-set of the procedure to be a cursor-type, or whether you want to process the result-set inside the stored-procedure in typed variables.
Study the Db2 Knowledge Centre for your version of Db2-LUW to learn about strongly typed cursors and how to use them in SQL PL.
There is an example online (shown as a module, but you can use the same techniques in a stored procedures) in this file "modules.db2" which also appears under the SAMPLES directory on your DB2-LUW server if you installed the samples component at installation time.
You can see the example online at
the example modules.sql
Here is one suggestion for using user defined types for a row-type that matches the cursor in your code:
--#SET TERMINATOR #
-- this type represents a row that might be returned in the cursor.
-- Choose your own name for the type.
-- Choose the correct data-types for your tables.
create or replace type DE_ROW_T as row
(
id_mes bigint,
ID_DISTRIBUIDOR_SO bigint,
CNPJ_DISTRIBUIDOR_SO bigint,
ID_COMPANIA bigint
)#
create or replace type C_ID_DIST_ERRADOS_CURSOR_T as DE_ROW_T CURSOR#
create or replace PROCEDURE "SP_ATUALIZA_ID_DISTRIBUIDOR_FT"
language sql
specific SP_ATUALIZA_ID_DISTRIBUIDOR_FT
BEGIN
DECLARE V_ROW DE_ROW_T;
DECLARE C_ID_DIST_ERRADOS C_ID_DIST_ERRADOS_CURSOR_T;
SET C_ID_DIST_ERRADOS = CURSOR FOR
SELECT DISTINCT
F.ID_MES,
F.ID_DISTRIBUIDOR_SO,
D.CNPJ_DISTRIBUIDOR_SO,
F.ID_COMPANIA
FROM
DMTLDBR.TB_FATO_VENDAS_SELLOUT F,
DMTLDBR.TB_DIM_ECDISTRIBUIDOR_SO D
WHERE ID_MES <= 201309 AND
F.ID_DISTRIBUIDOR_SO = D.ID_DISTRIBUIDOR_SO AND
F.ID_COMPANIA <> D.ID_COMPANIA
ORDER BY ID_MES;
OPEN C_ID_DIST_ERRADOS;
FETCH C_ID_DIST_ERRADOS INTO v_row;
END#

Temp table for user defined functions

I am trying to use temp table in user defined function for DB2. I am trying to do this in data studio but the code below is not working. How can I make this work?
Thanks.
CREATE FUNCTION BELSIZE.TEST (aSTRING VARCHAR(50))
RETURNS TABLE(
column1 INTEGER
)
F1: BEGIN ATOMIC
DECLARE c1 CURSOR WITH RETURN TO CLIENT FOR stmt;
SET v_dynStmt = 'SELECT 1 column1 from sysibm.sysdummy1';
PREPARE stmt FROM v_dynStmt;
OPEN c1;
RETURN
END
You have syntax errors in your code, more details below.
Apart from syntax errors, your title mentions temp table but your code does not, so your question is poor.
Never write "...is not working", instead write the exact SQLCODE and SQLSTATE and message that you see.
When asking for help with Db2, always write in the question the Db2-version and the operating-system (Z/OS, i-Series, Linux/Unix/Windows) on which the Db2-server runs, because the answer can depend on those facts. Different versions of Db2 for different operating systems have different capabilities and different syntax.
If you want to use cursors for result-sets then use SQL PL stored-procedures, because there are fewer restrictions.
SQL table functions are suitable when you don't need to declare a cursor for result-set.
Db2-LUW prevents you from declaring a cursor in an SQL table function when you use BEGIN ATOMIC.
If you are not using BEGIN ATOMIC, Db2-LUW (current versions, i.e. v11.1) lets you declare a cursor in an SQL UDF but you cannot use that cursor directly to return the result-set, as you can do inside SQL PL stored procedures.
For your example, the syntax below is valid and also useless, so consider using an SQL PL procedure instead:
--#SET TERMINATOR #
CREATE or replace FUNCTION BELSIZE.TEST (aSTRING VARCHAR(50))
RETURNS TABLE( column1 INTEGER)
language sql
specific belsize.test
BEGIN atomic
RETURN select 1 as column1 from sysibm.sysdummy1 ;
END
#
select * from table(belsize.test('a')) as t
#

Executing queries dynamically in PL/pgSQL

I have found solutions (I think) to the problem I'm about to ask for on Oracle and SQL Server, but can't seem to translate this into a Postgres solution. I am using Postgres 9.3.6.
The idea is to be able to generate "metadata" about the table content for profiling purposes. This can only be done (AFAIK) by having queries run for each column so as to find out, say... min/max/count values and such. In order to automate the procedure, it is preferable to have the queries generated by the DB, then executed.
With an example salesdata table, I'm able to generate a select query for each column, returning the min() value, using the following snippet:
SELECT 'SELECT min('||column_name||') as minval_'||column_name||' from salesdata '
FROM information_schema.columns
WHERE table_name = 'salesdata'
The advantage being that the db will generate the code regardless of the number of columns.
Now there's a myriad places I had in mind for storing these queries, either a variable of some sort, or a table column, the idea being to then have these queries execute.
I thought of storing the generated queries in a variable then executing them using the EXECUTE (or EXECUTE IMMEDIATE) statement which is the approach employed here (see right pane), but Postgres won't let me declare a variable outside a function and I've been scratching my head with how this would fit together, whether that's even the direction to follow, perhaps there's something simpler.
Would you have any pointers, I'm currently trying something like this, inspired by this other question but have no idea whether I'm headed in the right direction:
CREATE OR REPLACE FUNCTION foo()
RETURNS void AS
$$
DECLARE
dyn_sql text;
BEGIN
dyn_sql := SELECT 'SELECT min('||column_name||') from salesdata'
FROM information_schema.columns
WHERE table_name = 'salesdata';
execute dyn_sql
END
$$ LANGUAGE PLPGSQL;
System statistics
Before you roll your own, have a look at the system table pg_statistic or the view pg_stats:
This view allows access only to rows of pg_statistic that correspond
to tables the user has permission to read, and therefore it is safe to
allow public read access to this view.
It might already have some of the statistics you are about to compute. It's populated by ANALYZE, so you might run that for new (or any) tables before checking.
-- ANALYZE tbl; -- optionally, to init / refresh
SELECT * FROM pg_stats
WHERE tablename = 'tbl'
AND schemaname = 'public';
Generic dynamic plpgsql function
You want to return the minimum value for every column in a given table. This is not a trivial task, because a function (like SQL in general) demands to know the return type at creation time - or at least at call time with the help of polymorphic data types.
This function does everything automatically and safely. Works for any table, as long as the aggregate function min() is allowed for every column. But you need to know your way around PL/pgSQL.
CREATE OR REPLACE FUNCTION f_min_of(_tbl anyelement)
RETURNS SETOF anyelement
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT format('SELECT (t::%2$s).* FROM (SELECT min(%1$s) FROM %2$s) t'
, string_agg(quote_ident(attname), '), min(' ORDER BY attnum)
, pg_typeof(_tbl)::text)
FROM pg_attribute
WHERE attrelid = pg_typeof(_tbl)::text::regclass
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
);
END
$func$;
Call (important!):
SELECT * FROM f_min_of(NULL::tbl); -- tbl being the table name
db<>fiddle here
Old sqlfiddle
You need to understand these concepts:
Dynamic SQL in plpgsql with EXECUTE
Polymorphic types
Row types and table types in Postgres
How to defend against SQL injection
Aggregate functions
System catalogs
Related answer with detailed explanation:
Table name as a PostgreSQL function parameter
Refactor a PL/pgSQL function to return the output of various SELECT queries
Postgres data type cast
How to set value of composite variable field using dynamic SQL
How to check if a table exists in a given schema
Select columns with particular column names in PostgreSQL
Generate series of dates - using date type as input
Special difficulty with type mismatch
I am taking advantage of Postgres defining a row type for every existing table. Using the concept of polymorphic types I am able to create one function that works for any table.
However, some aggregate functions return related but different data types as compared to the underlying column. For instance, min(varchar_column) returns text, which is bit-compatible, but not exactly the same data type. PL/pgSQL functions have a weak spot here and insist on data types exactly as declared in the RETURNS clause. No attempt to cast, not even implicit casts, not to speak of assignment casts.
That should be improved. Tested with Postgres 9.3. Did not retest with 9.4, but I am pretty sure, nothing has changed in this area.
That's where this construct comes in as workaround:
SELECT (t::tbl).* FROM (SELECT ... FROM tbl) t;
By casting the whole row to the row type of the underlying table explicitly we force assignment casts to get original data types for every column.
This might fail for some aggregate function. sum() returns numeric for a sum(bigint_column) to accommodate for a sum overflowing the base data type. Casting back to bigint might fail ...
#Erwin Brandstetter, Many thanks for the extensive answer. pg_stats does indeed provide a few things, but what I really need to draw a complete profile is a variety of things, min, max values, counts, count of nulls, mean etc... so a bunch of queries have to be ran for each columns, some with GROUP BY and such.
Also, thanks for highlighting the importance of data types, i was sort of expecting this to throw a spanner in the works at some point, my main concern was with how to automate the query generation, and its execution, this last bit being my main concern.
I have tried the function you provide (I probably will need to start learning some plpgsql) but get a error at the SELECT (t::tbl) :
ERROR: type "tbl" does not exist
btw, what is the (t::abc) notation referred as, in python this would be a list slice, but it’s probably not the case in PLPGSQL

Why does PostgreSQL treat my query differently in a function?

I have a very simple query that is not much more complicated than:
select *
from table_name
where id = 1234
...it takes less than 50 milliseconds to run.
Took that query and put it into a function:
CREATE OR REPLACE FUNCTION pie(id_param integer)
RETURNS SETOF record AS
$BODY$
BEGIN
RETURN QUERY SELECT *
FROM table_name
where id = id_param;
END
$BODY$
LANGUAGE plpgsql STABLE;
This function when executed select * from pie(123); takes 22 seconds.
If I hard code an integer in place of id_param, the function executes in under 50 milliseconds.
Why does the fact that I am using a parameter in the where statement cause my function to run slow?
Edit to add concrete example:
CREATE TYPE test_type AS (gid integer, geocode character varying(9))
CREATE OR REPLACE FUNCTION geocode_route_by_geocode(geocode_param character)
RETURNS SETOF test_type AS
$BODY$
BEGIN
RETURN QUERY EXECUTE
'SELECT gs.geo_shape_id AS gid,
gs.geocode
FROM geo_shapes gs
WHERE geocode = $1
AND geo_type = 1
GROUP BY geography, gid, geocode' USING geocode_param;
END;
$BODY$
LANGUAGE plpgsql STABLE;
ALTER FUNCTION geocode_carrier_route_by_geocode(character)
OWNER TO root;
--Runs in 20 seconds
select * from geocode_route_by_geocode('999xyz');
--Runs in 10 milliseconds
SELECT gs.geo_shape_id AS gid,
gs.geocode
FROM geo_shapes gs
WHERE geocode = '9999xyz'
AND geo_type = 1
GROUP BY geography, gid, geocode
Update in PostgreSQL 9.2
There was a major improvement, I quote the release notes here:
Allow the planner to generate custom plans for specific parameter
values even when using prepared statements (Tom Lane)
In the past, a prepared statement always had a single "generic" plan
that was used for all parameter values, which was frequently much
inferior to the plans used for non-prepared statements containing
explicit constant values. Now, the planner attempts to generate custom
plans for specific parameter values. A generic plan will only be used
after custom plans have repeatedly proven to provide no benefit. This
change should eliminate the performance penalties formerly seen from
use of prepared statements (including non-dynamic statements in
PL/pgSQL).
Original answer for PostgreSQL 9.1 or older
A plpgsql functions has a similar effect as the PREPARE statement: queries are parsed and the query plan is cached.
The advantage is that some overhead is saved for every call.
The disadvantage is that the query plan is not optimized for the particular parameter values it is called with.
For queries on tables with even data distribution, this will generally be no problem and PL/pgSQL functions will perform somewhat faster than raw SQL queries or SQL functions. But if your query can use certain indexes depending on the actual values in the WHERE clause or, more generally, chose a better query plan for the particular values, you may end up with a sub-optimal query plan. Try an SQL function or use dynamic SQL with EXECUTE to force a the query to be re-planned for every call. Could look like this:
CREATE OR REPLACE FUNCTION pie(id_param integer)
RETURNS SETOF record AS
$BODY$
BEGIN
RETURN QUERY EXECUTE
'SELECT *
FROM table_name
where id = $1'
USING id_param;
END
$BODY$
LANGUAGE plpgsql STABLE;
Edit after comment:
If this variant does not change the execution time, there must be other factors at play that you may have missed or did not mention. Different database? Different parameter values? You would have to post more details.
I add a quote from the manual to back up my above statements:
An EXECUTE with a simple constant command string and some USING
parameters, as in the first example above, is functionally equivalent
to just writing the command directly in PL/pgSQL and allowing
replacement of PL/pgSQL variables to happen automatically. The
important difference is that EXECUTE will re-plan the command on each
execution, generating a plan that is specific to the current parameter
values; whereas PL/pgSQL normally creates a generic plan and caches it
for re-use. In situations where the best plan depends strongly on the
parameter values, EXECUTE can be significantly faster; while when the
plan is not sensitive to parameter values, re-planning will be a
waste.

Return multiple values from a SQL Server function

How would I return multiple values (say, a number and a string) from a user-defined function in SQL Server?
Change it to a table-valued function
Please refer to the following link, for example.
Another option would be to use a procedure with output parameters - Using a Stored Procedure with Output Parameters
Here's the Query Analyzer template for an in-line function - it returns 2 values by default:
-- =============================================
-- Create inline function (IF)
-- =============================================
IF EXISTS (SELECT *
FROM sysobjects
WHERE name = N'<inline_function_name, sysname, test_function>')
DROP FUNCTION <inline_function_name, sysname, test_function>
GO
CREATE FUNCTION <inline_function_name, sysname, test_function>
(<#param1, sysname, #p1> <data_type_for_param1, , int>,
<#param2, sysname, #p2> <data_type_for_param2, , char>)
RETURNS TABLE
AS
RETURN SELECT #p1 AS c1,
#p2 AS c2
GO
-- =============================================
-- Example to execute function
-- =============================================
SELECT *
FROM <owner, , dbo>.<inline_function_name, sysname, test_function>
(<value_for_#param1, , 1>,
<value_for_#param2, , 'a'>)
GO
Erland Sommarskog has an exhaustive post about passing data in SQL Server located here:
http://www.sommarskog.se/share_data.html
He covers SQL Server 2000, 2005, and 2008, and it should probably be read in its full detail as there is ample coverage of each method's advantages and drawbacks. However, here are the highlights of the article (frozen in time as of July 2015) for the sake of providing search terms that can be used to look greater details:
This article tackles two related questions:
How can I use the result set from one stored procedure in another, also expressed as How can I use the result set from a stored
procedure in a SELECT statement?
How can I pass a table data in a parameter from one stored procedure to another?
OUTPUT Parameters
Not generally applicable, but sometimes overlooked.
Table-valued Functions
Often the best choice for output-only, but there are several restrictions.
Examples:
Inline Functions: Use this to reuse a single SELECT.
Multi-statement Functions: When you need to encapsulate more complex logic.
Using a Table
The most general solution. My favoured choice for input/output scenarios.
Examples:
Sharing a Temp Table: Mainly for a single pair of caller/callee.
Process-keyed Table: Best choice for many callers to the same callee.
Global Temp Tables: A variation of process-keyed.
Table-valued Parameters
Req. Version: SQL 2008
Mainly useful when passing data from a client.
INSERT-EXEC
Deceivingly appealing, but should be used sparingly.
Using the CLR
Req. Version: SQL 2005
Complex, but useful as a last resort when INSERT-EXEC does not work.
OPENQUERY
Tricky with many pitfalls. Discouraged.
Using XML
Req. Version: SQL 2005
A bit of a kludge, but not without advantages.
Using Cursor Variables
Not recommendable.
Example of using a stored procedure with multiple out parameters
As User Mr. Brownstone suggested you can use a stored procedure; to make it easy for all i created a minimalist example. First create a stored procedure:
Create PROCEDURE MultipleOutParameter
#Input int,
#Out1 int OUTPUT,
#Out2 int OUTPUT
AS
BEGIN
Select #Out1 = #Input + 1
Select #Out2 = #Input + 2
Select 'this returns your normal Select-Statement' as Foo
, 'amazing is it not?' as Bar
-- Return can be used to get even more (afaik only int) values
Return(#Out1+#Out2+#Input)
END
Calling the stored procedure
To execute the stored procedure a few local variables are needed to receive the value:
DECLARE #GetReturnResult int, #GetOut1 int, #GetOut2 int
EXEC #GetReturnResult = MultipleOutParameter
#Input = 1,
#Out1 = #GetOut1 OUTPUT,
#Out2 = #GetOut2 OUTPUT
To see the values content you can do the following
Select #GetReturnResult as ReturnResult, #GetOut1 as Out_1, #GetOut2 as Out_2
This will be the result: