EDIT Taking COST 100 out made the command go through, however, I'm still unable to run my query because it yields this error:
ERROR: function group_concat(character) does not exist
HINT: No function matches the given name and argument types. You may need to add explicit type casts.
The query I'm running is this:
select tpid, group_concat(z) as z,
group_concat(cast(r as char(2))) as r,
group_concat(to_char(datecreated,'DD-Mon-YYYY HH12:MI am')) as datecreated,
group_concat(to_char(datemodified,'DD-Mon-YYYY HH12:MI am')) as datemodified
from tpids group by tpid order by tpid, zip
This function seems to work fine locally, but moving it online yields this error... Is there something I'm missing?
CREATE OR REPLACE FUNCTION group_concat(text, text)
RETURNS text AS
$BODY$
SELECT CASE
WHEN $2 IS NULL THEN $1
WHEN $1 IS NULL THEN $2
ELSE $1 operator(pg_catalog.||) ',' operator(pg_catalog.||) $2
END
$BODY$
LANGUAGE 'sql' IMMUTABLE
COST 100;
ALTER FUNCTION group_concat(text, text) OWNER TO j76dd3;
As the hint message states, you are missing an argument to your stored procedure. It expects 2 arguments, but in your column statments:
group_concat(cast(r as char(2))) as r,
group_concat(to_char(datecreated,'DD-Mon-YYYY HH12:MI am')) as datecreated,
group_concat(to_char(datemodified,'DD-Mon-YYYY HH12:MI am')) as datemodified
you supply only one.
Since PostgreSQL 8.4 default values for arguments are allowed. Take a look at the reference for more information and examples.
Related
I am trying to create a function in PostgreSQL that contains a dblink query. The definition of the field that it returns could vary, so I would like to set the field name and data types as input parameters. The error being returned suggests that it isn't referencing the input variables when I name them,
CREATE OR REPLACE FUNCTION validation.dblink_date_check(
username text,
pass text,
srcschemaname text,
srctablename text,
srcdbname text,
datefield text,
datetype text DEFAULT 'date'::text,
srchostname text DEFAULT 'xxx' text,
srcport integer DEFAULT 1234,
OUT max_date timestamp)
RETURNS timestamp
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
BEGIN
max_date := (
select c.*
from dblink('dbname='||srcdbname||' host='||srchostname||' port='||srcport||' user='||username||' password='||pass||'',
'select
max('||datefield||') from '||srcschemaname||'.'||srctablename||' a')
--this doesn't work
as c(datefield datetype
));
/*--this works
as c(datefield date
));;*/
raise notice '% - (source): %', date_trunc('second', clock_timestamp()::timestamp),max_date;
return;
End;
$BODY$;
When it doesn't work the error I get is,
ERROR: type "datetype" does not exist
LINE 6: as c(datefield datetype
^
Is there a way to structure the syntax to work (as sometimes it might not be a date, it might be a timestamp, or an integer etc)? I have used 'execute' in other functions (to insert), but using it to select the variable 'max_date' doesn't work.
Trying to create my first PostgreSQL function, I don't understand why I can't call this function.
CREATE FUNCTION public."SampledImpCountToOriginal"(IN integer)
RETURNS numeric
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
RETURN CASE WHEN $1 > 4 THEN EXP(POW($1 - 1.88, 1/2.3)) ELSE $1 END;
END;
$BODY$;
SELECT SampledImpCountToOriginal(5)
ERROR: function sampledimpcounttooriginal(integer) does not exist
LINE 1: SELECT SampledImpCountToOriginal(5)
^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. SQL state:
42883 Character: 8
Calling from the same database I created it in, tried changing owner, can't figure it out. The function is listed in pgAdmin as SampledImpCountToOriginal(IN integer).
You have to call like this - SELECT "SampledImpCountToOriginal"(5)
When ever you use Double Quotes "" to create Function, you have to use in calling process. like -
SELECT public."SampledImpCountToOriginal"(
<integer>
)
If you don't use double quotes "" to calling your created function with "". it consider different function.
SELECT public.sampledimpcounttooriginal(
<integer>
)
I have created custom data type. In that I have given alias name of the one field. you will get that in body of the function below.
create type voucher as (
ori numeric, RECEIPT_NO numeric
, receipt_date timestamp with time zone, reg_no character varying
, patient_name character varying, tot_refund_bill_amount double precision
, username character varying );
Thea above statement completes successfully.
Then I want to create a function:
create or replace function billing.voucher_receipt (in_from_date timestamp with time zone, in_to_date timestamp with time zone)
returns setof voucher as $$
declare
out_put voucher%rowtype;
begin
return query(select C.receipt_no as ori ,A.RECEIPT_NO, receipt_date , A.reg_no, patient_name, tot_refund_bill_amount, username
from billing.tran_counter_receipt as a inner join mas_user as b on a.ent_by=b.uid AND cash_book='REFUND'
INNER JOIN billing.tran_BILL AS C ON C.REG_NO=A.REG_NO AND C.CASH_BOOK='GCASH' where receipt_date>=in_from_date and receipt_date<=in_to_date);
end;$$
LANGUAGE plpgsql
Executes without problem.
But when I call it with input like this:
select * from voucher_receipt ('2014-09-25 11:42:44.298346+05:30'
, '2014-09-29 11:03:47.573049+05:30')
it shows an error:
ERROR: function voucher_receipt(unknown, unknown) does not exist
LINE 1: select * from voucher_receipt ('2014-09-25 11:42:44.298346+0...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
Can any one help me out from this?
Explain error
You created your function in the schema billing with:
create or replace function billing.voucher_receipt( ...
Then you call without schema-qualification:
select * from voucher_receipt ( ...
This only works while your current setting for search_path includes the schema billing.
Better function
You don't need to create a composite type. Unless you need the same type in multiple places just use RETURNS TABLE to define the return type in the function:
CREATE OR REPLACE FUNCTION billing.voucher_receipt (_from timestamptz
, _to timestamptz)
RETURNS TABLE (
ori numeric
, receipt_no numeric
, receipt_date timestamptz
, reg_no varchar
, patient_name varchar
, tot_refund_bill_amount float8
, username varchar) AS
$func$
BEGIN
RETURN QUERY
SELECT b.receipt_no -- AS ori
, cr.RECEIPT_NO
, ??.receipt_date
, cr.reg_no
, ??.patient_name
, ??.tot_refund_bill_amount
, ??.username
FROM billing.tran_counter_receipt cr
JOIN billing.tran_bill b USING (reg_no)
JOIN mas_user u ON u.uid = cr.ent_by
WHERE ??.receipt_date >= _from
AND ??.receipt_date <= _to
AND b.CASH_BOOK = 'GCASH'
AND ??.cash_book = 'REFUND'
END
$func$ LANGUAGE plpgsql;
Notes
Don't call your parameters "date" while they are actually timestamptz.
RETURN QUERY does not require parentheses.
No need for DECLARE out_put voucher%rowtype; at all.
Your format was inconsistent and messy. That ruins readability and that's also where bugs can hide.
This could just as well be a simple SQL function.
Column names in RETURNS TABLE are visible in the function body almost everywhere. table-qualify columns in your query to avoid ambiguities (and errors). Replace all ??. I left in the code, where information was missing.
Output column names are superseded by names in the RETURNS declaration. So AS ori in the SELECT list is just documentation in this case.
Why schema-qualify billing.tran_bill but not mas_user?
I want to find the median value of some data in pgsql. A quick google search told me that PGSQL 8.2 does not come with a median function. After some more searching I found this link
https://wiki.postgresql.org/wiki/Aggregate_Median
which provides some information on how to write a custom median function. Here is the code I have so far
CREATE OR REPLACE FUNCTION my_schema.final_median(anyarray) RETURNS float8 STRICT AS
$$
DECLARE
cnt INTEGER;
BEGIN
cnt := (SELECT count(*) FROM unnest($1) val WHERE val IS NOT NULL);
RETURN (SELECT avg(tmp.val)::float8
FROM (SELECT val FROM unnest($1) val
WHERE val IS NOT NULL
ORDER BY 1
LIMIT 2 - MOD(cnt, 2)
OFFSET CEIL(cnt/ 2.0) - 1
) AS tmp
);
END
$$ LANGUAGE plpgsql;
CREATE AGGREGATE my_schema.mymedian(anyelement) (
SFUNC=array_append,
STYPE=anyarray,
FINALFUNC=my_schema.final_median,
INITCOND='{}'
);
-- I need this filter here. This is a place holder for a larger query
select my_schema.mymedian(id) filter (where id < 5)
from my_schema.golf_data
However I am getting an error when I run the code
ERROR: function my_schema.mymedian(numeric) is not defined as STRICT
LINE 27: select my_schema.mymedian(id) filter (where id < 5)
^
HINT: The filter clause is only supported over functions defined as STRICT.
********** Error **********
ERROR: function my_schema.mymedian(numeric) is not defined as STRICT
SQL state: 0AM00
Hint: The filter clause is only supported over functions defined as STRICT.
Character: 661
I am guessing the interpreter wants me to add the keyword strict somewhere. But I am not sure where I need to make this change.
Any help would be appreciated
This page indicates how to use the STRICT keyword and what it does:
http://www.postgresql.org/docs/8.2/static/sql-createfunction.html
So try this:
CREATE OR REPLACE FUNCTION my_schema.final_median(anyarray) RETURNS float8 STRICT AS
IMPORTANT:
The impact of using STRICT is that if your input is NULL (In your case, if there are no records with id < 5) then the result will be ASSUMED to be NULL and the function will not be called. So you need to be sure the place where you call it from can cope with that.
I am trying check if a value is null if so the select null else cast to numeric, but it throws an error. This is actually part of an insert statement
INSERT into someTable(name,created,power)
SELECT 'xyz',now(),
case when :power ='null' then NULL else cast(:power as numeric) end from abc
error that I get is
Error: ERROR: invalid input syntax for type numeric: "null"
:power is a variable that can be given any value using java code. If I give a value of null it give an error.
In code I get the following error from the java stack trace
org.postgresql.util.PSQLException: ERROR: cannot cast type bytea to numeric
Error:
SELECT CASE WHEN 'null' = 'null' THEN NULL ELSE cast('null' AS numeric) END
No error:
DO $$
DECLARE
power text := 'null';
BEGIN
PERFORM CASE WHEN power = 'null' THEN NULL ELSE cast(power AS numeric) END;
END;
$$
Explanation:
If you build a query string, the expression cast('null' AS numeric) or simply 'null'::numeric always raises an exception, even in an ELSE block that is never executed, because it is invalid input syntax and the exception is raised during the syntax check (like the error message implies), not during execution.
A CASE statement like you display only makes sense with a parameter or variable not with literals. The second instance of the literal has no connection to the first instance whatsoever after the query string has been assembled.
For dynamic SQL like that, you need to check the value before you build the query string. Or you use a function or prepared statement and pass the value as parameter. That would work, too.
More advice after comment:
In your particular case you could check the value in the app and build a query string like this:
INSERT INTO tbl(name, abc_id, created, power)
SELECT 'xyz'
, abc_id
, now()
, <insert_value_of_power_or_NULL_here> -- automatically converted to numeric
FROM abc
You may be interested in a different approach to INSERT data from a file conditionally.
Use COPY for files local to the server or psql's meta-command \copy for files local to the client.
if the field value is null, and you want in this case to map it to some value you can use coalesce(field_name, 'Some value') or coalesce(field_name, 123).
For full documentation see here.
You have to check with the IS operator, and not with the equal when you dealing with NULL :
INSERT into someTable(name,created,power)
SELECT 'xyz',now(),
case when :power IS null then NULL else cast(:power as numeric) end from abc
INSERT into someTable(name,created,power) SELECT 'xyz',now(),
case :power when 'null' then NULL else :power end::numeric from abc
I was trying to do something similar in order to update/insert some records where a numeric value can be null or not.
You can validate a variable before you send it to the function or inside the function depending the value passed
(For me using a variable is better than use CASE WHEN THEN ELSE END CASE every time you need to validate the value)
So to work with the NULL values using a regular comparison operand in order to find a record to update can be done by turning transform_null_equals to ON
I hope this help someone
CREATE OR REPLACE FUNCTION update_insert_transaction(vcodaccount integer, vcodaccountaux text,
vdescription text, vcodgroup integer)
RETURNS integer AS $$
DECLARE
n integer = 0;
vsql text = 'NULL';
BEGIN
IF vcodaccountaux <> '' THEN
vsql = vcodaccountaux;
END IF;
SET LOCAL transform_null_equals TO ON;
EXECUTE 'UPDATE account_import_conf SET (codaccount, codaccountaux, description, codgroup) =
('||vcodaccount||','||vsql||',trim('||quote_literal(vdescription)||'),'||vcodgroup||')
WHERE codaccount='||vcodaccount||' AND codaccountaux = '||vsql||' RETURNING * ';
GET DIAGNOSTICS n = ROW_COUNT;
IF n = 0 THEN
EXECUTE 'INSERT INTO account_import_conf (codaccount, codaccountaux, description, codgroup)
SELECT '||vcodaccount||','||vsql||' ,trim('||quote_literal(vdescription)||'),'||vcodgroup||';';
END IF;
RETURN n;
END;$$
LANGUAGE plpgsql;