How to implement a function that check a condition - sql

I can't use boolean in a sql query.
Therefore I can't create a function that return true or false and use it to test a condition.
I must create a function that return something (1 for instance) and test it. Like that:
WITH
FUNCTION f (input INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN CASE WHEN input = 1 THEN 1 ELSE 0 END;
END;
A AS (SELECT 1 a FROM DUAL)
SELECT *
FROM a
WHERE f(a.a) = 1
instead of that:
WITH
FUNCTION f (input INTEGER)
RETURN boolean
IS
BEGIN
RETURN input = 1 ;
END;
A AS (SELECT 1 a FROM DUAL)
SELECT *
FROM a
WHERE f(a.a)
Unless there is another way?
code
I've tried to use a macro but to no avail
WITH
FUNCTION ft
RETURN VARCHAR2 SQL_MACRO
IS
BEGIN
RETURN q'{
SELECT 1
FROM dual
}';
END;
FUNCTION fc
RETURN VARCHAR2 SQL_MACRO
IS
BEGIN
RETURN q'{
1=1
}';
END;
SELECT *
FROM ft()
WHERE fc()
ORA-00920: invalid relational operator
code

The BOOLEAN data type is a PL/SQL only data type and is not supported in Oracle SQL statements.
Use two constants:
0 for false, 1 (or non-zero) for true (as per the C language).
0 for no errors, non-zero for errors (as per Unix program exit codes).
'Y' for yes, 'N' for no.
'success' and 'failure'
etc.
Whatever you return is your personal preference. Document the convention you are going to use and then use it consistently so that everyone on the same project uses the same convention.
Unless there is another way?
No, just pick a convention for truthy/falsy values and stick to that.

WITH
FUNCTION f_check_int ( p_str VARCHAR2 )
RETURN VARCHAR2 ------- Y / N
IS
lv_data NUMBER;
BEGIN
lv_data := TO_NUMBER(p_str);
IF lv_data>0 AND MOD(lv_data,1)=0 THEN
RETURN 'Y';
ELSE
RETURN 'N';
END IF;
EXCEPTION
WHEN VALUE_ERROR THEN
RETURN 'N';
END;
A AS (SELECT 1 a FROM DUAL)
SELECT *
FROM a
WHERE f_check_int(a.a) = 'Y'

Related

Scope of column names, aliases, and OUT parameters in PL/pgSQL function

