How to create a user defined function such as ISNULL? Parameter is Expression and return type is the type of replace type - sql-server

How can I create a user defined function that behaves in a similar manner to the builtin ISNULL in SQLServer2017?
ISNULL ( check_expression , replacement_value )
Arguments
check_expression Is the expression to be checked for NULL.
check_expression can be of any type.
Return Types Returns the same type as check_expression.
How can I create my own functions with this behaviour?

I understand what you want to do - and this isn't possible.
You can't create a single user defined function dbo.Foo that returns an int when passed an int and a varchar when passed a varchar for example.
You can use sql_variant as the type of the input parameter and the return type but this isn't really the same.

Related

Postgres: calling function with text[] param fails with array literal

I have a Postgres function that accepts a text[] as input. For example
create function temp1(player_ids text[])
returns void
language plpgsql
as
$$
begin
update players set player_xp = 0
where id in (player_ids);
-- the body is actually 20 lines long, updating a lot of tables
end;
$$;
and I'm trying to call it, but I keep getting
[42883] ERROR: operator does not exist: text = text[] Hint: No operator matches the given name and argument types. You might need to add explicit type casts. Where: PL/pgSQL function temp1(text[]) line 3 at SQL statement
I have tried these so far
select temp1('{F7AWLJWYQ5BMPKGXLMDNQKQ4NY,AQPBAFKQONGLBKIMCSOD747GY4}');
select temp1('{F7AWLJWYQ5BMPKGXLMDNQKQ4NY,AQPBAFKQONGLBKIMCSOD747GY4}'::text[]);
select temp1(array['F7AWLJWYQ5BMPKGXLMDNQKQ4NY,AQPBAFKQONGLBKIMCSOD747GY4']);
select temp1(array['F7AWLJWYQ5BMPKGXLMDNQKQ4NY,AQPBAFKQONGLBKIMCSOD747GY4']::text[]);
I have to be missing something obvious...how do I call this function with an array literal?
Use = any instead of in:
...
update players set player_xp = 0
where id = any(player_ids);
...
The IN operator acts on an explicit list of values.
expression IN (value [, ...])
When you want to compare a value to each element of an array, use ANY instead.
expression operator ANY (array expression)
Note that there are variants of both constructs for subqueries expression IN (subquery) and expression operator ANY (subquery). The first one was properly used in the other answer though a subquery seems excessive in this case.
You can use unnest function, this function is very easy and same time best performanced. Unnest using for converting array elements to rows. Example:
create function temp1(player_ids text[])
returns void
language plpgsql
as
$$
begin
update players set player_xp = 0
where id in (select pl.id from unnest(player_ids) as pl(id));
-- the body is actually 20 lines long, updating a lot of tables
end;
$$;
And you can easily cast array elements to another type for using unnest.
Example:
update players set player_xp = 0
where id in (select pl.id::integer from unnest(player_ids) as pl(id));

PostgreSQL C aggregate function: How to return multiple values in transition function [duplicate]

Is the only way to pass an extra parameter to the final function of a PostgreSQL aggregate to create a special TYPE for the state value?
e.g.:
CREATE TYPE geomvaltext AS (
geom public.geometry,
val double precision,
txt text
);
And then to use this type as the state variable so that the third parameter (text) finally reaches the final function?
Why aggregates can't pass extra parameters to the final function themselves? Any implementation reason?
So we could easily construct, for example, aggregates taking a method:
SELECT ST_MyAgg(accum_number, 'COMPUTE_METHOD') FROM blablabla
Thanks
You can define an aggregate with more than one parameter.
I don't know if that solves your problem, but you could use it like this:
CREATE OR REPLACE FUNCTION myaggsfunc(integer, integer, text) RETURNS integer
IMMUTABLE STRICT LANGUAGE sql AS
$f$
SELECT CASE $3
WHEN '+' THEN $1 + $2
WHEN '*' THEN $1 * $2
ELSE NULL
END
$f$;
CREATE AGGREGATE myagg(integer, text) (
SFUNC = myaggsfunc(integer, integer, text),
STYPE = integer
);
It could be used like this:
CREATE TABLE mytab
AS SELECT * FROM generate_series(1, 10) i;
SELECT myagg(i, '+') FROM mytab;
myagg
-------
55
(1 row)
SELECT myagg(i, '*') FROM mytab;
myagg
---------
3628800
(1 row)
I solved a similar issue by making a custom aggregate function that did all the operations at once and stored their states in an array.
CREATE AGGREGATE myagg(integer)
(
INITCOND = '{ 0, 1 }',
STYPE = integer[],
SFUNC = myaggsfunc
);
and:
CREATE OR REPLACE FUNCTION myaggsfunc(agg_state integer[], agg_next integer)
RETURNS integer[] IMMUTABLE STRICT LANGUAGE 'plpgsql' AS $$
BEGIN
agg_state[1] := agg_state[1] + agg_next;
agg_state[2] := agg_state[2] * agg_next;
RETURN agg_state;
END;
$$;
Then made another function that selected one of the results based on the second argument:
CREATE OR REPLACE FUNCTION myagg_pick(agg_state integer[], agg_fn character varying)
RETURNS integer IMMUTABLE STRICT LANGUAGE 'plpgsql' AS $$
BEGIN
CASE agg_fn
WHEN '+' THEN RETURN agg_state[1];
WHEN '*' THEN RETURN agg_state[2];
ELSE RETURN 0;
END CASE;
END;
$$;
Usage:
SELECT myagg_pick(myagg("accum_number"), 'COMPUTE_METHOD') FROM "mytable" GROUP BY ...
Obvious downside of this is the overhead of performing all the functions instead of just one. However when dealing with simple operations such as adding, multiplying etc. it should be acceptable in most cases.
You would have to rewrite the final function itself, and in that case you might as well write a set of new aggregate functions, one for each possible COMPUTE_METHOD. If the COMPUTE_METHOD is a data value or implied by a data value, then a CASE statement can be used to select the appropriate aggregate method. Alternatively, you may want to create a custom composite type with fields for accum_number and COMPUTE_METHOD, and write a single new aggregate function that uses this new data type.

