how to apply an aggregation function over a measure dynamically passed to a stored procedure? - sql

I am trying to do spit by dimension on an analytic view within a stored procedure, and I want to pass the measure on which I will apply the aggregation function dynamically. So I did the following:
create procedure procHO (in currentMeasure varchar(60))
language sqlscript as
begin
data_tab = select MONTH_NAME as ID, sum(:currentMeasure) from
_SYS_BIC."schema/analyticView" GROUP BY MONTH_NAME;
end;
then I call the procedure this way:
call procHO("MARGIN");
but I am getting an error saying :
inconsistent datatype: only numeric type is available for aggregation function: line 5 col 38 (at pos 124) Could not execute 'call procHO("MARGIN")'
I also tried to do this using CE_ functions, here is what I did:
create procedure procHO1(in currentMeasure varchar(60))
language sqlscript as
begin
out1 = CE_OLAP_VIEW("schema/analyticView", ["MONTH_NAME",
SUM(:currentMeasure)]);
end;
and I call the procedure this way:
call procHO1("MARGIN");
but still, I am getting an error saying:
feature not supported: line 5 col 70 (at pos 157)
Could not execute 'call procHO1("MARGIN")'
by the way, as a workaround, it is possible to create a dynamic SQL query that would resolve the issue, here is an example:
create procedure procHO2(in currentMeasure varchar(60))
language sqlscript as
begin
exec 'select MONTH_NAME AS ID, sum('||:currentMeasure||') as SUM_MEASURE from
_SYS_BIC."schema/analyticView" GROUP BY MONTH_NAME';
end;
I call it this way
call procHO2('MARGIN');
but I don't want to create the SQL query dynamically since it's not recommended by SAP.
So what to do to pass an aggregated measure dynamically?

this is what the sample code from the documentation:
CREATE PROCEDURE my_proc_caller (IN in_client INT, IN in_currency INT, OUT outtab mytab_t) LANGUAGE SQLSCRIPT
BEGIN
outtab = SELECT * FROM CALC_VIEW (PLACEHOLDER."$$client$$" => :in_client , PLACEHOLDER."$$currency$$" => :in_currency );
END;
of course this works only on the latest release. Which revision are you running on?

Related

Can I create a "table-valued function" in an Oracle package?

