I have the following code that gets the monthly resignation rate of people in a company
---2021
Select
c.id,
c.name as Company,
------January
round(cast( float8 (CASE
--employeesatendof2021
((Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date Between '0001-01-01 00:00:00' and '2021-12-31 00:00:00'
and e.terminated_at is null
and p.is_support = false)
+
(Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date is null
and e.terminated_at is null
and p.is_support = false))
WHEN 0 THEN null
Else
--eMPLOYEEsEPARATIONS2021
(Select count(e2.id) from employee e2
inner join person p2 on e2.person = p2.id
where e2.company = c.id
and e2.terminated_at at time zone l.timezone Between '2021-01-01 00:00:00' and '2021-1-31 24:00:00'
and p2.is_support = false)::numeric /
--employeesatendof2021
((Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date Between '0001-01-01 00:00:00' and '2021-12-31 00:00:00'
and e.terminated_at is null
and p.is_support = false)
+
(Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date is null
and e.terminated_at is null
and p.is_support = false))* 100
end)as numeric),2) as January_Resignation_Rate,
----February
round(cast( float8 (CASE
--employeesatendof2021
((Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date Between '0001-01-01 00:00:00' and '2021-12-31 00:00:00'
and e.terminated_at is null
and p.is_support = false)
+
(Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date is null
and e.terminated_at is null
and p.is_support = false))
--employeeSeaprtions2021
WHEN 0 THEN null
Else
--eMPLOYEEsEPARATIONS2021
(Select count(e2.id) from employee e2
inner join person p2 on e2.person = p2.id
where e2.company = c.id
and e2.terminated_at at time zone l.timezone Between '2021-02-01 00:00:00' and '2021-2-28 24:00:00'
and p2.is_support = false)::numeric /
--employeesatendof2021
((Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date Between '0001-01-01 00:00:00' and '2021-12-31 00:00:00'
and e.terminated_at is null
and p.is_support = false)
+
(Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date is null
and e.terminated_at is null
and p.is_support = false))* 100
end)as numeric),2) as February_Resignation_Rate,
CASE l.locale
WHEN 'en-AU' THEN 'Australia'
WHEN 'en-SA' THEN 'South Africa'
WHEN 'en-SG' THEN 'Singapore'
WHEN 'en-NZ' THEN 'New Zealand'
WHEN 'en-HK' THEN 'Hongkong'
WHEN 'en-UK' THEN 'United Kingdom'
end as locale
from company c
inner join location l on l.company = c.id and l.parent = 0
where l.locale = 'en-AU'
and c.id = 2
It works fine and does what it's supposed to do
But I've been using and hard coding this part of the code alot
--employeesatendof2021
((Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date Between '0001-01-01 00:00:00' and '2021-12-31 00:00:00'
and e.terminated_at is null
and p.is_support = false)
+
(Select count(e.id) from employee e
inner join person p on e.person = p.id
where e.company = c.id
and e.start_date is null
and e.terminated_at is null
and p.is_support = false))
And I was wondering if there is a way to save this part of the script in a variable or function that I can use through out the script without hardcoding it?
Here conditional input is used for retrieving data. If any input parameter value is NULL or empty string then it'll not used for searching option. Prepare a query based on requirement and store it in variable. Then use RETURN QUERY EXECUTE variable_full_query for executing the query and return result. Always keep in mind that out parameter name and data type must be matched with select column name and data type. Use setof records means it returns multiple records.
-- Function: public.rpt_member_info(character varying, character varying, character varying, character varying, character varying)
DROP FUNCTION IF EXISTS public.rpt_member_info(character varying, character varying, character varying, character varying, character varying);
CREATE OR REPLACE FUNCTION public.rpt_member_info(
IN p_officeInfoId CHARACTER VARYING,
IN p_projectInfoId CHARACTER VARYING,
IN p_memberInfoId CHARACTER VARYING,
IN p_fromDate CHARACTER VARYING,
IN p_toDate CHARACTER VARYING,
OUT member_id BIGINT,
out office_info_id bigint,
out project_info_id bigint,
OUT member_no CHARACTER VARYING,
OUT member_name CHARACTER VARYING,
OUT membership_date TIMESTAMP WITHOUT TIME ZONE)
RETURNS SETOF record AS
$BODY$
-- declare local variables
DECLARE v_prepareQuery VARCHAR(21840) DEFAULT '';
v_officeInfo VARCHAR(150) DEFAULT '';
v_projectInfo VARCHAR(150) DEFAULT '';
v_memberInfo VARCHAR(150) DEFAULT '';
refcur refcursor default 'test';
BEGIN
/**************************History**************************/
-- Name : rpt_member_info
-- Created Date (dd/MM/yyyy): 20/01/2022
-- Created By : Rahul Biswas
-- Reason :
-- Execute : SELECT * FROM rpt_member_info('101', '1', '1', '2022-01-01', '2022-01-31');
/***********************************************************/
-- prepare variable data based on given input
IF(p_officeInfoId != '' AND p_officeInfoId != 'null' AND p_officeInfoId IS NOT NULL)
THEN
v_officeInfo = CONCAT(' AND mi.office_info_id = ', p_officeInfoId, CHR(10));
END IF;
IF(p_projectInfoId != '' AND p_projectInfoId != 'null' AND p_projectInfoId IS NOT NULL)
THEN
v_projectInfo = CONCAT(' AND mi.project_info_id = ', p_projectInfoId, CHR(10));
END IF;
IF(p_memberInfoId != '' AND p_memberInfoId != 'null' AND p_memberInfoId IS NOT NULL)
THEN
v_memberInfo = CONCAT(' AND mi.id = ', p_memberInfoId, CHR(10));
END IF;
-- prepare query
v_prepareQuery := CONCAT('SELECT mi.id member_id
, mi.office_info_id
, mi.project_info_id
, mi.member_no
, mi.member_name
, mi.membership_date
FROM member_info mi
WHERE 1 = 1', CHR(10)
, v_officeInfo
, v_projectInfo
, v_memberInfo
, 'AND mi.membership_date BETWEEN ', '''', p_fromDate, '''', ' AND ', '''', p_toDate, '''', ';');
RETURN QUERY EXECUTE v_prepareQuery;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Execute the function with specific input value
SELECT * FROM rpt_member_info('101', '1', '1', '2022-01-01', '2022-01-31');
Execute the function when a param value is null or empty. This will return all values of a particular column
SELECT * FROM rpt_member_info('', 'null', NULL, '2022-01-01', '2022-01-31');
Please check this url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=e0f26dfa36b4636f70bd5267dab29979
Also use variable in a query and return result where all parameter value is available and prepare query isn't stored in a variable.
-- Function: public.rpt_member_info(character varying, character varying, character varying, character varying, character varying)
DROP FUNCTION IF EXISTS public.rpt_member_info(character varying, character varying, character varying, character varying, character varying);
CREATE OR REPLACE FUNCTION public.rpt_member_info(
IN p_officeInfoId BIGINT,
IN p_projectInfoId BIGINT,
IN p_memberInfoId BIGINT,
IN p_fromDate TIMESTAMP WITHOUT TIME ZONE,
IN p_toDate TIMESTAMP WITHOUT TIME ZONE,
OUT member_id BIGINT,
out office_info_id bigint,
out project_info_id bigint,
OUT member_no CHARACTER VARYING,
OUT member_name CHARACTER VARYING,
OUT membership_date TIMESTAMP WITHOUT TIME ZONE)
RETURNS SETOF record AS
$BODY$
-- declare local variables
DECLARE v_prepareQuery VARCHAR(21840) DEFAULT '';
v_officeInfo VARCHAR(150) DEFAULT '';
v_projectInfo VARCHAR(150) DEFAULT '';
v_memberInfo VARCHAR(150) DEFAULT '';
refcur refcursor default 'test';
BEGIN
/**************************History**************************/
-- Name : rpt_member_info
-- Created Date (dd/MM/yyyy): 20/01/2022
-- Created By : Rahul Biswas
-- Reason :
-- Execute : SELECT * FROM rpt_member_info(101, 1, 1, '2022-01-01', '2022-01-31');
/***********************************************************/
-- execute query
RETURN QUERY SELECT mi.id member_id
, mi.office_info_id
, mi.project_info_id
, mi.member_no
, mi.member_name
, mi.membership_date
FROM member_info mi
WHERE mi.office_info_id = p_officeInfoId
AND mi.project_info_id = p_projectInfoId
AND mi.id = p_memberInfoId
AND mi.membership_date BETWEEN p_fromDate AND p_toDate;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Execute the function
SELECT * FROM rpt_member_info(101, 1, 1, '2022-01-01', '2022-01-31');
Please check this url https://dbfiddle.uk/?rdbms=postgres_11&fiddle=7c8ed1d02f51f5df4e263cc36a2d50e8
Related
How can i achieve dynamic order by column and sort direction in a postgresql function.
Here is what i have so far:
CREATE OR REPLACE FUNCTION get_urls_by_crawl_id(
p_account_id character varying(64),
p_crawl_id character varying(64),
p_sort_column character varying(30),
p_sort_direction character varying(30),
p_page integer,
p_total integer
)
RETURNS TABLE(id character varying(64), source_url text, http_status_code integer, ref_cnt integer) AS $BODY$
BEGIN
RETURN QUERY SELECT u.id, u.source_url, u.http_status_code, u.ref_cnt FROM url AS u
JOIN crawl AS c ON(u.crawl_id = c.id)
JOIN site AS s ON(c.site_id = s.id)
JOIN person AS p ON(s.person_id = p.id)
WHERE p.account_id = p_account_id AND u.crawl_id = p_crawl_id AND u.secured = 0
ORDER BY p_sort_column (CASE WHEN p_sort_direction = 'ASC' THEN ASC ELSE DESC END) LIMIT p_total OFFSET (p_page * p_total);
END;
$BODY$ LANGUAGE plpgsql;
the psql client returns this error:
ERROR: syntax error at or near "ASC"
LINE 16: ...t_column (CASE WHEN p_sort_direction = 'ASC' THEN ASC ELSE D...
I have tried multiple forms of the CASE statement but none seems to work.
Use two different order by keys:
ORDER BY (case when p_sort_direction = 'ASC' then p_sort_column end)
asc,
p_sort_column desc
LIMIT p_total OFFSET (p_page * p_total);
Note that you have another problem. p_sort_column is a string. You would need to use dynamic SQL to insert it into the code.
Alternatively, you can use a series of cases:
order by (case when p_sort_column = 'column1' and p_sort_direction = 'ASC' then column1 end) asc,
(case when p_sort_column = 'column1' and p_sort_direction = 'DESC' then column1 end) desc,
(case when p_sort_column = 'column2' and p_sort_direction = 'ASC' then column2 end) asc,
(case when p_sort_column = 'column2' and p_sort_direction = 'DESC' then column2 end) desc,
. . .
Related to my question yesterday which was answered by #a_horse_with_no_name.
The following query works..
SELECT
CAST(FC.Life1monthofbirth AS VARCHAR(2)) || '/' || CAST(FC.Life1yearofbirth AS VARCHAR(4))
FROM
public.factclient FC;
...but when I have essentially the same code in a function...
,CAST(FC.Life1monthofbirth AS VARCHAR(2)) || '/' || CAST(FC.Life1yearofbirth AS VARCHAR(4)) AS Life1DOB
I get this error:
ERROR: operator is not unique: unknown / unknown
LINE 25: ,CAST(FC.Life1monthofbirth AS VARCHAR(2)) || '/' || CAST...
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
I don't understand why the first scenario works, but the second one doesn't. I'm using Postgres version 12 and writing my queries using pgAdmin 4.13
I have added the complete function code below. Apologies if it is not very readable.
CREATE OR REPLACE FUNCTION public.postgres_termpositionrawdata_ver2(
p_provider character varying,
p_date date,
p_correlationid character DEFAULT NULL::bpchar,
p_type character varying DEFAULT NULL::character varying,
p_productexclusionset character varying DEFAULT 'LevelTermPosition'::character varying,
p_ctmoptions character varying DEFAULT 'WOM'::character varying,
p_test character varying DEFAULT 'No'::character varying,
p_testaccountlogon character varying DEFAULT NULL::character varying,
p_averagetopx integer DEFAULT 5,
p_filterquote integer DEFAULT NULL::integer,
p_includeduplicatequotes character varying DEFAULT 'Yes'::character varying,
p_ignoreglobalpermissions character varying DEFAULT 'Yes'::character varying,
p_ignoredefaultproductexclusions character varying DEFAULT 'No'::character varying,
p_ignorelowstartresponseindicator character varying DEFAULT 'No'::character varying,
p_ignorevariableresponseindicator character varying DEFAULT 'No'::character varying)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
--DECLARE CONSTANTS
DECLARE
--DECLARE VARIABLES
v_Status varchar(10) = 'Success';BEGIN
-- assume success
-- SET CorrelationID
IF p_CorrelationId IS NULL THEN p_CorrelationId := public.swf_newid();
END IF;
DROP TABLE IF EXISTS FilteredRequests;
DROP TABLE IF EXISTS FilteredResponses;
DROP TABLE IF EXISTS Ranks;
CREATE TEMP TABLE FilteredRequests AS
SELECT * FROM public.dblink('srv_exchangemart',
'SELECT
FRQ.QuoteID
,DEO.Name AS OrganisationName
,DEO.Postcode AS OrganisationPostcode
,DEO.FRN AS OrganisationFRN
,DDQ.Date AS RequestDate
,DLB.LifeBasis
,DDC.Date AS CommencementDate
,CASE
WHEN FRQ.Term <> 0
THEN CAST (FRQ.Term AS Varchar(5)) || "Years"
WHEN FRQ.TermToAge <> 0
THEN "To Age " || CAST(FRQ.TermToAge AS Varchar(5))
ELSE "Unknown"
END AS PolicyTerm
,DQF.QuotationFor
,LCR.LifeCriticalIllnessRiskRelationship
,TDP.TotalPermanentDisabilityCover
,CB.CommissionBasis
,DCT.CommissionType
,DG1.Gender AS Life1Gender
,CONCAT_WS('|', FC.Life1MonthOfBirth, FC.Life1YearOfBirth) AS Life1DOB
,FC.Life1Age AS Life1Age
,FC.Life1QuarterlyAge
,DS1.Smoker AS Life1Smoker
,DO1.Occupation AS Life1Occupation
,DG2.Gender AS Life2Gender
,CONCAT_WS('|', FC.Life2MonthOfBirth, FC.Life2YearOfBirth) AS Life2DOB
,FC.Life2Age AS Life2Age
,FC.Life2QuarterlyAge
,DS2.Smoker AS Life2Smoker
,DO2.Occupation AS Life2Occupation
,DEP.PanelId
FROM ExchangeMart.dbo.FactRequest FRQ
INNER JOIN ExchangeMart.dbo.dimExchangeOrganisation DEO ON FRQ.ExchangeOrganisationID = DEO.ExchangeOrganisationId
INNER JOIN ExchangeMart.dbo.dimExchangeUser DEU ON FRQ.ExchangeUserId = DEU.ExchangeUserId
INNER JOIN ExchangeMart.dbo.dimLifeBasis DLB ON FRQ.LifeBasisId = DLB.LifeBasisID
INNER JOIN ExchangeMart.dbo.dimTotalPermanentDisabilityCover TDP ON FRQ.TotalPermanentDisabilityCoverId = TDP.TotalPermanentDisabilityCoverID
INNER JOIN ExchangeMart.dbo.dimDate DDQ ON FRQ.QuotationDateID = DDQ.DateID
INNER JOIN ExchangeMart.dbo.dimDate DDC ON FRQ.CommencementDateID = DDC.DateID
INNER JOIN ExchangeMart.dbo.dimExchangePanel DEP ON FRQ.ExchangePanelId = DEP.ExchangePanelId
INNER JOIN ExchangeMart.dbo.dimCommissionBasis CB ON FRQ.CommissionBasisId = CB.CommissionBasisId
INNER JOIN ExchangeMart.dbo.dimCommissionType DCT ON FRQ.CommissionTypeId = DCT.CommissionTypeId
INNER JOIN ExchangeMart.dbo.factClient FC ON FRQ.QuoteID = FC.QuoteID
INNER JOIN ExchangeMart.dbo.dimLifeCriticalIllnessRiskRelationship LCR ON FRQ.LifeCriticalIllnessRiskRelationshipId = LCR.LifeCriticalIllnessRiskRelationshipId
INNER JOIN ExchangeMart.dbo.dimQuotationFor DQF ON FRQ.QuotationForId = DQF.QuotationForId
INNER JOIN ExchangeMart.dbo.dimKeyPerson DKP ON FRQ.KeyPersonId = DKP.KeyPersonId
INNER JOIN ExchangeMart.dbo.dimBenefitBasis DBB ON FRQ.BenefitBasisId = DBB.BenefitBasisId
INNER JOIN ExchangeMart.dbo.dimIntegrator DI ON FRQ.ExchangeIntegratorId = DI.ExchangeIntegratorId
INNER JOIN ExchangeMart.dbo.dimGender DG1 ON FC.Life1GenderID = DG1.GenderID
INNER JOIN ExchangeMart.dbo.dimGender DG2 ON FC.Life2GenderID = DG2.GenderID
INNER JOIN ExchangeMart.dbo.dimSmoker DS1 ON FC.Life1SmokerID = DS1.SmokerID
INNER JOIN ExchangeMart.dbo.dimSmoker DS2 ON FC.Life2SmokerID = DS2.SmokerID
INNER JOIN ExchangeMart.dbo.dimOccupation DO1 ON FC.Life1OccupationId = DO1.OccupationID
INNER JOIN ExchangeMart.dbo.dimOccupation DO2 ON FC.Life2OccupationId = DO2.OccupationID
WHERE DDQ.Date = p_Date
AND FRQ.ProductTypeId IN (53) -- TERM ONLY
AND KeyPerson = "No" -- Not Business
AND DBB.BenefitBasis = "Benefit Led" -- ONLY RETURNS BENEFIT LED QUOTES
AND (DEU.LogonId = p_TestAccountLogon OR (p_TestAccountLogon IS NULL
AND DI.DefaultExclusion = "No"
AND DEO.DefaultExclusion = "No"))
AND ( (p_IncludeDuplicateQuotes = "No" AND FRQ.IsDuplicateQuote = 0)
OR (p_IncludeDuplicateQuotes = "Yes"))
AND (FRQ.QuoteId = p_FilterQuote OR p_FilterQuote IS NULL)
AND ( (p_CTMOptions = "CTM Only" AND DEU.LogonId = "CTM000")
OR (p_CTMOptions = "Exclude CTM" AND DEU.LogonId != "CTM000")
OR (p_CTMOptions = "WOM")
)
')
AS DATA(QuoteID INTEGER, OrganisationName character varying, OrganisationPostcode character varying, OrganisationFRN character varying
, RequestDate date, LifeBasis character varying, CommencementDate date, PolicyTerm character varying, QuotationFor character varying
, LifeCriticalIllnessRiskRelationship character varying, TotalPermanentDisabilityCover character varying, COmmissionBasis character varying
, CommisionType character varying, Life1Gender character varying, Life1DOB character varying, Life1Age INTEGER, Life1QuarterlyAge numeric(12,2)
, Life1Smoker character varying, Life1Occupation character varying, Life2Gender character varying, Life2Age INTEGER
, Life2QuarterlyAge numeric(12,2), Life2Smoker character varying, Life2Occupation character varying);
SELECT * FROM FilteredRequests;
END;
$BODY$;
The error message says you are dividing two items, while your intent is to concatenate them.
It implies that you are running this query using EXECUTE, so the query text is build then run, and the result of the concatenation you wrote is executed, leading to the unexpected division.
You would have to transform the query so that the result of the concatenation - which will be executed - is the desired concatenation. Something like:
EXECUTE 'select fieldA' || '||''/''||' || 'fieldB from myTable';
I have an function in postgresql. I don't know whether I made it a bit complicated. I have a select query and I need to append some more expressions into that. I am passing those expressions as input parameters.
CREATE OR REPLACE FUNCTION get_alldocuments(currUser text,
queryExp text,
query0 text,
query1 text)
RETURNS TABLE(fileleafref text,contenttypename text,fsobjtype text,id integer,conttypeid integer,contenttypeid integer,docicon text,encodedabsurl text,refId integer,filesizedisplay integer,created date,
createdby text,version text) AS $$
BEGIN
RETURN QUERY EXECUTE 'select distinct c.fileleafref, ct.contenttypename,c.fsobjtype,c.id,c.conttypeid,d.contenttypeid,c.docicon,c.encodedabsurl,d.ref_id as RefId,c.filesizedisplay,d.created,
d.createdby,c.version from public.documents d inner join public.documentcontent c on d.id=c.documentid inner join conttype ct on ct.id = c.conttypeid
where '
|| quote_literal(queryExp)
|| ' versionflag=true and c.permissiontype ="1" or (c.permissiontype="3" and c.createdby =currUser) order by d.created desc limit 300)
union
(select distinct c.fileleafref, ct.contenttypename,c.fsobjtype,c.id,c.conttypeid,d.contenttypeid,c.docicon,c.encodedabsurl,d.ref_id as RefId,c.filesizedisplay,d.created,
d.createdby,c.version from public.documents d inner join public.documentcontent c on d.id=c.documentid inner join conttype ct on ct.id = c.conttypeid '
|| quote_ident(query0) ||
' where' || quote_ident(queryExp) || 'versionflag=true '|| quote_ident(query1) ||
' order by d.created desc limit 300)';
END
$$ LANGUAGE plpgsql;
in the function I am trying to append different expressions at different places of my select query.
The input variables are
currUser - user1
queryExp - metadata #> ''{"Year":"2011"}'' and metadata #> ''{"Country":"US"}'' and
query0 - ,jsonb_array_elements(c.securitygppermission) as e(users) ,jsonb_array_elements_text(c.userspermission) as p(perm)
query1 - and (c.permissiontype='2' and e.users ->>'Deny'='false' and e.users ->>'Allow'='true' and e.users ->>'GroupName'='Manager' and p.perm ='user1')
The complete select query sample is
select * from get_alldocuments('user1','metadata #> ''{"Year":"2011"}'' and metadata #> ''{"Country":"US"}'' and ',',jsonb_array_elements(c.securitygppermission) as e(users) ,jsonb_array_elements_text(c.userspermission) as p(perm)','and (c.permissiontype="2" and e.users ->>"Deny"="false" and e.users ->>"Allow"="true" and e.users ->>"GroupName"="Manager" and p.perm ="user1")')
While executing the function its showing an error like
ERROR: syntax error at or near "versionflag"
LINE 3: ...11"}'' and metadata #> ''{"Country":"US"}'' and ' versionfla...
^
QUERY: select distinct c.fileleafref, ct.contenttypename,c.fsobjtype,c.id,c.conttypeid,d.contenttypeid,c.docicon,c.encodedabsurl,d.ref_id as RefId,c.filesizedisplay,d.created,
d.createdby,c.version from public.documents d inner join public.documentcontent c on d.id=c.documentid inner join conttype ct on ct.id = c.conttypeid
where 'metadata #> ''{"Year":"2011"}'' and metadata #> ''{"Country":"US"}'' and ' versionflag=true and c.permissiontype ="1" or (c.permissiontype="3" and c.createdby =currUser) order by d.created desc limit 300)
union
(select distinct c.fileleafref, ct.contenttypename,c.fsobjtype,c.id,c.conttypeid,d.contenttypeid,c.docicon,c.encodedabsurl,d.ref_id as RefId,c.filesizedisplay,d.created,
d.createdby,c.version from public.documents d inner join public.documentcontent c on d.id=c.documentid inner join conttype ct on ct.id = c.conttypeid ",jsonb_array_elements(c.securitygppermission) as e(users) ,jsonb_array_elements_text(c.userspermission) as p(perm)" where"metadata #> '{""Year"":""2011""}' and metadata #> '{""Country"":""US""}' and "versionflag=true "and (c.permissiontype=2 and e.users ->>""Deny""=""false"" and e.users ->>""Allow""=""true"" and e.users ->>""GroupName""=""Manager"" and p.perm =""user1"")" order by d.created desc limit 300)
CONTEXT: PL/pgSQL function get_alldocuments(text,text,text,text) line 4 at RETURN QUERY
********** Error **********
What is the issue? Am I concatenated in the correct way?
If I parse your query correctly then this parts looks wrong:
where 'metadata #> ''{"Year":"2011"}'' and metadata #> ''{"Country":"US"}'' and ' versionflag=true
Between the where and versionflag=true you have a string literal (single quoted string). So to postgresql it basically looks like you wrote where 'string' versionflag=true which doesn't make any sense. Looks like a comparison and a logical operator are missing.
I resolve the issue. 'quote_indent' created the issue. The final function is
CREATE OR REPLACE FUNCTION public.get_alldocuments(
curruser text,
queryexp text,
query0 text,
query1 text)
RETURNS TABLE(fileleafref character varying, contenttypename character varying, fsobjtype character varying, id integer, conttypeid integer, contenttypeid character varying, docicon character varying, encodedabsurl character varying, refid character varying, filesizedisplay integer, created date, createdby character varying, version character varying)
LANGUAGE 'plpgsql'
COST 100
VOLATILE
ROWS 1000
AS $BODY$
BEGIN
RETURN QUERY EXECUTE '(select distinct c.fileleafref, ct.contenttypename,c.fsobjtype,c.id,c.conttypeid,cast(d.contenttypeid as varchar) as contenttypeid,c.docicon,c.encodedabsurl,d.ref_id as RefId,c.filesizedisplay,cast(d.created as date) created,
d.createdby,c.version from public.documents d inner join public.documentcontent c on d.id=c.documentid inner join conttype ct on ct.id = c.conttypeid
where '
|| queryExp
|| ' versionflag=true and c.permissiontype=''1'' or (c.permissiontype=''3'' and c.createdby =''|| curruser ||'') order by created desc limit 300)
union
(select distinct c.fileleafref, ct.contenttypename,c.fsobjtype,c.id,c.conttypeid,cast(d.contenttypeid as varchar) as contenttypeid,c.docicon,c.encodedabsurl,d.ref_id as RefId,c.filesizedisplay,cast(d.created as date) created,
d.createdby,c.version from public.documents d inner join public.documentcontent c on d.id=c.documentid inner join conttype ct on ct.id = c.conttypeid '
|| query0 ||
' where ' || queryExp || ' versionflag=true ' || query1 ||
'order by created desc limit 300);';
END
$BODY$;
The search filter in the function below doesn't seem to work. If I don't provide a search parameter, it works, otherwise I get no recordset back. I'm assuming I am making a mess of the single-quoting and ILIKE, but not sure how to re-write this properly. Suggestions?
CREATE OR REPLACE FUNCTION get_operator_basic_by_operator(
_operator_id UUID DEFAULT NULL,
_search TEXT DEFAULT NULL,
_page_number INTEGER DEFAULT 1,
_page_size INTEGER DEFAULT 10,
_sort_col TEXT DEFAULT 'username',
_sort_dir TEXT DEFAULT 'asc',
_include_deleted BOOLEAN DEFAULT FALSE
)
RETURNS TABLE (
id UUID,
party_id UUID,
party_name TEXT,
username VARCHAR(32),
profile_picture_uri VARCHAR(512),
first_name VARCHAR(64),
last_name VARCHAR(64),
street VARCHAR(128),
specifier VARCHAR(128),
city VARCHAR(64),
state VARCHAR(2),
zipcode VARCHAR(9),
primary_email CITEXT,
primary_phone VARCHAR(10),
secondary_email CITEXT,
secondary_phone VARCHAR(10),
last_login TIMESTAMP WITH TIME ZONE,
created TIMESTAMP WITH TIME ZONE,
deleted TIMESTAMP WITH TIME ZONE
)
AS $$
DECLARE
_offset BIGINT;
BEGIN
IF (_page_number < 1 OR _page_number IS NULL) THEN
RAISE EXCEPTION '_page_number cannot be null or less than 1.';
END IF;
IF (_page_size < 1 OR _page_size IS NULL) THEN
RAISE EXCEPTION '_page_size cannot be null or less than 1.';
END IF;
IF (_sort_dir <> 'asc' AND _sort_dir <> 'desc') THEN
RAISE EXCEPTION '_sort_dir must be "asc" or "desc".';
END IF;
_offset := (_page_size * (_page_number-1));
RETURN QUERY EXECUTE '
SELECT
o.id,
p.id,
p.party_name,
o.username,
o.profile_picture_uri,
o.first_name,
o.last_name,
o.street,
o.specifier,
o.city,
o.state,
o.zipcode,
o.primary_email,
o.primary_phone,
o.secondary_email,
o.secondary_phone,
o.last_login,
o.created,
o.deleted
FROM
operator o
LEFT JOIN
party p ON o.party_id = p.id
WHERE ( -- include all or only those active, based on _include_deleted
$1 OR o.deleted > statement_timestamp()
)
AND o.party_id IN ( -- limit to operators in same party
SELECT oi.party_id FROM operator oi WHERE oi.id = $2
)
AND ( -- use optional search filter
$3 IS NULL
OR
o.username ILIKE ''%$3%''
OR
o.first_name ILIKE ''%$3%''
OR
o.last_name ILIKE ''%$3%''
OR
o.primary_email ILIKE ''%$3%''
)
ORDER BY ' || quote_ident(_sort_col) || ' ' || _sort_dir || '
LIMIT
$4
OFFSET
$5'
USING _include_deleted, _operator_id, _search, _page_size, _offset;
END;
The parameter is inserted as a text with quotes, so you should use it this way:
...
o.username ILIKE concat(''%'', $3, ''%'')
...
Personally I would use format() and dollar-quotes.
Strings handles as is, you should to pass ready to use values:
EXECUTE '... o.username ILIKE $3 ...' using ..., '%' || _search || '%', ...
We do have a small data warehouse in PostgreSQL database and I have to document all the tables.
I thought I can add a comment to every column and table and use pipe "|" separator to add more attributes. Then I can use information schema and array function to get documentation and use any reporting software to create desired output.
select
ordinal_position,
column_name,
data_type,
character_maximum_length,
numeric_precision,
numeric_scale,
is_nullable,
column_default,
(string_to_array(descr.description,'|'))[1] as cs_name,
(string_to_array(descr.description,'|'))[2] as cs_description,
(string_to_array(descr.description,'|'))[3] as en_name,
(string_to_array(descr.description,'|'))[4] as en_description,
(string_to_array(descr.description,'|'))[5] as other
from
information_schema.columns columns
join pg_catalog.pg_class klass on (columns.table_name = klass.relname and klass.relkind = 'r')
left join pg_catalog.pg_description descr on (descr.objoid = klass.oid and descr.objsubid = columns.ordinal_position)
where
columns.table_schema = 'data_warehouse'
order by
columns.ordinal_position;
It is a good idea or is there better approach?
Unless you must include descriptions of the system tables, I wouldn't try to shoehorn your descriptions into pg_catalog.pg_description. Make your own table. That way you get to keep the columns as columns, and not have to use clunky string functions.
Alternatively, consider adding specially formatted comments to your master schema file, along the lines of javadoc. Then write a tool to extract those comments and create a document. That way the comments stay close to the thing they're commenting, and you don't have to mess with the database at all to produce the report. For example:
--* Used for authentication.
create table users
(
--* standard Rails-friendly primary key. Also an example of
--* a long comment placed before the item, rather than on the
--* the same line.
id serial primary key,
name text not null, --* Real name (hopefully)
login text not null, --* Name used for authentication
...
);
Your documentation tool reads the file, looks for the --* comments, figures out what comments go with what things, and produces some kind of report, e.g.:
table users: Used for authentication
id: standard Rails-friendly primary key. Also an example of a
long comment placed before the item, rather than on the same
line.
name: Real name
login: Name used for authentication
You might note that with appropriate comments, the master schema file itself is a pretty good report in its own right, and that perhaps nothing else is needed.
If anyone interested, here is what I've used for initial load for my small documentation project. Documentation is in two tables, one for describing tables and one for describing columns and constraints. I appreciate any feedback.
/* -- Initial Load - Tables */
drop table dw_description_table cascade;
create table dw_description_table (
table_description_key serial primary key,
physical_full_name character varying,
physical_schema_name character varying,
physical_table_name character varying,
Table_Type character varying, -- Fact Dimension ETL Transformation
Logical_Name_CS character varying,
Description_CS character varying,
Logical_Name_EN character varying,
Description_EN character varying,
ToDo character varying,
Table_Load_Type character varying, --Manually TruncateLoad AddNewRows
Known_Exclusions character varying,
Table_Clover_Script character varying
);
insert into dw_description_table (physical_full_name, physical_schema_name, physical_table_name) (
select
table_schema || '.' || table_name as physical_full_name,
table_schema,
table_name
from
information_schema.tables
where
table_name like 'dw%' or table_name like 'etl%'
)
/* -- Initial Load - Columns */
CREATE TABLE dw_description_column (
column_description_key serial,
table_description_key bigint,
physical_full_name text,
physical_schema_name character varying,
physical_table_name character varying,
physical_column_name character varying,
ordinal_position character varying,
column_default character varying,
is_nullable character varying,
data_type character varying,
logical_name_cs character varying,
description_cs character varying,
logical_name_en character varying,
description_en character varying,
derived_rule character varying,
todo character varying,
pk_name character varying,
fk_name character varying,
foreign_table_name character varying,
foreign_column_name character varying,
is_primary_key boolean,
is_foreign_key boolean,
CONSTRAINT dw_description_column_pkey PRIMARY KEY (column_description_key ),
CONSTRAINT fk_dw_description_table_key FOREIGN KEY (table_description_key)
REFERENCES dw_description_table (table_description_key) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
insert into dw_description_column (
table_description_key ,
physical_full_name ,
physical_schema_name ,
physical_table_name ,
physical_column_name ,
ordinal_position ,
column_default ,
is_nullable ,
data_type ,
logical_name_cs ,
description_cs ,
logical_name_en ,
description_en ,
derived_rule ,
todo ,
pk_name ,
fk_name ,
foreign_table_name ,
foreign_column_name ,
is_primary_key ,
is_foreign_key )
(
with
dw_constraints as (
SELECT
tc.constraint_name,
tc.constraint_schema || '.' || tc.table_name || '.' || kcu.column_name as physical_full_name,
tc.constraint_schema,
tc.table_name,
kcu.column_name,
ccu.table_name AS foreign_table_name,
ccu.column_name AS foreign_column_name,
TC.constraint_type
FROM
information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu ON (tc.constraint_name = kcu.constraint_name and tc.table_name = kcu.table_name)
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
WHERE
constraint_type in ('PRIMARY KEY','FOREIGN KEY')
AND tc.constraint_schema = 'bizdata'
and (tc.table_name like 'dw%' or tc.table_name like 'etl%')
group by
tc.constraint_name,
tc.constraint_schema,
tc.table_name,
kcu.column_name,
ccu.table_name ,
ccu.column_name,
TC.constraint_type
)
select
dwdt.table_description_key,
col.table_schema || '.' || col.table_name || '.' || col.column_name as physical_full_name,
col.table_schema as physical_schema_name,
col.table_name as physical_table_name,
col.column_name as physical_column_name,
col.ordinal_position,
col.column_default,
col.is_nullable,
col.data_type,
null as Logical_Name_CS ,
null as Description_CS ,
null as Logical_Name_EN,
null as Description_EN ,
null as Derived_Rule ,
null as ToDo,
dwc1.constraint_name pk_name,
dwc2.constraint_name as fk_name,
dwc2.foreign_table_name,
dwc2.foreign_column_name,
case when dwc1.constraint_name is not null then true else false end as is_primary_key,
case when dwc2.constraint_name is not null then true else false end as foreign_key
from
information_schema.columns col
join dw_description_table dwdt on (col.table_schema || '.' || col.table_name = dwdt.physical_full_name )
left join dw_constraints dwc1 on ((col.table_schema || '.' || col.table_name || '.' || col.column_name) = dwc1.physical_full_name and dwc1.constraint_type = 'PRIMARY KEY')
left join dw_constraints dwc2 on ((col.table_schema || '.' || col.table_name || '.' || col.column_name) = dwc2.physical_full_name and dwc2.constraint_type = 'FOREIGN KEY')
where
col.table_name like 'dw%' or col.table_name like 'etl%'
)