How to call pl-sql function that takes table of numbers as input with 1 number? - sql

I have a function that takes table of Numbers as an input:
function foo(ids in custom_array)...
where custom_array is a table of Numbers.
How do I call this function from SQL query if I need to pass only 1 parameter?
I supposed that this should look like this
select * from table(foo(123))
but my guess was wrong.
edit:
function's declaration looks like this:
create or replace function foo(id in custom_array) return
tableof2numbers AS
mytable tableof2numbers;
BEGIN
SELECT cast(
MULTISET (
/*there goes some super secret business logic*/
/*let's say that here we just do smth like this: */
select 123, 123 from dual
/*because this is not so important for this question*/
)
AS tableof2numbers)
INTO
mytable
FROM dual;
RETURN mytable;
where tableof2numbers is another custom type which declaration is pretty similar to custom_array, but with 2 numbers.
Thanks in advance!!!

My example:
1) TYPE
create or replace TYPE "TABLE_OF_NUMBER"
AS TABLE OF NUMBER
2) FUNCTION
CREATE OR REPLACE FUNCTION foo(
par IN table_of_number)
RETURN NUMBER
IS
BEGIN
RETURN par(1);
END;
3) SELECT
SELECT foo(table_of_number(123)) FROM dual
If your function returns table, it will be something like
SELECT * FROM TABLE (foo(custom_array(123)))
Hope it will help.

Related

Oracle SQL: how to pass parameter to a function that is used in a view through a column in a view?

Let's say we have such view that uses function with hard-coded parameter:
CREATE OR REPLACE VIEW x AS
SELECT t.some_value
FROM table(function(p1 => '1')) t;
If I'd like to pass that parameter to a function through a view, what are possible options? Please mind that using global or context/bind variables is not an option. So far I've came up with an option to use a table that holds all available parameter values (keys) that could be passed to a view:
CREATE OR REPLACE VIEW x AS
SELECT st.input_param,
t.some_value
FROM some_table st
table(function(p1 => st.input_param)) t;
However, I am wondering if there are any other possible options?
You can't pass a parameter to a view but you can use the next alternative:
CREATE TYPE RECORDS_VARCHAR AS TABLE OF VARCHAR2(100);
create or replace function virtual_table( input_param number )
return RECORDS_VARCHAR
PIPELINED
is
begin
FOR a IN (
select '1' AS VALUE from dual where input_param = 2
UNION ALL
select '8' AS VALUE from dual
) loop
pipe row (a.VALUE);
end loop;
return;
end;
SELECT * FROM TABLE(virtual_table(2)); --1,8
SELECT * FROM TABLE(virtual_table(1)); --8

How return dynamic number of columns in function?

In PostgreSQL 11 database I have table with 6 column. Next function return static number of defined columns.
CREATE FUNCTION CALCULATION(INTEGER)
RETURNS TABLE(
ORGANIZATION_ID INT4,
ORGANIZATION_NAME VARCHAR,
ORGANIZATION_RANG INT4,
PARENT_ORGANIZATION_ID INT4,
PARENT_ORGANIZATION_NAME VARCHAR,
PARENT_ORGANIZATION_RANG INT4
) AS $$
SELECT * FROM ANALYTICS;
$$ LANGUAGE SQL;
How can I make an SQL function in Postgres 11 which return a result set with dynamic number of columns according to a parameter passed in?
For example if I call SELECT * FROM CALCULATION(2);, function return first 2 columns.
If this is not possible with an SQL function, is it possible with a PL/pgSQL function?
This is possible for RECORD returning functions.
CREATE FUNCTION calculation(how_many integer) RETURNS SETOF RECORD
LANGUAGE plpgsql
AS $fff$
BEGIN
IF how_many = 1
THEN RETURN QUERY SELECT 'foo'::text;
ELSIF how_many = 2
THEN RETURN QUERY SELECT 'foo'::text, 'bar'::text;
END IF;
END;
$fff$
;
And now you can do:
jbet=> SELECT * FROM calculation(1) AS f(first_col text);
first_col
-----------
foo
(1 row)
jbet=> SELECT * FROM calculation(2) AS f(first_col text, second_col text);
first_col | second_col
-----------+------------
foo | bar
(1 row)
The very serious downside is that each time you call the function you have to define set of returned columns, so I don't think you'll find this answer useful : )
Anyway, Postgresql needs to know returned type of each SELECT before it runs the query, so one or other way you have to define the columns.
JSON return value could be a reasonable answer if you just want the data and don't care if there are separate columns or not.
Backing up a step, why not use a standard select to get the columns you want from your set-returning function?
select organization_name,
organization_rang,
parent_organization_name,
parent_organization_rang
from calculation();
That's easy to follow and flexible. I'm guessing that you've written a simplified example and have a good reason for what you're asking...but I figured I'd double-check.

