How to check if value in input array in PostgreSQL function - postgresql

I have a pure SQL function like this:
CREATE OR REPLACE FUNCTION get_buildings_by_type(
building_types TEXT
)
RETURNS TABLE (bldg_id TEXT, "SurfArea" FLOAT, geom GEOMETRY) AS
$$
SELECT
bldg."OBJECTID"::TEXT AS bldg_id,
bldg."SurfArea"::FLOAT,
bldg.geom
FROM
static.buildings AS bldg
WHERE
bldg."LandUse" = $1
$$
LANGUAGE SQL;
And it behaves as expected, everything is working. However, I would like to have it work with an input array of building_types, rather than a single values. When I try this instead:
CREATE OR REPLACE FUNCTION get_buildings_by_type(
building_types TEXT[]
)
RETURNS TABLE (bldg_id TEXT, "SurfArea" FLOAT, geom GEOMETRY) AS
$$
SELECT
bldg."OBJECTID"::TEXT AS bldg_id,
bldg."SurfArea"::FLOAT,
bldg.geom
FROM
static.buildings AS bldg
WHERE
bldg."LandUse" IN $1
$$
LANGUAGE SQL;
I get a syntax error:
ERROR: syntax error at or near "$1"
LINE 15: bldg."LandUse" IN $1
Any ideas?
The version is 9.6 if that is relevant.

The equivalent of an IN operator for arrays is the any operator:
You need to use:
WHERE
bldg."LandUse" = any($1);

Related

Error while creating table : Syntax error at or near "#"

I am trying to convert SQL Server stored function to PostgreSQL stored function I am getting one syntactical error at declare #table1 table
CREATE OR REPLACE FUNCTION ETL_GetBuildingDetailsByUserID ( p_nInstID numeric=0)
RETURNS Boolean
AS $$
declare #table1 table
(
nbuilding numeric(18, 0) NOT NULL,
sbuild_name varchar(50) NULL,
sclient_build_id varchar(50) NULL,
nbuilding_inst_id numeric(18, 0) NOT NULL,
ntemp_building_id numeric(18,0) NULL,
nno_of_floors numeric(18,0) NULL
)
declare v_strDeptIds text;
v_strSubDeptIds text;
BEGIN
v_strsql := 'SELECT building.*
FROM building
WHERE (building.nbuilding_inst_id = '|| cast(p_nInstID as varchar(1)) ||')
';
print v_strsql
v_strsql1 text;
v_strsql1 := v_strsql
Insert into #table1; execute sp_executesql; v_strsql1
select * from #table1;
Return true;
END;
$$ LANGUAGE plpgsql;
Error
ERROR: syntax error at or near "#"
LINE 4: declare #table1 table
Can any one please tell what I am doing wrong?
It seems your function actually returns the result of a SELECT query, not a boolean value, so returns boolean is wrong to begin with.
To return a result, you need to declare the function as returns table(). But as you seem to simply return rows from the building table you can define it as returns setof building.
Then remove the useless dynamic SQL which seems completely unnecessary.
In PL/pgSQL there are no table variables, and copying the result of a query into one before returning that result from that table seems to be an unnecessary step which only slows down things. In Postgres you simply return the result of the query, there is no need to store it locally.
Additionally: rather than casting a parameter to another type inside the function it's better to declare that parameter with the type you expect.
So the simplified version of that function in PostgreSQL would be:
CREATE OR REPLACE FUNCTION ETL_GetBuildingDetailsByUserID ( p_nInstID text)
RETURNS setof building
AS $$
select building.*
from building
WHERE building.nbuilding_inst_id = p_nInstID
$$ LANGUAGE sql;
You can use it like this:
select *
from ETL_GetBuildingDetailsByUserID ('42');
Unrelated, but: using numeric(18,0) for columns that store values without decimals is overkill. You should define those columns as bigint. Much faster and uses less space than numeric.

postgres function returns unexpected set

I have a simple function that returns a geometry for a given ZIP code:
CREATE FUNCTION public._zip_geom(IN zip VARCHAR)
RETURNS SETOF geometry AS $$
SELECT "the_geom_4326" FROM "shapes"."zip_polys" a WHERE a."zip" = zip
$$ LANGUAGE sql;
that I call with SELECT public._zip_geom('80302');.
When the return is defined as SETOF geometry, I get all geometries in the zip_polys table. When I change it to just geometry, I get a single geometry, but not the geometry that matches the provided ZIP code.
Why is that and how do I write a function that will return the correct value?
Your function argument collides with a column name (Duh!), you could try:
CREATE FUNCTION public._zip_geom(IN _omg VARCHAR)
RETURNS SETOF geometry AS $$
SELECT "the_geom_4326" FROM "shapes"."zip_polys" a WHERE a."zip" = _omg
$$ LANGUAGE sql;
The prefix underscore in _doh or _zip is more or less a convention for arguments; column names cannot start with an underscore, so _omg is collision free.

It could refer to either a PL/pgSQL variable or a table column