I'm relatively new to the Oracle world. Most of my experience is with SQL Server.
I am writing code that would benefit from a "parameterized view", aka a "table-valued function" (tvf) in SQL Server.
I found a good example here that I'm trying to follow: Oracle: Return a «table» from a function
But I need mine to be inside a package, and I'm having a devil of a time with it.
Here's an example of what I'm trying:
CREATE OR REPLACE PACKAGE pkg_test_oracle_tvfs IS
TYPE t_tvf_row IS RECORD(
i NUMBER,
n VARCHAR2(30));
TYPE t_tvf_tbl IS TABLE OF t_tvf_row INDEX BY BINARY_INTEGER;
FUNCTION fn_get_tvf(p_max_num_rows INTEGER) RETURN t_tvf_tbl;
END pkg_test_oracle_tvfs;
CREATE OR REPLACE PACKAGE BODY pkg_test_oracle_tvfs IS
FUNCTION fn_get_tvf(p_max_num_rows INTEGER) RETURN t_tvf_tbl IS
v_tvf_tbl t_tvf_tbl;
BEGIN
SELECT pkg_test_oracle_tvfs.t_tvf_row(rownum,
uo.object_name)
BULK COLLECT
INTO v_tvf_tbl
FROM user_objects uo
WHERE rownum <= p_max_num_rows;
RETURN v_tvf_tbl;
END;
END pkg_test_oracle_tvfs;
With the intent that I can do something like:
SELECT * FROM pkg_test_oracle_tvfs.fn_get_tvf(5);
Or
SELECT * FROM TABLE(pkg_test_oracle_tvfs.fn_get_tvf(5));
(I'm unclear if the TABLE() is required.)
But when I compile the package I get:
Compilation errors for PACKAGE BODY XXX.PKG_TEST_ORACLE_TVFS
Error: PL/SQL: ORA-00913: too many values
Line: 11
Text: FROM user_objects uo
Error: PL/SQL: SQL Statement ignored
Line: 7
Text: SELECT pkg_test_oracle_tvfs.t_tvf_row(rownum,
What am I doing wrong here? Why does this syntax seem to work fine outside of a package but not inside one?
Do I need to use the "pipeline" style of constructing the table as described in Oracle: Pipelined PL/SQL functions If so, why is this example different than the one I've been trying to follow?
Thanks!
Your initial error is because you're selecting into a record type, not an object type, so you don't need the constructor:
SELECT rownum, uo.object_name
BULK COLLECT
INTO v_tvf_tbl
fiddle, which shows it now compiles, but you can't call it from SQL for the reason's MTO already explained.
As an alternative to creating an object type, you can as you suggested use a pipelined function, if you modify the collection type:
CREATE OR REPLACE PACKAGE pkg_test_oracle_tvfs IS
TYPE t_tvf_row IS RECORD(
i NUMBER,
n VARCHAR2(30));
TYPE t_tvf_tbl IS TABLE OF t_tvf_row;
FUNCTION fn_get_tvf(p_max_num_rows INTEGER) RETURN t_tvf_tbl PIPELINED;
END pkg_test_oracle_tvfs;
/
CREATE OR REPLACE PACKAGE BODY pkg_test_oracle_tvfs IS
FUNCTION fn_get_tvf(p_max_num_rows INTEGER) RETURN t_tvf_tbl PIPELINED IS
v_tvf_tbl t_tvf_tbl;
BEGIN
SELECT rownum, uo.object_name
BULK COLLECT
INTO v_tvf_tbl
FROM user_objects uo
WHERE rownum <= p_max_num_rows;
FOR i IN 1..v_tvf_tbl.COUNT LOOP
PIPE ROW (v_tvf_tbl(i));
END LOOP;
RETURN;
END;
END pkg_test_oracle_tvfs;
/
SELECT * FROM pkg_test_oracle_tvfs.fn_get_tvf(5);
I
N
1
PKG_TEST_ORACLE_TVFS
2
PKG_TEST_ORACLE_TVFS
SELECT * FROM TABLE(pkg_test_oracle_tvfs.fn_get_tvf(5));
I
N
1
PKG_TEST_ORACLE_TVFS
2
PKG_TEST_ORACLE_TVFS
fiddle
There is a fundamental flaw; both RECORDs and associative arrays (TABLE OF ... INDEX BY ...) are PL/SQL only data types and cannot be used in SQL statements.
If you want to use a record-like and array-like data structure in an SQL statement then you will need to define it in the SQL scope which means that you cannot define it in a package and would need to use an OBJECT type and a nested-table collection type:
CREATE TYPE t_tvf_row IS OBJECT(
i NUMBER,
n VARCHAR2(30)
);
CREATE TYPE t_tvf_tbl IS TABLE OF t_tvf_row;
Then:
CREATE OR REPLACE PACKAGE pkg_test_oracle_tvfs IS
FUNCTION fn_get_tvf(
p_max_num_rows INTEGER
) RETURN t_tvf_tbl;
END pkg_test_oracle_tvfs;
/
CREATE OR REPLACE PACKAGE BODY pkg_test_oracle_tvfs IS
FUNCTION fn_get_tvf(
p_max_num_rows INTEGER
) RETURN t_tvf_tbl
IS
v_tvf_tbl t_tvf_tbl;
BEGIN
SELECT t_tvf_row(
rownum,
object_name
)
BULK COLLECT INTO v_tvf_tbl
FROM (
SELECT object_name
FROM user_objects
ORDER BY object_name
)
WHERE rownum <= p_max_num_rows;
RETURN v_tvf_tbl;
END;
END pkg_test_oracle_tvfs;
/
fiddle
why is this example different than the one I've been trying to follow?
Because you are defining data-types in a PL/SQL scope (a package) that can only be used in PL/SQL (because records and associative arrays are PL/SQL-only data types) and then trying to use them in an SQL scope (a SELECT statement). The example you are following defines the data types as an OBJECT and a non-associative array and defines them in the SQL scope (outside of a package) and then using them in an SQL statement is allowable.

Accessing parameters in a SQL stored Procedure in Snowflake

I am trying to create a stored procedure that uses an integer parameter and dateadd() to create a timestamp that's used to filter results in a where clause. I am getting "Uncaught exception of type 'STATEMENT_ERROR' on line 3 at position 2 : SQL compilation error: error line 9 at position 53 invalid identifier 'X_DAYS_BACK'" when I try to run the procedure below. If I move the return line above the create table line the procedure successfully runs and the return string includes the actual value of x_days_back (e.g. base.ITEM updated for last 3 days.).
CREATE OR REPLACE PROCEDURE BASE.ITEM_LOAD_test(X_DAYS_BACK INTEGER)
returns string not null
language SQL
as
$$
BEGIN
CREATE OR REPLACE TEMPORARY TABLE BASE.TEMP_DELTA_ITEM (
ID STRING
)
AS
SELECT
SOURCE||KEY AS ID
FROM BASE.SUPPLIER_CATALOG_ITEM
WHERE
CDP__ETL_UPDATE_TIMESTAMP >= DATEADD(Day ,-1*X_DAYS_BACK, CURRENT_DATE);
drop TABLE BASE.TEMP_DELTA_ITEM;
RETURN 'base.ITEM updated for last ' || X_DAYS_BACK || ' days.' ;
END;
$$
How do I access a parameter of a SQL stored procedure within a function such as dateadd? I know it's a preview feature, so is this not currently implemented or?
Toss a colon in front of the variable/parameter:
DATEADD(day, -1*:X_DAYS_BACK, CURRENT_DATE);
Their documentation isn't amazing for SQL stored procedures but there is mention of this here where it states:
You can use a variable in a SQL statement. (This is sometimes referred to as binding a variable.) Prefix the variable name with a colon.

Need help translating T-SQL to PostgreSQL

I just want a translation of a simple procedure that returns the amount of clients in a specific area (area_id), to use it in PostgreSQL.
The code in T-SQL is:
CREATE PROCEDURE findGeoClients
#area_id INT
AS
SELECT COUNT(*) AS clients_in_that_area
FROM clients
WHERE area_id = #area_id
I tried converting it using sqlines.com/online but I am constantly getting syntax errors.
Procedures aren't meant to return anything, you need a function. But you don't need PL/pgSQL for this. A simple SQL function will do:
CREATE function findgeoclients(p_area_id int)
returns bigint
AS
$$
SELECT COUNT(*) AS clients_in_that_area
FROM clients
WHERE area_id = p_area_id;
$$
language sql
stable;

Column '' has unsupported type "information_schema.sql_identifier"

I am trying to test my stored procedure in MySQL workbench/j. I get an error when I am trying to call the stored procedure.
I have created a table to store the result of my stored procedure
CREATE TABLE IF NOT EXISTS ableok
(
name VARCHAR(50) ENCODE lzo
);
This is my stored procedure:
CREATE OR REPLACE PROCEDURE sp_GetDistSchema()
AS '
BEGIN
SELECT table_schema INTO ableok FROM information_schema.tables;
END;
'
LANGUAGE plpgsql;
This is how i call my stored procedure in SQL workbench/j:
call sp_getdistschema();
Result:
An error occurred when executing the SQL command:
call sp_getdistschema()
[Amazon](500310) Invalid operation: Column "table_schema" has unsupported type "information_schema.sql_identifier".; [SQL State=0A000, DB Errorcode=500310]
1 statement failed.
The SELECT ... INTO structure is used to store a query result into variables. It looks as though you are really just trying to populate the distTable directly. Try this instead:
Update: When processing the information schema in Redshift/PostgreSQL, you apparently need to convert the column datatypes using CAST:
CREATE OR REPLACE PROCEDURE sp_GetDistSchema()
BEGIN
INSERT INTO distTable SELECT DISTINCT CAST(table_schema AS VARCHAR) FROM information_schema.tables;
END;
As #user9601310 mentioned (up voted), you need to CAST the column data types.
I was scratching my head too, even in plain old Postgres when your using the information_schema.
This will 'describe' a table or a view, but won't work unless the query columns are cast as VARCHAR:
CREATE OR REPLACE FUNCTION public.fn_desc(p_tablename VARCHAR)
RETURNS TABLE(vtable_name VARCHAR, vcolumn_name VARCHAR, vdata_type VARCHAR)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
table_name::VARCHAR,
column_name::VARCHAR,
data_type::VARCHAR
FROM
information_schema.columns
WHERE
table_name ILIKE p_tablename;
END;
$function$
SELECT * FROM public.fn_desc('any_table_or_view');

Syntax for SQL function, in variables and return

I have a sql function called task2.sql:
-- script to create function task2
CREATE OR REPLACE Function task2
(input_userid IN integer)
begin
return (select *
from tweet twt
where twt.userid = input_userid
order by publishtime desc, tweetid desc);
END;
/
exit;
I'm trying to return all the rows with the goal being to write them to a text file. I'm using a java file to call this function and to manage the results. I am messing up the syntax somewhere. Any help is appreciated.
Oracle functions can't do this in such way. For returning rows from function you should use Table Function (with pipeline may be)
http://docs.oracle.com/cd/B19306_01/appdev.102/b14289/dcitblfns.htm#CHDCIEJG
But, for wrapping query into procedure i can reccomend you return SYS_REFCURSOR
CREATE OR REPLACE PROCEDURE task2
(input_userid IN integer, out_cur OUT SYS_REFCURSOR)
begin
OPEN out_cur FOR select *
from tweet twt
where twt.userid = input_userid
order by publishtime desc, tweetid desc;
END;
Then, you should fetch rows from out_cur in your application. It's easy and google and SF full of this examples. For example read data from SYS_REFCURSOR in a Oracle stored procedure and reuse it in java