PL/pgSQL How To Create Test Query To Test Function - postgresql

I create a function that returns TEXT, end I'm trying to create a simple query to test this function.
the query while looks like this:
CREATE TEMP TABLE function_test(actuel TEXT,expected TEXT,t_result TEXT);
/* the t_result should be'passed' if function(actuel) = expected */
INSERT INTO
function_test(actuel ,expected);
VALUES
('a','A'),/*function(`a`) return 'A'*/
('b','B'),/*function(`b`) return 'B'*/
('c','C');/*function(`c`) return 'C'*/
IF function(actuel)=expected THEN
INSERT INTO
function_test(t_result) VALUES 'passed' ;
ELSE
INSERT INTO
function_test(t_result) VALUES 'failed';
SELECT * FROM function_test;
DROP TABLE function_test;
Output
It would be nice if I could do it better than this.
thinks.

Not entirely sure I understand what you are asking, but could you not just do either
select MY_FUNCTION('a','A') from dual;
or if you need a row on success and no row on failure
select 'PASS' from dual
where MY_FUNCTION('a','A') = 'YES'
DUAL is a 1 one row table that exists in every Oracle database.

Related

Passing a column as a function parameter that creates a table

I need to create a function that will generate a table.
This table will have columns brought with several left joins.
At some point i need to filter the data based on the value of a dynamic column (i need to have: WHERE table_name.dynamic_column_name = 1)(that column has 1s and 0s, i need to keep only the 1s)
So when I 'call' the function, user should type like this: SELECT * FROM function_name(dynamic_column_name)
What i did:
CREATE OR REPLACE FUNCTION check_gap (_col_name varchar)
RETRUNS TABLE ( ... here i have several columns with their type ...)
LANGUAGE plpsql
AS $function$
BEGIN
RETURN QUERY
SELECT ... a bunch of columns ...
FROM ... a bunch of tables left joined ...
WHERE _col_name = 1;
END;
$function$
;
I even tried with table_name._col_name .. though it wasn't necessary, the query (select from) works just as fine without
** I found some solutions for dynamic value but not for a dynamic column
*** I am using PostgreSQL (from DBeaver)
You need dynamic SQL for that. Pls. note that the code below is SQL injection prone.
CREATE OR REPLACE FUNCTION check_gap (col_name varchar)
RETURNS TABLE (... several columns with their type ...) LANGUAGE plpgsql AS
$$
BEGIN
RETURN QUERY EXECUTE format
(
'SELECT ... a bunch of columns ...
FROM ... a bunch of tables left joined ...
WHERE %I = 1;', col_name
);
END;
$$;

Executing query as string variable and storing in a table

I have a simple code that works as follows. I have a table A and I want to query it and store the result in a new table B. For simplicity assume qty and price are two of the fields of the table A. My simple sql file contains the following and works just fine.
DROP TABLE IF EXISTS B;
CREATE TABLE B AS
SELECT *
FROM A
WHERE A.qty >10
Now I want to dynamically modify the query by representing it as a string variable (say s) and updating the string s on the fly. Suppose the string variable s is originally
s = 'SELECT * FROM A WHERE A.qty>10'
we then update it on the fly by concatenating another string as follows:
s = 'SELECT * FROM A WHERE A.qty>10 AND A.price >30'
I want to execute s as a query and save the results in Table B.
I read dynamic sql related documents of postgresql and not quite sure how to achieve the above goal. I am novice and any help will be greatly appreciated.
You can write a function like this and pass the condition as parameter:
create or replace function add_table_b(s text) returns int as $$
declare
rc int:=0;
query_ text:='';
begin
if(trim(s)= '') then
s='';
else
s=' where '||s;
end if;
DROP TABLE IF EXISTS B;
execute 'CREATE TABLE B AS SELECT * FROM A '|| s;
GET DIAGNOSTICS rc = ROW_COUNT;
return rc;
end;
$$
language plpgsql
this function will return the count of rows inserted in table B.

Postgresql 8.4 a variable hold set of records returned from another function

