Calling stored procedure in select statement with Db2 - db2

I have a select statement that's working fine but I need one more value that comes from a stored procedure and I can't call it as a value in the select statement
select
recp.percent,
(call libraryEXT.get_rate_for_detail(detail_number))
from
mainTable.orders ORD
inner join
mainTable.receipts recp on ORD.rcptID = recp.rcptID
It works fine to only get recp.percent, but putting the next value in for calling the stored procedure says it doesn't expect "call"
If I run the stored procedure by itself it returns one record with the columns: name, rcptID, time and I need the rcptID from that
How can I properly call the stored procedure and make the value returned be my other value in the select statment?

As #mao said, this is not possible to call an SP in a select statement.
If you are the SP owner, try creating a table-function instead. This one can be called in a select statement.

Given the way you've used CALL libraryEXT.get_rate_for_detail(detail_number) it's apparently only returning a single value.
That being the case, you'd be better served by having it as a scaler User Defined Function (UDF).
Then this is what your code would look like:
select
recp.percent,
libraryEXT.get_rate_for_detail_UDF(detail_number) as rate
from
mainTable.orders ORD
inner join
mainTable.receipts recp on ORD.rcptID = recp.rcptID
I renamed the function just to be clear it's NOT the stored proc. If you created the stored proc, you could delete it and use the same name for the UDF.

Related

Postgres Functions: Getting the Return Table Column Details

I feel the need to get the column names and data types of the table returned by any function that has a 'record' return data type, because...
A key process in an existing SQL Server-based system makes use of a stored procedure that takes a user-defined function as a parameter. An initial step gets the column names and types of the table returned by the function that was passed as a parameter.
In Postgres 13 I can use pg_proc.prorettype and the corresponding pg_type to find functions that return record types...that's a start. I can also use pg_get_function_result() to get the string containing the information I need. But, it's a string, and while I ultimately will have to assemble a very similar string, this is just one application of the info. Is there a tabular equivalent containing (column_name, data_type, ordinal_position), or do I need to do that myself?
Is there access to a composite data type the system may have created when such a function is created?
One option that I think will work for me, but I think it's a little weird, is to:
> create temp table t as select * from function() limit 0;
then look that table up in info_schema.columns, assemble what I need and drop the temp table...putting all of this into a function.
You can query the catalog table pg_proc, which contains all the required information:
SELECT coalesce(p.na, 'column' || p.i),
p.ty::regtype,
p.i
FROM pg_proc AS f
CROSS JOIN LATERAL unnest(
coalesce(f.proallargtypes, ARRAY[f.prorettype]),
f.proargmodes,
f.proargnames
)
WITH ORDINALITY AS p(ty,mo,na,i)
WHERE f.proname = 'interval_ok'
AND coalesce(p.mo, 'o') IN ('o', 't')
ORDER BY p.i;

DB2oC (DB2 on Cloud) : Facing "attempted to modify data but was not defined as MODIFIES SQL DATA" error

I have created very simple function in DB2oC as below, which has one UPDATE sql statement and one SELECT sql statement along with MODIFIES SQL DATA. But still I get the below error, though I have specified MODIFIES SQL DATA. I did GRANT ALL on that TEST table to my user id and also did GRANT EXECUTE ON FUNCTION to my user id on safe side. Can you please help to explain on what could be the issue?
I have simply invoked the function using SELECT statement like below:
SELECT TESTSCHEMA.MAIN_FUNCTION() FROM TABLE(VALUES(1));
SQL Error [38002]: User defined routine "TESTSCHEMA.MAIN_FUNCTION"
(specific name "SQL201211013006981") attempted to modify data but was
not defined as MODIFIES SQL DATA.. SQLCODE=-577, SQLSTATE=38002,
DRIVER=4.27.25
CREATE OR REPLACE FUNCTION MAIN_FUNCTION()
RETURNS VARCHAR(20)
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
DECLARE val VARCHAR(20);
UPDATE TEST t SET t.CONTENT_TEXT = 'test value' WHERE t.ID = 1;
select CONTENT_TEXT into val from TEST where ID = 1;
return val;
end;
Appreciate your help.
For the modifies SQL data clause , the usage of the function is restricted on Db2-LUW.
These restrictions do not apply for user defined functions that do not modify data.
For your specific example, that UDF will operate when used as the sole expression on the right hand side of an assignment statement in a compound-SQL compiled statemnent.
For example:
create or replace variable my_result varchar(20) default null;
begin
set my_result = main_function();
end#
Consider using stored procedures to modify table contents, instead of user defined functions.
You could avoid using a function, and just use a single "change data statement"
SELECT CONTENT_TEXT
FROM NEW TABLE(
UPDATE TEST t
SET t.CONTENT_TEXT = 'test value'
WHERE t.ID = 1
)