I have a hard time understanding why I can refer to the output columns in returns table(col type).
There is a subtle bug in the below code, the order by var refers to res in returns, not to data1 which we aliased to res. res in where is always null and we get 0 rows.
Why can I refer to the column name in output?
In what cases do I want this?
CREATE OR REPLACE FUNCTION public.test(var INTEGER)
RETURNS table(res int )
LANGUAGE plpgsql
AS $function$
begin
return query
select data1 res
from table_with_data
where res < var;
end
$function$
Why can I refer to the column name in output
From the manual, the section about function parameters:
column_name The name of an output column in the RETURNS TABLE syntax. This is effectively another way of declaring a named OUT parameter, except that RETURNS TABLE also implies RETURNS SETOF.
What this means is that in your case res is effectively a writeable variable, which type you plan to return a set of. As any other variable without a default value assigned, it starts off as null.
In what case do I want this
You can return multiple records from a function of this type with a single return query, but another way is by a series of multiple return query or return next - in the second case, filling out the fields in a record of your output table each time. You could have expected a return statement to end the function, but in this scenario only a single return; without anything added would have that effect.
create table public.test_res (data integer);
CREATE OR REPLACE FUNCTION public.test(var INTEGER)
RETURNS table(res int )
LANGUAGE plpgsql
AS $function$
begin
insert into public.test_res select res;--to inspect its initial value later
select 1 into res;
return next;
return next;--note that res isn't reset after returning next
return query select 2;--doesn't affect the current value of res
return next;--returning something else earlier didn't affect res either
return;--it will finish here
select 3 into res;
return next;
end
$function$;
select * from test(0);
-- res
-------
-- 1
-- 1
-- 2
-- 1
--(4 rows)
table public.test_res; --this was the initial value of res within the function
-- data
--------
-- null
--(1 row)
Which is the most useful with LOOPs
CREATE OR REPLACE FUNCTION public.test(var INTEGER)
RETURNS table(comment text,res int) LANGUAGE plpgsql AS $function$
declare rec record;
array_slice int[];
begin
return query select 'return query returned these multiple records in one go', a from generate_series(1,3,1) a(a);
res:=0;
comment:='loop exit when res>4';
loop exit when res>4;
select res+1 into res;
return next;
end loop;
comment:='while res between 5 and 8 loop';
while res between 5 and 8 loop
select res+2 into res;
return next;
end loop;
comment:='for element in reverse 3 .. -3 by 2 loop';
for element in reverse 3 .. -3 by 2 loop
select element into res;
return next;
end loop;
comment:='for <record> in <expression> loop';
for rec in select pid from pg_stat_activity where state<>'idle' loop
select rec.pid into res;
return next;
end loop;
comment:='foreach array_slice slice 1 in array arr loop';
foreach array_slice SLICE 1 in array ARRAY[[1,2,3],[11,12,13],[21,22,23]] loop
select array_slice[1] into res;
return next;
end loop;
end
$function$;
Example results
select * from public.test(0);
-- comment | res
----------------------------------------------------------+--------
-- return query returned these multiple records in one go | 1
-- return query returned these multiple records in one go | 2
-- return query returned these multiple records in one go | 3
-- loop exit when res>4 | 1
-- loop exit when res>4 | 2
-- loop exit when res>4 | 3
-- loop exit when res>4 | 4
-- loop exit when res>4 | 5
-- while res between 5 and 8 loop | 7
-- while res between 5 and 8 loop | 9
-- for element in reverse 3 .. -3 by 2 loop | 3
-- for element in reverse 3 .. -3 by 2 loop | 1
-- for element in reverse 3 .. -3 by 2 loop | -1
-- for element in reverse 3 .. -3 by 2 loop | -3
-- for <record> in <expression> loop | 118786
-- foreach array_slice slice 1 in array arr loop | 1
-- foreach array_slice slice 1 in array arr loop | 11
-- foreach array_slice slice 1 in array arr loop | 21
--(18 rows)
True, OUT parameters (including field names in a RETURNS TABLE (...) clause) are visible in all SQL DML statements in a PL/pgSQL function body, just like other variables. Find details in the manual chapters Variable Substitution and Returning from a Function for PL/pgSQL.
However, a more fundamental misunderstanding comes first here. The syntax of your nested SELECT is invalid to begin with. The PL/pgSQL variable happens to mask this problem (with a different problem). In SQL, you cannot refer to output column names (column aliases in the SELECT clause) in the WHERE clause. This is invalid:
select data1 res
from table_with_data
where res < var;
The manual:
An output column's name can be used to refer to the column's value in
ORDER BY and GROUP BY clauses, but not in the WHERE or HAVING clauses;
there you must write out the expression instead.
This is different for ORDER BY, which you mention in the text, but don't include in the query. See:
GROUP BY + CASE statement
Fixing immediate issue
Could be repaired like this:
CREATE OR REPLACE FUNCTION public.test1(var int)
RETURNS TABLE(res int)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT data1 AS res -- column alias is just noise (or documentation)
FROM table_with_data
WHERE data1 < var; -- original column name!
END
$func$
fiddle
See:
Real number comparison for trigram similarity
The column alias is just noise in this case. The name of the column returned from the function is res in any case - as defined in the RETURNS TABLE clause.
Aside: It's recommended not to omit the AS keyword for column aliases (unlike table aliases). See:
Query to ORDER BY the number of rows returned from another SELECT
If there was actual ambiguity between column and variable name - say, you declared an OUT parameter or variable named data1 - you'd get an error message like this:
ERROR: column reference "data1" is ambiguous
LINE 2: select data1
^
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
Brute force fix
Could be fixed with a special command at the start of the function body:
CREATE OR REPLACE FUNCTION public.test3(var int)
RETURNS TABLE(data1 int)
LANGUAGE plpgsql AS
$func$
#variable_conflict use_column -- ! to resolve conflicts
BEGIN
RETURN QUERY
SELECT data1
FROM table_with_data
WHERE data1 < var; -- !
END
$func$
See:
Naming conflict between function parameter and result of JOIN with USING clause
Proper fix
Table-qualify column names, and avoid conflicting variable names to begin with.
CREATE OR REPLACE FUNCTION public.test4(_var int)
RETURNS TABLE(res int)
LANGUAGE plpgsql STABLE AS
$func$
BEGIN
RETURN QUERY
SELECT t.data1 -- table-qualify column name
FROM table_with_data t
WHERE t.data1 < _var; -- !
END
$func$
Example:
Calling a PostgreSQL function from Java

