I have a postgres table with the following format:
I want add two (or more) columns to right (and maybe to left) like this:
But if I try to do that I get this:
ERROR: invalid return type
DETAIL: Query-specified return tuple has 15 columns but crosstab returns 14.
CONTEXT: PL/pgSQL function> carranza(date,date) line 8 at RETURN QUERY
This is my code:
Can anyone help me please?
CREATE OR REPLACE FUNCTION carranza(
IN p_fecha_inicio date,
IN p_fecha_fin date)
RETURNS TABLE(r_sucursal text, r_cliente text, r_nombre_cliente character varying, r_fecha_pago text, r_fecha_inicio text, r_fecha_fin text, r_fecha_proceso text, r_proceso text, r_nomina text, r_periodo text, r_desc_periodo text, r_fecha_factura text, r_factura text, r_cia text, r_servicio text, r_total text, r_costo_nt text, r_costo_sc text) AS
$BODY$
DECLARE
v_registros record;
BEGIN
RETURN QUERY
SELECT
(SELECT sucursal_name FROM "Facturacion_facturaprevia" WHERE factura_generada_id = obj_concentrado."factura"::integer)::text,
obj_concentrado."id cliente",
(SELECT cliente FROM "Clientes_cliente" WHERE id = obj_concentrado."id cliente"::integer),
obj_concentrado."fecha_pago",
obj_concentrado."fecha_inicio",
obj_concentrado."fecha_fin",
obj_concentrado."fecha_proceso",
obj_concentrado."proceso",
obj_concentrado."num_nomin",
obj_concentrado."periodo",
(SELECT descripcion_corta FROM "Catalogos_tiponomina" WHERE id_primario = obj_concentrado."descrip periodo")::text,
(SELECT fecha::date FROM "Facturacion_facturagenerada" WHERE id = obj_concentrado."factura"::integer)::text,
obj_concentrado."factura",
(SELECT cia FROM "Facturacion_facturagenerada" WHERE id = obj_concentrado."factura"::integer)::text,
(SELECT desc_corta FROM "Catalogos_servicio" WHERE id = obj_concentrado."servicio_id"::integer)::text,
(SELECT total_factura FROM "Facturacion_facturagenerada" WHERE id = obj_concentrado."factura"::integer)::text,
obj_concentrado."cia_NT",
obj_concentrado."cia_SC"
FROM (
SELECT * FROM crosstab (
'SELECT
proceso::text,
num_nomina::text,
(periodo || ''-'' || periodo_esp) as periodo,
tipo_periodo,
fecha_inicio::text,
fecha_fin::text,
fecha_pago::text,
fecha_proceso::text,
cliente_id::text,
servicio_id::text,
factura::text,
tipo_compania::text,
sum(activas)::text,
sum(canceladas)::text
FROM (
SELECT
factura,
cliente_id,
CASE
WHEN nat_concepto_id = 1 AND estatus = true
THEN importe
WHEN nat_concepto_id = 2 AND estatus = true
THEN importe * -1
WHEN nat_concepto_id = 3 AND estatus = true
THEN importe
END activas,
CASE
WHEN nat_concepto_id = 1 AND estatus = false
THEN importe
WHEN nat_concepto_id = 2 AND estatus = false
THEN importe * -1
WHEN nat_concepto_id = 3 AND estatus = false
THEN importe
END canceladas,
tipo_compania,
fecha_inicio,
fecha_fin,
fecha_pago,
fecha_proceso,
num_nomina,
servicio_id,
proceso,
periodo,
periodo_esp,
tipo_periodo
FROM "Facturacion_detallenomina"
WHERE (proceso || ''-'' || num_nomina) IN (
SELECT proceso || ''-'' || nomina
FROM (
SELECT distinct(proceso), nomina
FROM "Facturacion_facturapreviadataregistros"
WHERE facturaprevia_id IN (
SELECT id
FROM "Facturacion_facturaprevia"
WHERE factura_generada_id IN (select id FROM "Facturacion_facturagenerada" where num_factura<>'''')
)
) obj_data
)AND NOT rubro_id = ANY(ARRAY[0,10,14])
)obj_importes
GROUP BY tipo_compania, factura, cliente_id, fecha_pago, fecha_fin, fecha_inicio, fecha_proceso, num_nomina, proceso, periodo, periodo_esp, tipo_periodo, servicio_id
ORDER BY factura',
'select m from generate_series(1,2) m'
)as (
"proceso" text,
"num_nomin" text,
"periodo" text,
"descrip periodo" text,
"fecha_inicio" text,
"fecha_fin" text,
"fecha_pago" text,
"fecha_proceso" text,
"id cliente" text,
"servicio_id" text,
"factura" text,
"cia_NT" text,
"cia_SC" text,
"cia_nt_can" text,
"cia_sc_can" text
)
)obj_concentrado;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION carranza(date, date)
OWNER TO curso;
Related
So this is the function
`
create function createGridFromChart(p_y_value character varying) returns text
language plpgsql
as
$$
DECLARE
v_query text;
BEGIN
v_query = 'SELECT result,
dateG,
(SELECT name player1 from mts_players WHERE mts_game.id_player1 = mts_players.id),
(SELECT name player2 from mts_players WHERE mts_game.id_player2 = mts_players.id),
(SELECT name player3 from mts_players WHERE mts_game.id_player3 = mts_players.id),
(SELECT name player4 from mts_players WHERE mts_game.id_player4 = mts_players.id),
trophy, winners
from mts_game
where dateG> CURRENT_DATE-30 and ((SELECT name from mts_players WHERE mts_game.id_player1 = mts_players.id) = '''|| p_y_value || ''' or (SELECT name from mts_players WHERE mts_game.id_player2 = mts_players.id) = '''|| p_y_value ||''' or (SELECT name from mts_players WHERE mts_game.id_player3 = mts_players.id) = '''|| p_y_value ||''' or (SELECT name from mts_players WHERE mts_game.id_player4 = mts_players.id) ='''|| p_y_value ||''' )
order by dateG';
RETURN v_query;
END;
$$;
`
Right now the function returns the query and it has to be manually executed in order to produce results. I want to edit it so that you don't have to do that, but it's executed right away when the function is called.
I read about return query execute command but I haven't managed to make it work.
I don't expect complete solution but some tips or help would be appreciated.
You need to change the function to return a table, rather than a string.
Returning the result of a query can easily be done using language sql:
create function createGridFromChart(p_y_value character varying)
returns table (result text, dateg date, name1 text, name2 text, name3 text, name4 text, tropy text, winners text) --<<< adjust types here!!
language sql
as
$$
SELECT result,
dateG,
(SELECT name player1 from mts_players WHERE mts_game.id_player1 = mts_players.id),
(SELECT name player2 from mts_players WHERE mts_game.id_player2 = mts_players.id),
(SELECT name player3 from mts_players WHERE mts_game.id_player3 = mts_players.id),
(SELECT name player4 from mts_players WHERE mts_game.id_player4 = mts_players.id),
trophy,
winners
from mts_game
where dateG > CURRENT_DATE-30
and ( (SELECT name from mts_players WHERE mts_game.id_player1 = mts_players.id) = p_y_value
or (SELECT name from mts_players WHERE mts_game.id_player2 = mts_players.id) = p_y_value
or (SELECT name from mts_players WHERE mts_game.id_player3 = mts_players.id) = p_y_value
or (SELECT name from mts_players WHERE mts_game.id_player4 = mts_players.id) = p_y_value )
order by dateG';
$$;
Note that you need to adjust the data types in the returns table (...) part.
You can then use the function like this:
select *
from creategridfromchart('blabla');
Can I use PostgreSQL function in join? also can I use cursor function in join?
actually this is what I want
Select m.* from Medication m
Left Join public.GetResidentMedications(, , , , , ,) f on f.Id= m.Id
same with the cursor functions?
**Below is my function which is a cursor function and I want to join it **
CREATE OR REPLACE FUNCTION public."GetResidentMedications" (
ref refcursor,
_alfid integer,
_residentid integer,
_types varchar,
_limits integer,
_offsets integer
)
RETURNS refcursor AS
$body$
BEGIN
open ref for
-- select * from public."GetResidentMedications"('refcursor', 25, 331, '' , 20, 1)
with cte AS (
select m."Id"
from public."Medication" m
where m."AlfId" = _AlfId
and (m."ResidentId" = _ResidentId or coalesce (_ResidentId, 0) = 0)
and 1 = (
case when
'IsVerified' = ANY ('{IsVerifiedsss, IsVerifieds}') and m."IsVerified" = true then 1 else 0 end
)
)
select * from (
table cte order BY "Id" limit _limits offset _offsets
) sub
Left join (select count("Id") from cte) c(TotalRecords) on true;
return ref;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
PARALLEL UNSAFE
COST 100;
is it possible to do it?
Make your function a set-returning function by defining it as "returns table (...)". The ref cursor is not needed.
CREATE OR REPLACE FUNCTION public."GetResidentMedications" (
_alfid integer,
_residentid integer,
_types varchar,
_limits integer,
_offsets integer
)
RETURNS table (id integer, ... other columns ... )
as
$body$
with cte AS (
select m."Id"
from public."Medication" m
where m."AlfId" = _AlfId
and (m."ResidentId" = _ResidentId or coalesce (_ResidentId, 0) = 0)
and 1 = (
case when
'IsVerified' = ANY ('{IsVerifiedsss, IsVerifieds}') and m."IsVerified" = true then 1 else 0 end
)
)
select *
from (
table cte order BY "Id" limit _limits offset _offsets
) sub
Left join (select count("Id") from cte) c(TotalRecords) on true;
$body$
LANGUAGE sql
stable;
then you can use it like this:
select ...
from some_table
left join "GetResidentMedications"(1,2,'bla',10,200) as g on g.id = ...;
I really need advice. I'm trying to use the function : pivot_subrecords_set_table_1
In the following query:
SELECT rec.id AS key,
rec.primaryurn AS urn,
rec.mx_syscreatedby AS creared_by,
(SELECT mf_mxdate_str_to_date (epi.mx_epizoddate)) AS date_of_creation,
epi.mx_epizodenotes AS notes,
epi.mx_effectscountry AS country,
epi.mx_effectsregion AS region,
epi.mx_effectscity AS city,
(SELECT get_epizod_subrecords (epi.id)) AS epizod_type
(pivot_subrecords_set_table_1 (epi.id)).idx,
(pivot_subrecords_set_table_1 (epi.id)).upn_1 AS upn_1,
(pivot_subrecords_set_table_1 (epi.id)).status_1
FROM mxt_recordheader AS rec, mt_epizod AS epi, mt_epizod_subrecord AS eps
WHERE rec.entitytype = 'epizod'
AND rec.logicalserverprefix = 'EA'
AND epi.id = rec.id;
The function looks like this:
CREATE OR REPLACE FUNCTION public.pivot_subrecords_set_table_1(id bigint,
OUT idx bigint, OUT upn_1 text, OUT status_1 text, OUT upn_2 text, OUT
status_2 text, OUT upn_3 text, OUT status_3 text, OUT upn_4 text, OUT
status_4 text, OUT upn_5 text, OUT status_5 text, OUT upn_6 text, OUT
status_6 text, OUT upn_7 text, OUT status_7 text, OUT upn_8 text, OUT
status_8 text, OUT upn_9 text, OUT status_9 text, OUT upn_10 text, OUT
status_10 text)
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
DECLARE
key ALIAS FOR $1;
BEGIN
RETURN QUERY
WITH sub_upn AS (SELECT * FROM crosstab
('SELECT id, mx_osobastatus, mx_osobaupn FROM mt_epizod_subrecord
WHERE mt_epizod_subrecord.mx_syscategory = ''tableepizodinfo''
AND mt_epizod_subrecord.id = '|| key ||' GROUP BY id, mx_osobaupn,
mx_osobastatus
ORDER BY id') AS ct
(id bigint, UPN_1 text, UPN_2 text, UPN_3 text, UPN_4 text, UPN_5 text, UPN_6
text, UPN_7 text, UPN_8 text ,UPN_9 text, UPN_10 text)
),
sub_stat AS
(SELECT * FROM crosstab
('SELECT id, mx_osobaupn, mx_osobastatus FROM mt_epizod_subrecord
WHERE mt_epizod_subrecord.mx_syscategory = ''tableepizodinfo''
AND mt_epizod_subrecord.id = '|| key ||' GROUP BY 1,2,3 ORDER BY id ')
AS cn (id bigint, status_1 text, status_2 text, status_3 text, status_4 text,
status_5 text, status_6 text, status_7 text, status_8 text,
status_9 text, status_10 text)
)
SELECT
supn.id,
supn.UPN_1,
sust.status_1,
supn.UPN_2,
sust.status_2,
supn.UPN_3,
sust.status_3,
supn.UPN_4,
sust.status_4,
supn.UPN_5,
sust.status_5,
supn.UPN_6,
sust.status_6,
supn.UPN_7,
sust.status_7,
supn.UPN_8,
sust.status_8,
supn.UPN_9,
sust.status_9,
supn.UPN_10,
sust.status_10
FROM sub_upn supn
JOIN sub_stat sust ON supn.id = sust.id;
END;
$function$
But the performance of the function is very low.
How can I increase the performance of a function or can I rewrite the query in which it is used?
Use plprofiler to figure out what is slow in your function and optimize that.
I ran into a problem with regards to trying to insert multiple rows in a table at once. I know it sounds easy, but here is the twist. The procedure itself gets data from a trigger, and the trigger returns a number of rows. So i need to make 1 insert statement to insert those rows and some other data. here is the code:
CREATE PROCEDURE [a01].[usp_raiseFriendAlerts]
(#AccountA UNIQUEIDENTIFIER, #AccountB UNIQUEIDENTIFIER)
AS
BEGIN
DECLARE #typeID TINYINT;
DECLARE #notificationID UNIQUEIDENTIFIER = NEWID();
DECLARE #accountAName NVARCHAR(356);
DECLARE #accountBName NVARCHAR(356);
SET #typeID = ( SELECT typeID
FROM [a01].[tbl_notificationTypes]
WHERE typeName = 'Added friend');
SET #accountAName = ( SELECT accountUsername
FROM [a01].[tbl_userAccounts]
WHERE accountID = #AccountA);
SET #accountBName = ( SELECT accountUsername
FROM [a01].[tbl_userAccounts]
WHERE accountID = #AccountB);
DECLARE #AccountIDZZ UNIQUEIDENTIFIER;
SET #AccountIDZZ = (SELECT friendAccountID
FROM [a01].[udf_getAddedFriendContacts](#AccountA, #AccountB)
EXCEPT
SELECT targetAccountID
FROM [a01].[tbl_blockedAccounts]);
INSERT INTO [a01].[tbl_notificationsInbox] (notificationID, notificationMessage, notificationDate, accountID, typeId)
VALUES (#notificationID, #accountAName + ' is now friends with ' + #accountBName, SYSDATETIMEOFFSET(), #AccountIDZZ , #typeID)
END;
GO
Try this:
CREATE PROCEDURE [a01].[usp_raiseFriendAlerts]
(
#AccountA UNIQUEIDENTIFIER
, #AccountB UNIQUEIDENTIFIER
)
AS
BEGIN
DECLARE #typeID TINYINT
, #notificationID UNIQUEIDENTIFIER = NEWID()
, #accountAName NVARCHAR(356)
, #accountBName NVARCHAR(356)
, #AccountIDZZ UNIQUEIDENTIFIER;
SELECT #typeID = typeID
FROM [a01].[tbl_notificationTypes]
WHERE typeName = 'Added friend';
SELECT #accountAName = accountUsername
FROM [a01].[tbl_userAccounts]
WHERE accountID = #AccountA;
SELECT #accountBName = accountUsername
FROM [a01].[tbl_userAccounts]
WHERE accountID = #AccountB;
INSERT INTO [a01].[tbl_notificationsInbox]
(
notificationID
, notificationMessage
, notificationDate
, accountID
, typeId
)
SELECT #notificationID
, #accountAName + ' is now friends with ' + #accountBName
, SYSDATETIMEOFFSET()
, AIDZZ.accountID
, #typeID
FROM (
SELECT friendAccountID AS 'accountID'
FROM [a01].[udf_getAddedFriendContacts](#AccountA, #AccountB)
EXCEPT
SELECT targetAccountID AS 'accountID'
FROM [a01].[tbl_blockedAccounts]
) AS AIDZZ
END;
GO
I try to correct this function but is impossible!!
I declare one integer "var_id", and insert in id_val the value of frist query,
if is null the tag name is inserted in a table and var_id = last insert id, otherwise do the last insert...
CREATE OR REPLACE FUNCTION public."InsertVideoTag"
(
IN in_idvideo integer,
IN in_tagname VARCHAR(25)
)
RETURNS bigint AS
$$
DECLARE var_id bigint DEFAULT NULL;
SELECT var_id := IDTag FROM Tag WHERE TagName = in_tagname;
IF var_id IS NULL
THEN
INSERT INTO tag ( TagName )
VALUES( in_tagname );
var_id := SELECT CURRVAL(pg_get_serial_sequence('public.tag','idtag'));
END IF;
INSERT INTO video_has_tag
(
IDVideo,
IDTag
)
VALUES
(
in_idvideo,
var_id
);
SELECT CURRVAL(pg_get_serial_sequence('public.video','idvideo'));
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;
The function can be converted into a pure SQL one, which will make it much better performing one. Also I've noted, that the current functionality will create duplicate entries in the video_has_tag table if called multiple times with the same arguments.
I've changed the function to be idempotent.
First table structure that I've assumed:
CREATE TABLE tag (
idTag serial,
tagName text);
CREATE TABLE video_has_tag (
idVideo integer,
idTag integer);
And then the function itself:
CREATE OR REPLACE FUNCTION insVideoTag(in_idvideo integer, in_tagname text)
RETURNS integer STRICT AS $insVideoTag$
WITH
new_tag AS (
INSERT INTO tag (tagName)
SELECT $2
WHERE NOT EXISTS (SELECT 1 FROM tag WHERE tagName = $2)
RETURNING idTag, tagName
), tag_data AS (
SELECT * FROM new_tag
UNION
SELECT idTag, tagName FROM tag
WHERE tagName = $2
), new_target AS (
INSERT INTO video_has_tag(idVideo, idTag)
SELECT $1, td.idTag
FROM tag_data td
WHERE NOT EXISTS (SELECT 1 FROM video_has_tag
WHERE idVideo=$1 AND idTag=td.idTag)
RETURNING idVideo, idTag
)
SELECT idVideo FROM (
SELECT * FROM new_target
UNION
SELECT * FROM video_has_tag
WHERE idVideo=$1 AND idTag=(SELECT idTag FROM tag_data)
) s;
$insVideoTag$ LANGUAGE sql;
No need for if statements, a where clause is enough:
selecting the current value is not suitable as a return value (or to be entered into an other tables FK) : it could have been bumped after the first insert.
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE tag
( idtag SERIAL NOT NULL PRIMARY KEY
, tagname varchar
);
CREATE TABLE video_has_tag
( idvideo INTEGER NOT NULL
, idtag INTEGER NOT NULL REFERENCES tag (idtag)
);
CREATE OR REPLACE FUNCTION tmp.insertvideotag
( in_idvideo integer , in_tagname VARCHAR )
RETURNS bigint AS
$$
DECLARE var_id bigint DEFAULT NULL;
BEGIN
INSERT INTO tag (tagname )
SELECT in_tagname
WHERE NOT EXISTS (
SELECT * FROM tag
WHERE tagname = in_tagname
);
INSERT INTO video_has_tag (idvideo,idtag)
SELECT in_idvideo, tg.idtag
FROM tag tg
WHERE tg.tagname = in_tagname
RETURNING idtag
INTO var_id
;
RETURN var_id;
END;
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;
SELECT insertvideotag(11, 'Eleven');
SELECT insertvideotag(12, 'Eleven');
SELECT insertvideotag(13, 'Thirteen');
SELECT tv.idvideo
,tv.idtag, tg.tagname
FROM video_has_tag tv
JOIN tag tg ON tg.idtag = tv.idtag
;
Result:
NOTICE: CREATE TABLE will create implicit sequence "tag_idtag_seq" for serial column "tag.idtag"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "tag_pkey" for table "tag"
CREATE TABLE
CREATE TABLE
CREATE FUNCTION
insertvideotag
----------------
1
(1 row)
insertvideotag
----------------
2
(1 row)
idvideo | idtag | tagname
---------+-------+----------
11 | 1 | Eleven
12 | 1 | Eleven
13 | 2 | Thirteen
(2 rows)
CREATE OR REPLACE FUNCTION public."InsertVideoTag"
(
IN in_idvideo integer,
IN in_tagname VARCHAR(25)
)
RETURNS bigint AS
$$
DECLARE var_id bigint DEFAULT NULL;
begin
var_id := (select IDTag FROM Tag WHERE TagName = in_tagname);
IF var_id IS NULL
THEN
INSERT INTO tag ( TagName )
VALUES( in_tagname );
var_id := (SELECT CURRVAL(pg_get_serial_sequence('public.tag','idtag')));
END IF;
INSERT INTO video_has_tag
(
IDVideo,
IDTag
)
VALUES
(
in_idvideo,
var_id
);
return (SELECT CURRVAL(pg_get_serial_sequence('public.video','idvideo')));
end;
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;
I've found it's much easier to work with lower case columns & table names, etc. with postgres, so I've made a few changes to your existing code :
CREATE OR REPLACE FUNCTION public."insertvideotag"
(
IN in_idvideo integer,
IN in_tagname VARCHAR(25)
)
RETURNS bigint AS
$$
DECLARE var_id bigint DEFAULT NULL;
BEGIN
--SELECT var_id := IDTag FROM Tag WHERE TagName = in_tagname;
SELECT idtag into var_id FROM tag WHERE tagname = in_tagname;
IF var_id IS NULL
THEN
INSERT INTO tag ( TagName )
VALUES( in_tagname );
--var_id := SELECT CURRVAL(pg_get_serial_sequence('public.tag','idtag'));
SELECT CURRVAL(pg_get_serial_sequence('public.tag','idtag')) into var_id;
END IF;
INSERT INTO video_has_tag
(
idvideo,
idtag
)
VALUES
(
in_idvideo,
var_id
);
SELECT CURRVAL(pg_get_serial_sequence('public.video','idvideo'));
END
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;
The remaining potential issues are the sequence names, and perhaps what value you'd like to return.