Stored procedure never deletes rows when called

Inside a SQL Server 2012 stored procedure, I have the following lines of code that do the following:
Declares and sets a variable called #galaxyID
Then uses that #galaxyID to delete all rows from a table called galaxyObjects that have a particular galaxyID
I need to check to make sure that #galaxy is not null because sometimes the column is NULL in the table astroList.
I don't have any errors, but nothing is ever deleted and I'm not sure why.
Here is the code:
DECLARE #galaxyID UNIQUEIDENTIFIER
SET #galaxyID = (SELECT galaxyID FROM astroList WHERE astroID = #astroID)
DELETE FROM galaxyObjects
WHERE galaxyID = #galaxyID AND #galaxyID IS NOT NULL
You can't really use SET #var = (SELECT ...) unless there is no possible way for the SELECT to ever return more than one row. So anyone looking at this code that doesn't know the data model will assume this is a ticking time bomb. A safer option in SQL Server is to use a DELETE with a join (which works for any number of rows, and is a no-op when there is no match):
DELETE go
FROM dbo.astroList AS al
INNER JOIN dbo.galaxyObjects AS go
ON al.galaxyID = go.galaxyID
WHERE al.astroID = #astroID;
This will ignore rows where galaxyID in astroList is NULL.

sybase - procedure returns previous result set

I have a procedure with 2 selects, looks something like this...
PROCEDURE getRec
#pId INTEGER
AS
BEGIN
SELECT 'anything'
SELECT id, name
FROM my_table
WHERE id = #pId
END
When my perl script calls this stored procedure, if my_table has a matching record then it is displayed. However, if the ID passed in has no matches then the stored procedure returns 'anything'.
If there are no rows in the second select then I just want the procedure to return an empty result set. How can I achieve this?
Every SELECT will produce a result set (with the exception of SELECT #var = ...)
So will first be receiving the 'anything' result set
After that, you will receive the empty result set.
You need to get your perl code to fetch all rows in the first result set, then get the next result set and fetch all rows in that result set.
The functions to get the next result set will greatly depend on the perl library you are using.

How to update a value using string_agg without using a function?

I solved this problem with a function and I like my solution, but I want to know if there is a way to solve this problem without using functions. Here is the thing:
There are four tables that are relevant to this:
entities: entities of the system (tenants)
members: members of an entity
member_sets: sets of members
members_and_sets: table to join members and sets (many to many)
The member_sets table has a column named bits which is a binary representation of the set, so, for example, if an entity has 5 members and one specific set has the third member, the value of the bits column is 00100, the entity has three special kinds of sets: universe, empty and unit, their binary repesentation is: 11111, 00000 and 10000 respectively, assuming the unit set has the first member.
The challenge is keep this binary representation of the set up to date; Whenever one member is added to the entity, all binary representations must be updated. This is easy to do with a trigger and a function, my solution is this:
CREATE FUNCTION setbits(INTEGER) RETURNS VARBIT AS
$$SELECT STRING_AGG(belongs, '')::VARBIT AS setbits FROM (
SELECT LEAST(COALESCE(members_and_sets.set_id, 0), 1)::text AS belongs
FROM members LEFT JOIN members_and_sets
ON members.id=members_and_sets.member_id
AND members_and_sets.set_id=$1
GROUP BY members.id,members_and_sets.set_id
ORDER BY members.id)
AS bitsring;$$
LANGUAGE SQL
RETURNS NULL ON NULL INPUT;
-- calling this function in a trigger after inserting a new member:
UPDATE member_sets ms
SET bits=setbits(ms.id)
WHERE ms.entity_id=NEW.entity_id;
Now my question is: Can I do this without using a function? I tried with CTE but apparently I'm to noob to accomplish this; I couldn't pass the set_id to the must inner query so my solution was to wrap the query in a function and pass the set_id as an argument to the function. Again, this solution works perfectly, I just want to know if there is no way I can do this without a function call.
As your function body is simply a SELECT, you should be able to replace the function call with a subquery:
UPDATE member_sets ms
SET bits= (
SELECT STRING_AGG(belongs, '')::VARBIT AS setbits FROM (
SELECT LEAST(COALESCE(members_and_sets.set_id, 0), 1)::text AS belongs
FROM members LEFT JOIN members_and_sets
ON members.id=members_and_sets.member_id
AND members_and_sets.set_id=ms.id
GROUP BY members.id,members_and_sets.set_id
ORDER BY members.id)
AS bitsring
)
WHERE ms.entity_id=NEW.entity_id;