how to return an array of (bigint, double precision) from a function in order to convert it to json array

What I need is a string/text json-compliant array of arrays like the following:
[
[1421420100, 14214201003487],
[1421420101, 14214201003487],
[1421420109, 14214201003487]
...
]
in order to return it from the server to the client without any further elaboration.
I have a function that queries on a table that returns the two fields, but I would like to return an array of arrays in order to call the function and then call array_to_json. Then, I would like to convert it into text/string.
Is that possible? Can an array of arrays have two different types?
Postgres arrays cannot hold elements of different type.
But you can return an array of composite type (bigint, double precision) instead. The type needs to be registered in the database instance. Either with an explicit CREATE TYPE statement, or implicitly as row type of a table or view.
Example:
CREATE TYPE int8_float AS (b bigint, f float);
CREATE OR REPLACE FUNCTION f_combo_arr()
RETURNS int8_float[]
LANGUAGE sql AS
$func$
SELECT ARRAY(SELECT (int8_col, float_col)::int8_float FROM tbl);
$func$;
Call:
SELECT f_combo_arr();
float is an alias for double precision in Postgres.
There may be a smarter solution, like returning data type json or a set instead of the array.
Related:
Return type for function with array_agg()
PostgreSQL - best way to return an array of key-value pairs

WrappedArray to Array in HiveQL

I'm using collect_list to group some column like:
val res = hiveContext.sql("SELECT date, time, collect_list(id) AS id_list FROM table1 GROUP BY date, time")
The id_list returns as a WrappedArray:
WrappedArray(1,2,1,2)
WrappedArray(4,3,4)
WrappedArray(6,7,6,7,6)
However I'm passing the id_list into myFunc that takes an Array[Double] as input:
def myFunc(xs: Array[Double]) {...}
My question is how can I call myFunc correctly to parse the id_list. I'm having something like:
res.collect.foreach(x => myFunc(x(2)))
but it's giving me an type mismatch; found : Any required: Array[Double] error.
What is the correct way to implicitly convert the WrappedArray into an Array or how can I call myFunc in an optimized way?
Thanks!
The main question here is rather to explicitly convert Any to WrappedArray[Double] that you may do as:
x(2).asInstanceOf[mutable.WrappedArray[Double]].toArray
in your case. Or you may use pattern matching for type conversion that you may refer How to use a Scala match expression instead of isInstanceOf (to match types) for details.

T-SQL function that, when called, behaves as a boolean expression?

I'm trying to write a T-SQL function that evaluates to a boolean.
I've read several articles stating that BIT is T-SQL equivalent of a BOOLEAN type (such as Is there a Boolean data type in Microsoft SQL Server like there is in MySQL?, http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/c3143a77-c921-40a7-920e-3d5c5f7a269a).
Yet, when I create function returning BIT:
create function dbo.fn_checkLength( #name nvarchar(50), #maxLength int) returns bit
as begin
if len(#name) > #maxLength return cast(0 as bit);
return cast(1 as bit);
end;
GO
It does not behave as a function that evaluates to a boolean, because the following statement:
create table dbo.test_table (
id int,
name nvarchar(50),
constraint
ck_test_table_maxLength
check (
dbo.fn_checkLength(name, 10)
)
);
GO
Gives me:
Msg 4145, Level 15, State 1, Line 10
An expression of non-boolean type specified in a context where a condition is expected, near ')'.
To make it work, when calling my function I have add a boolean operator in the mix, to really make the expression boolean:
create table dbo.test_table (
id int,
name nvarchar(50),
constraint
ck_test_table_maxLength
check (
dbo.fn_checkLength(name, 10) > 0
)
);
GO
Why is this so? Is there any way to declare function that can be used in T-SQL statement as a regular boolean expression?
Thanks!
Before SQL:1999 SQL did not have a true Boolean data type, and few implementations currently support this feature.
The bit type is not a true Boolean type - it's a numeric type that can have the values 1 or 0 (or NULL).
So unless you use a SQL:1999 implementation that supports this feature you are stuck with having to have a Boolean expression in your check clause.
Interesting.. I guess the only explanation is, CHECK by definition accepts only 'conditional expression that evaluates to TRUE or FALSE'. As per the second line in the documentation You can create a CHECK constraint with any logical (Boolean) expression that returns TRUE or FALSE based on the logical operators. 'Logical operators' is the key.

Resources