Create a procedure named 'payment_count' which accepts one output parameter p_count with number as its data type. This procedure should print the count of successful payments (response_msg as 'Successful') with alias name as 'successful_payment_count'.
Hints: Procedure name : payment_count
Output parameter : p_count
Table used : payment_authorization
Function used : count
create or replace procedure payment_count(p_count output number)
as
begin
select count(response_msg='Successful') into successful_payment_count from payment_authorization;
return successful_payment_count;
end;
/
schema payment_authorization
Is this a task in an exam?
Sounds so... anyway:
You have to change keyword output to OUT, change the COUNT(response_msg='Successful') to COUNT(*) and add response_msg='Successful' to the where clause of the query.
create or replace procedure payment_count(p_count out number)
as
begin
select count(response_msg) as successful_payment_count
into p_count
from payment_authorization
where response_msg = 'Successful';
end;
/
Related
I am using mariadb 10.3.9, and have created a user defined aggregate function (UDAF) and placed in a common_schema. This schema contains my utility functions to be used by other schema/databases on the same server.
The issue is that when calling the UDAF while using any other schema, it always return NULL!
The following is to demonstrate the issue:
CREATE SCHEMA IF NOT EXISTS common_schema;
DELIMITER $$
DROP FUNCTION IF EXISTS common_schema.add_ints $$
CREATE FUNCTION common_schema.add_ints(int_1 INT, int_2 INT) RETURNS INT NO SQL
BEGIN
RETURN int_1 + int_2;
END $$
DROP FUNCTION IF EXISTS common_schema.sum_ints $$
CREATE AGGREGATE FUNCTION common_schema.sum_ints(int_val INT) RETURNS INT
BEGIN
DECLARE result INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN result;
LOOP FETCH GROUP NEXT ROW;
SET result = common_schema.add_ints(result, int_val);
END LOOP;
END $$
DELIMITER ;
Now, calling it this way, returns the result as expected:
USE common_schema;
SELECT common_schema.sum_ints(seq)
FROM (SELECT 1 seq UNION ALL SELECT 2) t;
-- result: 3
Calling it using any other schema, it returns NULL:
USE other_schema;
SELECT common_schema.sum_ints(seq)
FROM (SELECT 1 seq UNION ALL SELECT 2) t;
-- result: null
Am I missing something here? Is there any configuration that is missing?
Appreciate your help.
Reported as a Bug https://jira.mariadb.org/browse/MDEV-18100.
As a workaround, create the UDAF in every schema.
I have a stored procedure I've started coding and need to return a value. In SQL Server I could just do a SELECT of the variable to return it. However this does not seem to work with Teradata and have not found a similar example on how to do this. Here is my stored procedure:
REPLACE PROCEDURE sp_Get_MyValue()
BEGIN
DECLARE mytestvar VARCHAR(40);
SELECT mycolumn INTO mytestvar FROM MyTable;
SELECT mytestvar;
END;
I get this error:
STATEMENT 2: REPLACE failed. Failed [5526 : HY000] Stored Procedure
is not created/replaced due to error(s).{Nested Failure Msg [5526 :
HY000] SPL1045:E(L10), Invalid or missing INTO clause.}
I also tried adding an OUT variable to the procedure but that did not work either:
REPLACE PROCEDURE sp_Get_MyValue(mytestvarout VARCHAR(40))
BEGIN
DECLARE mytestvar VARCHAR(40);
SELECT mycolumn INTO mytestvar FROM MyTable;
END;
With this error:
Executed as Single statement. Failed [5531 : HY000] Named-list is not
supported for arguments of a procedure. Elapsed time = 00:00:00.079
To return a single row you must define and OUT-variable and assign a value to it:
REPLACE PROCEDURE sp_Get_MyValue(OUT mytestvarout VARCHAR(40))
BEGIN
DECLARE mytestvar VARCHAR(40);
SELECT mycolumn INTO mytestvar FROM MyTable;
SET mytestvarout = mytestvar;
END;
To return a result set you need to define a kind of dummy-cursor (blame Standard SQL :-)
Returning Result Sets from a Stored Procedure
I was trying to create a function that given a customer name, get the total balance of that customer's accounts from the account table(A#, CNAME, BAL).
When compiling in command, I got errors:
line "SELECT SUM(bal) INTO total_a_bal FROM account" ignored
and line "WHERE account.cname = v_acname" SQL cmd not properly ended.
Please help correct the errors. Any help would be greatly appreciated!
My function is :
CREATE OR REPLACE FUNCTION totbal
(v_acname IN account.cname%type)
RETURN NUMBER
IS
total_a_bal NUMBER;
BEGIN
SELECT SUM(bal) INTO total_a_bal FROM account
group by account.cname
WHERE account.cname = v_acname;
RETURN total_a_bal;
END;
/
This may not be the only problem with your function, but the WHERE clause always precedes the GROUP BY clause in a SQL query. Hence, your function should look like this:
CREATE OR REPLACE FUNCTION totbal
(v_acname IN varchar2) -- a type should follow IN, e.g. varchar2
RETURN NUMBER
IS
total_a_bal NUMBER;
BEGIN
SELECT SUM(bal) INTO total_a_bal
FROM account
WHERE account.cname = v_acname; -- WHERE always precedes GROUP BY
GROUP BY account.cname
RETURN total_a_bal;
END;
Oracle not only works with the parameter definition "account.cname%type" that is preferred method. What it does is to bind the parameter definition to the column in the database. If the column definition is changed the procedure automatically gets the changed.
This concept of defining in terms of data base columns can (and perhaps should) be extended into the variables and return types of the procedure/function itself.
This function then becomes:
CREATE OR REPLACE FUNCTION totbal
(v_acname IN account.cname%type)
RETURN account.bal%type
IS
total_a_bal account.bal%type;
BEGIN
SELECT SUM(bal) INTO total_a_bal FROM account
group by account.cname
WHERE account.cname = v_acname;
RETURN total_a_bal;
END;
I have to use existing stored procedure which returns REF CURSOR. I need to insert that resultset into a temporary table.
Spec of procedure is:
TYPE cur IS REF CURSOR;
PROCEDURE get(p_one NUMBER ,p_two OUT cur);
How can I insert the resultset of this procedure into a table.
I just re-read the title of your question. Do you actually need to be able to select from a procedure?
If so, this can be achieved by using pipelined functions.
The process for this is:
Create an object type to represent the record-type you require.
Create a nested table type of the object.
Create a pipelined function which returns the nest table.
You can then select from this function.
This example should get you on your way:
create or replace type to_test as object (
val1 varchar2(32),
val2 varchar2(32)
);
create or replace type tt_test as table of to_test;
create or replace function demo_pipe return tt_test pipelined
is
v_test to_test;
begin
for rec in (select * from user_tables) loop
v_test := to_test(rec.table_name, rec.tablespace_name);
pipe row (v_test);
end loop;
end;
/
select * from table(demo_pipe);
input
Package name (IN)
procedure name (or function name) (IN)
A table indexed by integer, it will contain values that will be used to execute the procedure (IN/OUT).
E.g
let's assume that we want to execute the procedure below
utils.get_emp_num(emp_name IN VARCHAR
emp_last_name IN VARCHAR
emp_num OUT NUMBER
result OUT VARCHAR);
The procedure that we will create will have as inputs:
package_name = utils
procedure_name = get_emp_num
table = T[1] -> name
T[2] -> lastname
T[3] -> 0 (any value)
T[4] -> N (any value)
run_procedure(package_name,
procedure_name,
table)
The main procedure should return the same table that has been set in the input, but with the execution result of the procedure
table = T[1] -> name
T[2] -> lastname
T[3] -> 78734 (new value)
T[4] -> F (new value)
any thought ?
You can achieve it with EXECUTE IMMEDIATE. Basically, you build a SQL statement of the following form:
sql := 'BEGIN utils.get_emp_num(:1, :2, :3, :4); END;';
Then you execute it:
EXECUTE IMMEDIATE sql USING t(1), t(2), OUT t(3), OUT t(4);
Now here comes the tricky part: For each number of parameters and IN/OUT combinations you need a separate EXECUTE IMMEDIATE statement. And to figure out the number of parameters and their direction, you need to query the ALL_ARGUMENTS table first.
You might be able to simplify it by passing the whole table as a bind argument instead of a separate bind argument for each table element. But I haven't quite figured out how you would do that.
And the next thing you should consider: the elements of the table T your using will have a type: VARCHAR, NUMBER etc. So the current mixture where you have both numbers and strings won't work.
BTW: Why do you want such a dynamic call mechanism anyway?
Get from the all_arguments table the argument_name, data_type, in_out, and the position
Build the PLSQL block
DECLARE
loop over argument_name and create the declare section
argument_name data_type if in_out <> OUT then := VALUE OF THE INPUT otherwise NULL
BEGIN
--In the case of function create an additional argument
function_var:= package_name.procedure_name( loop over argument_name);
--use a table of any_data, declare it as global in the package
if function then
package_name.ad_table.EXTEND;
package_name.ad_table(package_name.ad_table.LAST):= function_var;
end if
--loop over argument_name IF IN_OUT <> IN
package_name.ad_table.EXTEND;
package_name.ad_table(package_name.ad_table.LAST):=
if data_type = VARCHAR2 then := ConvertVarchar2(argument_name)
else if NUMBER then ConvertNumber
else if DATE then ConvertDate
...
END;
The result is stored in the table.
To get value use Access* functions