Selecting and passing a record as a function argument

It may look like a duplicate of existing questions (e.g. This one) but they only deal with passing "new" arguments, not selecting rows from the database.
I have a table, for example:
CREATE TABLE my_table (
id bigserial NOT NULL,
name text,
CONSTRAINT my_table_pkey PRIMARY KEY (id)
);
And a function:
CREATE FUNCTION do_something(row_in my_table) RETURNS void AS
$$
BEGIN
-- does something
END;
$$
LANGUAGE plpgsql;
I would like to run it on data already existing in the database. It's no problem if I would like to use it from another PL/pgSQL stored procedure, for example:
-- ...
SELECT * INTO row_var FROM my_table WHERE id = 123; -- row_var is of type my_table%rowtype
PERFORM do_something(row_var);
-- ...
However, I have no idea how to do it using an "ordinary" query, e.g.
SELECT do_something(SELECT * FROM my_table WHERE id = 123);
ERROR: syntax error at or near "SELECT"
LINE 1: SELECT FROM do_something(SELECT * FROM my_table ...
Is there a way to execute such query?
You need to pass a scalar record to that function, this requires to enclose the actual select in another pair of parentheses:
SELECT do_something( (SELECT * FROM my_table WHERE id = 123) );
However the above will NOT work, because the function only expects a single column (a record of type my_table) whereas select * returns multiple columns (which is something different than a single record with multiple fields).
In order to return a record from the select you need to use the following syntax:
SELECT do_something( (SELECT my_table FROM my_table WHERE id = 123) );
Note that this might still fail if you don't make sure the select returns exactly one row.
If you want to apply the function to more than one row, you can do that like this:
select do_something(my_table)
from my_table;

How do I change the formatting for my return values in this function?

I have a function I created like this:
create or replace function core.nrt_summary_by_param_id(int) returns table (reason_type_id int, reason_id int, descr varchar(256), num int, pct numeric, cause_rank int) as $$
begin
return query
with r3 as (
//A bunch of queries
)
select
r3.reason_type_id,
r3.reason_id,
r3.desc,
r3.num,
r3.pct,
r3.cause_rank
from r3;
where
r3.param_id = $1
return;
end
$$ language plpgsql;
When I call the function like so:
select nrt_summary_by_param_id(5);
The results are like this:
nrt_summary_by_param_id
----------------------------------------------------
(1,2001,"A",14,19.72,1)
(1,2006,"B",9,12.68,2)
(1,2202,"C",8,11.27,3)
(1,2002,"D",8,11.27,3)
(1,2302,"E",7,9.86,5)
(1,2201,"F",4,5.63,6)
(1,2206,"G",4,5.63,6)
(1,2301,"H",2,2.82,8)
(1,2303,"I",2,2.82,8)
(1,2005,"J",2,2.82,8)
(1,2004,"K",2,2.82,8)
(1,2204,"L",2,2.82,8)
(,,"M",7,9.87,11)
(13 rows)
How do I get the return from my function as a table with more than one column? How do I get each of the individual columns to show up? I would like the return to be like if the whole query that's inside the function was executed normally from the command line. Thanks!
If your function is return a table then you should treat it like a table. How do we normally look at tables in SQL? We SELECT ... FROM them. You're looking for something more like this:
select ...
from nrt_summary_by_param_id(5);

Oracle and SQLServer function evaluation in queries

Let's say I have a function call on a select or where clause in Oracle like this:
select a, b, c, dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3)
from my_table
A similar example can be constructed for MS SQLServer.
What's the expected behavior in each case?
Is the HASH function going to be called once for each row in the table, or DBMS will be smart enough to call the function just once, since it's a function with constant parameters and no side-effects?
Thanks a lot.
The answer for Oracle is it depends. The function will be called for every row selected UNLESS the Function is marked 'Deterministic' in which case it will only be called once.
CREATE OR REPLACE PACKAGE TestCallCount AS
FUNCTION StringLen(SrcStr VARCHAR) RETURN INTEGER;
FUNCTION StringLen2(SrcStr VARCHAR) RETURN INTEGER DETERMINISTIC;
FUNCTION GetCallCount RETURN INTEGER;
FUNCTION GetCallCount2 RETURN INTEGER;
END TestCallCount;
CREATE OR REPLACE PACKAGE BODY TestCallCount AS
TotalFunctionCalls INTEGER := 0;
TotalFunctionCalls2 INTEGER := 0;
FUNCTION StringLen(SrcStr VARCHAR) RETURN INTEGER AS
BEGIN
TotalFunctionCalls := TotalFunctionCalls + 1;
RETURN Length(SrcStr);
END;
FUNCTION GetCallCount RETURN INTEGER AS
BEGIN
RETURN TotalFunctionCalls;
END;
FUNCTION StringLen2(SrcStr VARCHAR) RETURN INTEGER DETERMINISTIC AS
BEGIN
TotalFunctionCalls2 := TotalFunctionCalls2 + 1;
RETURN Length(SrcStr);
END;
FUNCTION GetCallCount2 RETURN INTEGER AS
BEGIN
RETURN TotalFunctionCalls2;
END;
END TestCallCount;
SELECT a,TestCallCount.StringLen('foo') FROM(
SELECT 0 as a FROM dual
UNION
SELECT 1 as a FROM dual
UNION
SELECT 2 as a FROM dual
);
SELECT TestCallCount.GetCallCount() AS TotalFunctionCalls FROM dual;
Output:
A TESTCALLCOUNT.STRINGLEN('FOO')
---------------------- ------------------------------
0 3
1 3
2 3
3 rows selected
TOTALFUNCTIONCALLS
----------------------
3
1 rows selected
So the StringLen() function was called three times in the first case. Now when executing with StringLen2() which is denoted deterministic:
SELECT a,TestCallCount.StringLen2('foo') from(
select 0 as a from dual
union
select 1 as a from dual
union
select 2 as a from dual
);
SELECT TestCallCount.GetCallCount2() AS TotalFunctionCalls FROM dual;
Results:
A TESTCALLCOUNT.STRINGLEN2('FOO')
---------------------- -------------------------------
0 3
1 3
2 3
3 rows selected
TOTALFUNCTIONCALLS
----------------------
1
1 rows selected
So the StringLen2() function was only called once since it was marked deterministic.
For a function not marked deterministic, you can get around this by modifying your query as such:
select a, b, c, hashed
from my_table
cross join (
select dbms_crypto.hash(utl_raw.cast_to_raw('HELLO'),3) as hashed from dual
);
For SQL server, it will be evaluated for every single row.
You will be MUCH better off by running the function once and assigning to a variable and using the variable in the query.
short answer....it depends.
If the function is accessing data ORACLE does not know if it is going to be the same for each row, therefore, it needs to query for each. If, for example, your function is just a formatter that always returns the same value then you can turn on caching (marking it as Deterministic) which may allow for you to only do the function call once.
Something you may want to look into is ORACLE WITH subquery:
The WITH query_name clause lets you
assign a name to a subquery block. You
can then reference the subquery block
multiple places in the query by
specifying the query name. Oracle
optimizes the query by treating the
query name as either an inline view or
as a temporary table
I got the quoted text from here, which has plenty of examples.