I have a function in pgsql
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(date_in_ad date)
RETURNS character varying AS
$$
BEGIN
RETURN(
SELECT date_in_bs FROM core.date_conversion
WHERE date_in_ad = $1
);
END
$$
LANGUAGE plpgsql;
It is created with no errors, but when i use this function it through following error:
ERROR: column reference "date_in_ad" is ambiguous
LINE 3: WHERE date_in_ad = $1
^
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
QUERY: SELECT (
SELECT MAX(date_in_bs) FROM core.date_conversion
WHERE date_in_ad = $1
)
CONTEXT: PL/pgSQL function core.date_bs_from_ad(date) line 3 at RETURN
********** Error **********
ERROR: column reference "date_in_ad" is ambiguous
SQL state: 42702
Detail: It could refer to either a PL/pgSQL variable or a table column.
Context: PL/pgSQL function core.date_bs_from_ad(date) line 3 at RETURN
In cases like these, where the code is simple straightforward enough, sometimes it is useful to rely on one of these special plpgsql commands at the start of the function text:
#variable_conflict error
#variable_conflict use_variable
#variable_conflict use_column
In this case, it would be used as follows:
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(date_in_ad date)
RETURNS character varying AS
$$
#variable_conflict use_column
BEGIN
RETURN(
SELECT date_in_bs FROM core.date_conversion
WHERE date_in_ad = $1
);
END
$$
This is especially useful for cases when the clash is not with the parameters, but rather with the output column names, such as this:
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(p_date_in_ad date)
RETURNS TABLE (date_in_bs character varying) AS
$$
BEGIN
RETURN QUERY
SELECT date_in_bs FROM core.date_conversion
WHERE date_in_ad = p_date_in_ad;
END;
$$
The function above will fail because it the compiler cannot decide if date_in_bs is the output variable name or one of core.date_conversion's columns. For problems like these, the command #variable_conflict use_column can really help.
There is a collision between SQL identifier and PlpgSQL variable. There are no clean, what do you want. You wrote a predicate, that is TRUE always.
Good to use:
prefix (usually "_") for local variables
qualified names in embedded SQL - like table_name.column_name
so both techniques (only one is necessary)
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(_date_in_ad date)
RETURNS character varying AS $$
BEGIN
RETURN SELECT dc.date_in_bs
FROM core.date_conversion dc
WHERE dc.date_in_ad = _date_in_ad;
END
$$ LANGUAGE plpgsql;
For these one line functions is SQL language better:
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(_date_in_ad date)
RETURNS character varying AS $$
SELECT dc.date_in_bs
FROM core.date_conversion dc
WHERE dc.date_in_ad = $1;
$$ LANGUAGE sql;

Returning from a function with OUT parameter

I have an error, but I don't know what the problem is.
I want execute a function and return a value from a column filled in by the column default, a sequence - the equivalent of currval(sequence).
I use:
PostgreSQL 9.0
pgAdmin III
CREATE OR REPLACE FUNCTION name_function(in param_1 character varying
, out param_2 bigint)
AS
$$
BEGIN
INSERT INTO table (collumn_seq,param_1) VALUES (DEFAULT,param_1)
returning collumn_seq;
--where:collumn_seq reference a collumn serial..
END;
$$
LANGUAGE plpgsql VOLATILE;
I can create the function without error but when trying to execute, the following error is returned:
SELECT name_function('GHGHGH');
ERROR: The query has no destination for result data
It would work like this:
CREATE OR REPLACE FUNCTION name_function(param_1 varchar
, OUT param_2 bigint)
LANGUAGE plpgsql AS
$func$
BEGIN
INSERT INTO table (collumn_seq, param_1) -- "param_1" also the column name?
VALUES (DEFAULT, param_1)
RETURNING collumn_seq
INTO param2;
END
$func$;
Normally, you would add a RETURN statement, but with OUT parameters, this is optional.
Refer to the manual for more details:
Returning from a function
Executing a Query with a Single-row Result
The simple case can be covered with a plain SQL function.
And you can omit the target column that shall get its DEFAULT value.
And you can just as well use a RETURNS clause in this case:
CREATE OR REPLACE FUNCTION name_function(param_1 varchar)
RETURNS bigint
LANGUAGE sql AS
$func$
INSERT INTO table (param_1) -- "param_1" also the column name?
VALUES (param_1)
RETURNING collumn_seq;
$func$;

Can I use INSERT inside CASE in postgres

I am trying to create a function in postgres that would insert a row if it doesn't exist and return the row's id (newly created or existing).
I came up with this:
CREATE OR REPLACE FUNCTION
get_enttype(name character varying, module character varying)
RETURNS integer LANGUAGE SQL STABLE AS $$
SELECT typeid FROM enttypes WHERE name = $1 AND module = $2
$$;
CREATE OR REPLACE FUNCTION
ensure_enttype(name character varying, module character varying)
RETURNS integer LANGUAGE SQL AS $$
SELECT CASE WHEN get_enttype($1, $2) IS NULL
THEN
INSERT INTO enttypes(name, module) VALUES ($1, $2) RETURNING typeid
ELSE
get_enttype($1, $2)
END
$$;
It however raises a syntax error because of INSERT inside CASE. I managed to fix this problem by creating a separate function with this INSERT and using this function in CASE. It works as expected, but this fix seems a little strange. My question is - can I fix this without creating another function?
No, you can't use INSERT inside CASE in an sql function. You can, however:
Use a PL/PgSQL function with IF ... ELSE ... END statements; or
Use a writable common table expression (wCTE) or INSERT INTO ... SELECT query
Pure SQL can do it:
create or replace function ensure_enttype(
name character varying, module character varying
) returns integer language sql as $$
insert into enttypes(name, module)
select $1, $2
where get_enttype($1, $2) is null
;
select get_enttype($1, $2);
$$;