I am using postgresql 8.4 in backend
I have a postgres function say A() it can return a set of records (3 columns) like:
<A_id>::int,<A_ts_1>::timestamp,<A_ts_2>::timestamp
function A define like this(for example):
CREATE OR REPLACE FUNCTION A()
RETURNS SETOF record AS
$$
DECLARE
BEGIN
RETURN QUERY SELECT DISTINCT ON (A.id) A.id, A.ts_1, A.ts_2 FROM tablea;
END;
$$ LANGUAGE plpgsql;
SQL
function A has been called in another function B. In function B I need a variable to hold what returned from A() then do some query for example:
<variable> = select * from A();
a_id_array = ARRAY(select A_id from <variable>);
a_filtered_array = ARRAY(select A_id from <variable> where A_ts_1 ><a_timestamp> and A_ts_2 < <a_timestamp>);
So My question is what variable I should define to hold the set of records returned from A().
I tried temp table which really not good for multi-session env, it blocks data insertion. postgresql create temp table could block data insertion?
I checked doc for views seems not meet my requirements, however I may wrong so if any of you could give me an idea on how to use view in this case and use view will block data insertion as well?
Thank all!
P.S.
I think the worse case is in function B() I call function A() twice for example:
a_id_array = ARRAY(select A_id from A());
a_filtered_array = ARRAY(select A_id from A() where A_ts_1 ><a_timestamp> and A_ts_2 < <a_timestamp>);
Then my question would slightly change, can I achive this case just using one function call to A()?
PostgreSQL doesn't (yet, as of postgres 10) have table-valued variables backed by a tuplestore. So your best options are:
Return a REFCURSOR and use it from the other function. Can be clumsy to work with as you cannot reuse the resultset easily or FETCH in a subquery. It's not always easy to generate a cursor resultset either, depending on how you're creating the results.
Use temp tables with generated names so they don't collide. Lots of dynamic SQL involved here (EXECUTE format(...)) but it works.
Avoid trying to pass result sets between functions
After researching, found a way to replace using temp table and query returned set of record which is using WITH query.
SELECT c.r_ids, c.a_r_ids into a_id_array, a_filtered_array FROM(
WITH returned_r AS (SELECT * FROM a())
SELECT * from (
SELECT ARRAY( SELECT A_id from returned_r ) as r_ids ) as a
CROSS JOIN (
SELECT ARRAY(SELECT A_id FROM returned_r WHERE A_ts_1 is NOT NULL AND A_ts_2 IS NULL) as a_r_ids
) as b
) as c;

How to work with more than one output parameter in single stored procedure

I have a SP with an Output parameter that looks like:
ALTER PROCEDURE [dbo].[SP_Name] #VarName decimal(18,2) OUTPUT as ...
I call that procedure from vb.net to get the value for calculations. My problem is: I have 8 SP's with the following structure:
CREATE PROCEDURE [dbo].[SP_Name] #VarName decimal(18,2) OUTPUT as ...
CREATE TABLE #TempTable
Begin
Select ...
End
SET #VarName = Result
But the TempTable is always the same. No I am looking for a way to get all 8 values with only one stored procedure. My idea:
CREATE PROCEDURE [dbo].[SP_Name] #VarName decimal(18,2) OUTPUT as ...
CREATE TABLE #TempTable
---Get first value
Begin
Select ...
End
SET #VarName1 = Result
---Get second value
Begin
Select ...
End
SET #VarName2 = Result
...
How do i have to rewrite the line: ALTER PROCEDURE [dbo].[SP_Name] #VarName decimal(18,2) OUTPUT ir can I even work with an array?
You can use a single stored procedure with all your queries in it. Following will return a single row result set with eight fields and you can grab them from your code using the specific filed name or index.
CREATE PROCEDURE [dbo].[SP_Name]
#VarName decimal(18,2)
AS
BEGIN
DECLARE #VarName1 Datatype, #VarName2 Datatype, ...#VarName8 Datatype
SELECT #VarName1 = yourCol
FROM --First query
SELECT #VarName2 = yourCol
FROM --Second query
...
SELECT #VarName8 = yourCol
FROM --Eighth query
--Finally Select all the variables
SELECT #VarName1 Col1, #VarName2 Col2, ...,#VarName8 Col8
END
OR if you are looking to return results of your all 8 queries, that is also possible. Simply do your select queries in a single stored procedure and grab the DATASET from your code and you can access individual table using zero based Index (ex DataTable1 = YourDataSet.Tables[0])

Union different select statement

I want to union 3 different statements, the problem is that I am using a loop and if in my query:
WHILE some condition
BEGIN
if(condition 1)
begin
select something
end
else if(condition 2)
begin
select something
end
else if(condition 3)
begin
select something
end
END
The query is working fine but it returns more than 100 different select results (distinct tables). How can I union these select results into one table?
If you mean in TSQL, then you need a temporary table (#foo) or table variable (#bar). Then at each step:
INSERT {name} ({cols})
SELECT {cols}
{etc}
Then do a final
SELECT {cols}
FROM {name}
The choice between table-variable and temporary-table is subtle. I prefer table-variables; but for large data, or if you need an identity/index, a temporary-table may be more versatile. But if you aren't inside a SPROC you must ensure you clean up your temp table.