Oracle. Not valid ascii value of regex result

I'd like to edit a string. Get from 2 standing nearby digits digit and letter (00 -> 0a, 01 - 0b, 23-> 2c etc.)
111324 -> 1b1d2e.
Then my code:
set serveroutput on size unlimited
declare
str varchar2(128);
function convr(num varchar2) return varchar2 is
begin
return chr(ascii(num)+49);
-- return chr(ascii(num)+49)||'<-'||(ascii(num)+49)||','||ascii(num)||','||num||'|';
end;
function replace_dd(str varchar2) return varchar2 is
begin
return regexp_replace(str,'((\d)(\d))','\2'||convr('\3'));
end;
begin
str := '111324';
Dbms_Output.Put_Line(str);
Dbms_Output.Put_Line(replace_dd(str));
end;
But I get the next string: '112'.
When I checked result by commented return string I'v got:
'1<-141,92,1|1<-141,92,3|2<-141,92,4|'.
ascii(num) does not depend on num. It always works like ascii('\'). It is 92, plus 49 we got 141 and it is out of ascii table. But num by itself is printed correctly.
How can I get correct values? Or maybe another way to resolve this issue?
What is happening is that the replacement string is expanded first, and only after it is fully processed, any remaining backreferences like \2 are replaced by string fragments. So convr('\3') is processed first, and at this stage '\3' is a literal. ascii() returns the ascii code of the FIRST character of whatever string it receives as argument. So the 3 plays no role, you only get ascii('\') as you noticed. Then your user-defined function is evaluated and plugged back into the concatenation... by now there is no \3 left in the replacement string.
Exercise: Try to explain/understand why
regexp_replace('abcdef', '(b).*(e)', '\2' || upper('\1'))
is aebf and not aeBf. (Hint: what is the return from upper('\1') by itself, unrelated to anything else?)
You could split the input string into component characters, apply your transformation on those with even index and combine the string back (all in SQL, no need for loops and such). Something like this (done in plain SQL, you can rewrite it into your function if you like):
with
inputs ( str ) as (
select '111324' from dual union all
select '372' from dual
),
singletons ( str, idx, ch ) as (
select str, level, substr(str, level, 1)
from inputs
connect by level <= length(str)
and prior str = str
and prior sys_guid() is not null
)
select str,
listagg(case mod(idx, 2) when 1 then ch else chr(ascii(ch)+49) end, '')
within group (order by idx)
as modified_str
from singletons
group by str
;
STR MODIFIED_STR
------ --------------
111324 1b1d2e
372 3h2
Here code adds 5 to a single letter and resolve the isssue.
set serveroutput on size unlimited
declare
str varchar2(128);
str1 varchar2(128);
function replace_a(str varchar2) return varchar2 is
begin
return regexp_replace(str,'(\D)','5\1');
end;
function convr(str varchar2) return varchar2 is
ind number;
ret varchar2(128);
begin
Dbms_Output.Put_Line(str);
--return chr(ascii(num)+49)||'<-'||(ascii(num)+49)||','||ascii(num)||','||num||'|';
ind := 1 ;
ret :=str;
loop
ind := regexp_instr(':'||ret,'(#\d#)',ind) ;
exit when ind=0;
Dbms_Output.Put_Line(ind);
ret := substr(ret,1,ind-2)||chr(ascii(substr(ret,ind,1))+49)||substr(ret,ind+2);
SYS.Dbms_Output.Put_Line(ret);
end loop;
return ret;
end;
function replace_dd(str varchar2) return varchar2 is
begin
return convr(regexp_replace(str,'((\d)(\d))','\2#\3#'));
end;
begin
str := '11a34';
Dbms_Output.Put_Line(str);
Dbms_Output.Put_Line(replace_a(str));
Dbms_Output.Put_Line(replace_dd(replace_a(str)));
end;
result:
11a34
115a34
1#1#5a3#4#
3
1b5a3#4#
7
1b5a3e
1b5a3e

Oracle PL/SQL string compare issue

I have the following Oracle PL/SQL codes that may be rusty from you guys perspective:
DECLARE
str1 varchar2(4000);
str2 varchar2(4000);
BEGIN
str1:='';
str2:='sdd';
IF(str1<>str2) THEN
dbms_output.put_line('The two strings is not equal');
END IF;
END;
/
This is very obvious that two strings str1 and str2 are not equal, but why 'The two strings are not equal' was not printed out? Do Oracle have another common method to compare two string?
As Phil noted, the empty string is treated as a NULL, and NULL is not equal or unequal to anything. If you expect empty strings or NULLs, you'll need to handle those with NVL():
DECLARE
str1 varchar2(4000);
str2 varchar2(4000);
BEGIN
str1:='';
str2:='sdd';
-- Provide an alternate null value that does not exist in your data:
IF(NVL(str1,'X') != NVL(str2,'Y')) THEN
dbms_output.put_line('The two strings are not equal');
END IF;
END;
/
Concerning null comparisons:
According to the Oracle 12c documentation on NULLS, null comparisons using IS NULL or IS NOT NULL do evaluate to TRUE or FALSE. However, all other comparisons evaluate to UNKNOWN, not FALSE. The documentation further states:
A condition that evaluates to UNKNOWN acts almost like FALSE. For example, a SELECT statement with a condition in the WHERE clause that evaluates to UNKNOWN returns no rows. However, a condition evaluating to UNKNOWN differs from FALSE in that further operations on an UNKNOWN condition evaluation will evaluate to UNKNOWN. Thus, NOT FALSE evaluates to TRUE, but NOT UNKNOWN evaluates to UNKNOWN.
A reference table is provided by Oracle:
Condition Value of A Evaluation
----------------------------------------
a IS NULL 10 FALSE
a IS NOT NULL 10 TRUE
a IS NULL NULL TRUE
a IS NOT NULL NULL FALSE
a = NULL 10 UNKNOWN
a != NULL 10 UNKNOWN
a = NULL NULL UNKNOWN
a != NULL NULL UNKNOWN
a = 10 NULL UNKNOWN
a != 10 NULL UNKNOWN
I also learned that we should not write PL/SQL assuming empty strings will always evaluate as NULL:
Oracle Database currently treats a character value with a length of zero as null. However, this may not continue to be true in future releases, and Oracle recommends that you do not treat empty strings the same as nulls.
Let's fill in the gaps in your code, by adding the other branches in the logic, and see what happens:
SQL> DECLARE
2 str1 varchar2(4000);
3 str2 varchar2(4000);
4 BEGIN
5 str1:='';
6 str2:='sdd';
7 IF(str1<>str2) THEN
8 dbms_output.put_line('The two strings is not equal');
9 ELSIF (str1=str2) THEN
10 dbms_output.put_line('The two strings are the same');
11 ELSE
12 dbms_output.put_line('Who knows?');
13 END IF;
14 END;
15 /
Who knows?
PL/SQL procedure successfully completed.
SQL>
So the two strings are neither the same nor are they not the same? Huh?
It comes down to this. Oracle treats an empty string as a NULL. If we attempt to compare a NULL and another string the outcome is not TRUE nor FALSE, it is NULL. This remains the case even if the other string is also a NULL.
I compare strings using = and not <>. I've found out that in this context = seems to work in more reasonable fashion than <>. I have specified that two empty (or NULL) strings are equal. The real implementation returns PL/SQL boolean, but here I changed that to pls_integer (0 is false and 1 is true) to be able easily demonstrate the function.
create or replace function is_equal(a in varchar2, b in varchar2)
return pls_integer as
begin
if a is null and b is null then
return 1;
end if;
if a = b then
return 1;
end if;
return 0;
end;
/
show errors
begin
/* Prints 0 */
dbms_output.put_line(is_equal('AAA', 'BBB'));
dbms_output.put_line(is_equal('AAA', null));
dbms_output.put_line(is_equal(null, 'BBB'));
dbms_output.put_line(is_equal('AAA', ''));
dbms_output.put_line(is_equal('', 'BBB'));
/* Prints 1 */
dbms_output.put_line(is_equal(null, null));
dbms_output.put_line(is_equal(null, ''));
dbms_output.put_line(is_equal('', ''));
dbms_output.put_line(is_equal('AAA', 'AAA'));
end;
/
To fix the core question, "how should I detect that these two variables don't have the same value when one of them is null?", I don't like the approach of nvl(my_column, 'some value that will never, ever, ever appear in the data and I can be absolutely sure of that') because you can't always guarantee that a value won't appear... especially with NUMBERs.
I have used the following:
if (str1 is null) <> (str2 is null) or str1 <> str2 then
dbms_output.put_line('not equal');
end if;
Disclaimer: I am not an Oracle wizard and I came up with this one myself and have not seen it elsewhere, so there may be some subtle reason why it's a bad idea. But it does avoid the trap mentioned by APC, that comparing a null to something else gives neither TRUE nor FALSE but NULL. Because the clauses (str1 is null) will always return TRUE or FALSE, never null.
(Note that PL/SQL performs short-circuit evaluation, as noted here.)
I've created a stored function for this text comparison purpose:
CREATE OR REPLACE FUNCTION TextCompare(vOperand1 IN VARCHAR2, vOperator IN VARCHAR2, vOperand2 IN VARCHAR2) RETURN NUMBER DETERMINISTIC AS
BEGIN
IF vOperator = '=' THEN
RETURN CASE WHEN vOperand1 = vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END;
ELSIF vOperator = '<>' THEN
RETURN CASE WHEN vOperand1 <> vOperand2 OR (vOperand1 IS NULL) <> (vOperand2 IS NULL) THEN 1 ELSE 0 END;
ELSIF vOperator = '<=' THEN
RETURN CASE WHEN vOperand1 <= vOperand2 OR vOperand1 IS NULL THEN 1 ELSE 0 END;
ELSIF vOperator = '>=' THEN
RETURN CASE WHEN vOperand1 >= vOperand2 OR vOperand2 IS NULL THEN 1 ELSE 0 END;
ELSIF vOperator = '<' THEN
RETURN CASE WHEN vOperand1 < vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NOT NULL THEN 1 ELSE 0 END;
ELSIF vOperator = '>' THEN
RETURN CASE WHEN vOperand1 > vOperand2 OR vOperand1 IS NOT NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END;
ELSIF vOperator = 'LIKE' THEN
RETURN CASE WHEN vOperand1 LIKE vOperand2 OR vOperand1 IS NULL AND vOperand2 IS NULL THEN 1 ELSE 0 END;
ELSIF vOperator = 'NOT LIKE' THEN
RETURN CASE WHEN vOperand1 NOT LIKE vOperand2 OR (vOperand1 IS NULL) <> (vOperand2 IS NULL) THEN 1 ELSE 0 END;
ELSE
RAISE VALUE_ERROR;
END IF;
END;
In example:
SELECT * FROM MyTable WHERE TextCompare(MyTable.a, '>=', MyTable.b) = 1;
Only change the line
str1:='';
to
str1:=' ';
The '' would be treated as NULL, so, both the strings need to be checked as NULL.
Function:
CREATE OR REPLACE FUNCTION str_cmpr_fnc(str_val1_in IN VARCHAR2, str_val2_in IN VARCHAR2) RETURN VARCHAR2
AS
l_result VARCHAR2(50);
BEGIN
-- string comparison
CASE
WHEN str_val1_in IS NULL AND str_val2_in IS NULL THEN
l_result := 'Both Unknown';
WHEN str_val1_in IS NULL THEN
l_result := 'Str1 Unknown';
WHEN str_val2_in IS NULL THEN
l_result := 'Str2 Unknown';
ELSE
CASE
WHEN str_val1_in = str_val2_in THEN
l_result := 'Both are equel';
ELSE
l_result := 'Both strings are not equal';
END CASE;
END CASE;
-- return result
RETURN l_result;
EXCEPTION
WHEN OTHERS THEN
-- set serveroutput on to get the error information
DBMS_OUTPUT.put_line(SQLERRM||' ,'|| DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
-- return result
RETURN l_result;
END str_cmpr_fnc;
Sql Statement:
SELECT str_cmpr_fnc('7', 'd') FROM DUAL;
To the first question:
Probably the message wasn't print out because you have the output turned off. Use these commands to turn it back on:
set serveroutput on
exec dbms_output.enable(1000000);
On the second question:
My PLSQL is quite rusty so I can't give you a full snippet, but you'll need to loop over the result set of the SQL query and CONCAT all the strings together.

Intersection of multiple arrays in PostgreSQL

I have a view defined as:
CREATE VIEW View1 AS
SELECT Field1, Field2, array_agg(Field3) AS AggField
FROM Table1
GROUP BY Field1, Field2;
What I would like to do is get the intersection of the arrays in AggField with something like:
SELECT intersection(AggField) FROM View1 WHERE Field2 = 'SomeValue';
Is this at all possible, or is there a better way to achieve what I want?
The closest thing to an array intersection that I can think of is this:
select array_agg(e)
from (
select unnest(a1)
intersect
select unnest(a2)
) as dt(e)
This assumes that a1 and a2 are single dimension arrays with the same type of elements. You could wrap that up in a function something like this:
create function array_intersect(a1 int[], a2 int[]) returns int[] as $$
declare
ret int[];
begin
-- The reason for the kludgy NULL handling comes later.
if a1 is null then
return a2;
elseif a2 is null then
return a1;
end if;
select array_agg(e) into ret
from (
select unnest(a1)
intersect
select unnest(a2)
) as dt(e);
return ret;
end;
$$ language plpgsql;
Then you could do things like this:
=> select array_intersect(ARRAY[2,4,6,8,10], ARRAY[1,2,3,4,5,6,7,8,9,10]);
array_intersect
-----------------
{6,2,4,10,8}
(1 row)
Note that this doesn't guarantee any particular order in the returned array but you can fix that if you care about it. Then you could create your own aggregate function:
-- Pre-9.1
create aggregate array_intersect_agg(
sfunc = array_intersect,
basetype = int[],
stype = int[],
initcond = NULL
);
-- 9.1+ (AFAIK, I don't have 9.1 handy at the moment
-- see the comments below.
create aggregate array_intersect_agg(int[]) (
sfunc = array_intersect,
stype = int[]
);
And now we see why array_intersect does funny and somewhat kludgey things with NULLs. We need an initial value for the aggregation that behaves like the universal set and we can use NULL for that (yes, this smells a bit off but I can't think of anything better off the top of my head).
Once all this is in place, you can do things like this:
> select * from stuff;
a
---------
{1,2,3}
{1,2,3}
{3,4,5}
(3 rows)
> select array_intersect_agg(a) from stuff;
array_intersect_agg
---------------------
{3}
(1 row)
Not exactly simple or efficient but maybe a reasonable starting point and better than nothing at all.
Useful references:
array_agg
create aggregate
create function
PL/pgSQL
unnest
The accepted answer did not work for me. This is how I fixed it.
create or replace function array_intersect(a1 int[], a2 int[]) returns int[] as $$
declare
ret int[];
begin
-- RAISE NOTICE 'a1 = %', a1;
-- RAISE NOTICE 'a2 = %', a2;
if a1 is null then
-- RAISE NOTICE 'a1 is null';
return a2;
-- elseif a2 is null then
-- RAISE NOTICE 'a2 is null';
-- return a1;
end if;
if array_length(a1,1) = 0 then
return '{}'::integer[];
end if;
select array_agg(e) into ret
from (
select unnest(a1)
intersect
select unnest(a2)
) as dt(e);
if ret is null then
return '{}'::integer[];
end if;
return ret;
end;
$$ language plpgsql;
It is bit late to answer this question but maybe somebody will need it so I decided to share something I wrote cause did not found any ready solution for intersection of any number of arrays. So here it is. This function receives array of arrays, if it is only single array, function returns first array, if there are 2 arrays function intersects 2 arrays and returns result, if it is more that 2 arrays, function takes intersection of 2 first arrays, stores it in some variable and loops through all other arrays, intersect each next array with stored result and stores result in variable. if result is null it exists with null. In the and the variable that stores array with interacted data returned from the function.
CREATE OR REPLACE FUNCTION array_intersected(iarray bigint[][])
RETURNS bigint[] AS
$BODY$
declare out_arr bigint[]; set1 bigint[]; set2 bigint[];
BEGIN
--RAISE NOTICE '%', array_length(iarray, 1);
if array_length(iarray, 1) = 1 then
SELECT ARRAY(SELECT unnest(iarray[1:1])) into out_arr;
elseif array_length( iarray, 1) = 2 then
set1 := iarray[1:1];
set2 := iarray[2:2];
SELECT ARRAY(SELECT unnest(set1) INTERSECT SELECT unnest(set2))into out_arr;
elseif array_length(iarray, 1) > 2 then
set1 := iarray[1:1];
set2 := iarray[2:2];
--exit if no common numbers exists int 2 first arrays
SELECT ARRAY(SELECT unnest(set1) INTERSECT SELECT unnest(set2))into out_arr;
if out_arr = NULL then
EXIT;
END IF;
FOR i IN 3 .. array_upper(iarray, 1)
LOOP
set1 := iarray[i:i];
SELECT ARRAY(SELECT unnest(set1) INTERSECT SELECT unnest(out_arr))into out_arr;
if out_arr = NULL then
EXIT;
END IF;
END LOOP;
end if;
return out_arr;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Here is the code to validate it works.
select array_intersected(array[[1, 2]]::bigint[][]);
select array_intersected(array[[1, 2],[2, 3]]::bigint[][]);
select array_intersected(array[[1, 2],[2, 3], [2, 4]]::bigint[][]);
select array_intersected(array[[1, 2, 3, 4],[null, null, 4, 3], [3, 1, 4, null]]::bigint[][]);

Postgres nested if in case query

Could you tell my why the following isnt working in postgres sql?:
See updated code below
UPDATE:
I expect the query to return "0.30" as float.
This construct is only for testing purposes, i have some complex querys which depend on this conditional structure... BUt i dont know how to fix it..
Result is:
ERROR: syntax error at or near "1"
LINE 4: if 1=1 then
UPDATE:
This construction appears in a function... so I want to do following:
CREATE FUNCTION f_test(myvalue integer) RETURNS float AS $$
BEGIN
select (
case (select '1')
when '1' then
if 1=1 then
0.30::float
else
0.50::float
end
else
1.00::float
end
);
END;
$$ LANGUAGE plpgsql;
select f_test(1) as test;
Error message see above.
There is no IF expr THEN result ELSE result END syntax for normal SQL queries in Postgres. As there is neither an IF() function as in MySQL, you have to use CASE:
select (
case (select '1')
when '1' then
case when 1=1 then 0.30::float else 0.50::float end
else
1.00::float
end
);
I don't know what you're trying to achieve with this function, but here's a working version.
CREATE FUNCTION f_test(myvalue integer) RETURNS float AS $$
BEGIN
IF myvalue = 1 THEN
IF 1=1 THEN
RETURN 0.30::FLOAT;
ELSE
RETURN 0.50::FLOAT;
END IF;
ELSE
RETURN 1.0::FLOAT;
END IF;
END;
The function returns 0.3 if input value is 1, otherwise it'll return 1. Edit: Note that 0.5 is never returned by the function.