using xmlagg to find count to concatenate - sql

SELECT DISTINCT
ent.entity_key_id AS query1kid,
CAST(substr(rtrim(XMLAGG(xmlelement(e,alstd.relationship_desc
||
CASE
WHEN(alstd.joint_flag = 'No' AND ent.anonymous_flag = 'No') THEN ''
ELSE('('
||
CASE
WHEN alstd.joint_flag = 'Yes' THEN 'Joint'
ELSE ''
END
||
CASE
WHEN ent.anonymous_flag = 'Yes' THEN ',Anon'
ELSE ''
END
|| ')')
END
|| ': '
|| allocthm.allocation_description
|| '('
|| substr(allocthm.allocation_code,5,6) || ***count(ben.entity_key_ID)***
|| ')',',').extract('//text()')
ORDER BY
ent.entity_key_id
).getclobval(),','),1,4000) AS VARCHAR(4000) ) AS displayfiled1
FROM
er_datamart.allocation_theme allocthm
left JOIN er_datamart.allocation_stewardee alstd ON (allocthm.allocation_code = alstd.allocation_code and alstd.status_code <> 'F')
INNER JOIN er_datamart.entity_d ent ON alstd.entity_key_id = ent.entity_key_id
LEFT OUTER JOIN ER_DATAMART.ALLOCATION_BENEFICIARY ben ON ben.allocation_code = allocthm.allocation_code
GROUP BY
ent.entity_key_id
This gives me an error:
ORA-00937: not a single-group group function
I'm trying to find the count(ben.entity_key_ID) so that I can have it appended to my already functional query. Any help would be appreciated.

The problem seems to be the count() inside the xmlagg() - you have nested group functions that the group-by clause can't handle.
You can move the string concatenation into an inline view with its own group-by to get that count, and then perform the XML aggregation from that; something like:
SELECT
entity_key_id AS query1kid,
CAST(substr(rtrim(
XMLAGG(xmlelement(e, constructed_string, ',').extract('//text()')
ORDER BY entity_key_id
).getclobval(),','),1,4000) AS VARCHAR(4000) ) AS displayfiled1
FROM (
SELECT ent.entity_key_id,
alstd.relationship_desc
||
CASE
WHEN(alstd.joint_flag = 'No' AND ent.anonymous_flag = 'No') THEN ''
ELSE('('
||
CASE
WHEN alstd.joint_flag = 'Yes' THEN 'Joint'
ELSE ''
END
||
CASE
WHEN ent.anonymous_flag = 'Yes' THEN ',Anon'
ELSE ''
END
|| ')')
END
|| ': '
|| allocthm.allocation_description
|| '('
|| substr(allocthm.allocation_code,5,6) || count(ben.entity_key_ID)
|| ')' as constructed_string
FROM
allocation_theme allocthm
LEFT JOIN allocation_stewardee alstd
ON allocthm.allocation_code = alstd.allocation_code and alstd.status_code <> 'F'
INNER JOIN entity_d ent
ON alstd.entity_key_id = ent.entity_key_id
LEFT OUTER JOIN ALLOCATION_BENEFICIARY ben
ON ben.allocation_code = allocthm.allocation_code
GROUP BY
ent.entity_key_id,
alstd.relationship_desc,
alstd.joint_flag,
ent.anonymous_flag,
allocthm.allocation_description,
allocthm.allocation_code
)
GROUP BY
entity_key_id
The ELSE '' parts are redundant as that is the default behaviour but you might prefer to keep them for clarity/explicitness. Your joint/anon part might need a bit more work - if joint is No and anon is Yes you get (,Anon) - I think - which looks a bit odd, but might be what you need.

Related

How do I use concat and case switch together in PostgresSQL?

I am trying to use concat as function and inside the same function use case switch statements in PostgresSQL. The end goal is populating a table with a bunch of statements that change according to the case.
Does anybody know how to solve this?
I tried to use '+' and ',' but I always get the same Syntax error
(SELECT concat(cast(idpersonale as varchar(5)),' = '
,case when dstitolo is not null then dstitolo else '' end + ' '
,case when dsnome is not null then dsnome else '' end + ' '
,case when dscognome is not null then dscognome else '' end
dscognome)
FROM global.glb_personale
WHERE cch.glb_personale.idpersonale =inter.interventoeseguitoda) as InterventoEseguitoDa
FROM cch.pats_cch_interventi inter
WHERE (idtiporecord is null or not idtiporecord in('uti','int')) and
NOT idintervento IN (SELECT idint
FROM cch.glo_error_data_2
WHERE (iddb = 'cch') AND (cnerror = 1))
order by idintervento;
Concatenation works with || operator or concat(text1, text2, text3, ...) function. Every text literal can be also an expression which gives a type text, like a CASE clause:
demo:db<>fiddle
SELECT
'you' || 'can' || 'concat' || 'like' || 'that' AS concat1,
concat('or', 'like', 'that') AS concat2,
concat('Mix' || 'both', 'up') AS concat3,
concat(
'And',
CASE WHEN false THEN 'now' ELSE 'doint' END || 'this',
CASE
WHEN true THEN 'with' || 'some'
WHEN false THEN 'CASE'
ELSE 'clauses'
END
) AS concat4
That means in your case: Change concat(text1 + text2 + text3) to concat(text1, text2, text3) (because the texts are function arguments) or to text1 || text2 || text3
since you are using concat(), replace + to , instead.
(SELECT concat(cast(idpersonale as varchar(5)),' = '
,case when dstitolo is not null then dstitolo else '' end, ' '
,case when dsnome is not null then dsnome else '' end, ' '
,case when dscognome is not null then dscognome else '' end
dscognome)
FROM global.glb_personale
WHERE cch.glb_personale.idpersonale =inter.interventoeseguitoda) as InterventoEseguitoDa
FROM cch.pats_cch_interventi inter
WHERE (idtiporecord is null or not idtiporecord in('uti','int')) and
NOT idintervento IN (SELECT idint
FROM cch.glo_error_data_2
WHERE (iddb = 'cch') AND (cnerror = 1))
While the concatenation operator (||) and the function (concat) combine strings there is a big difference between them: how they handle nulls.
If any operand is null then the concatenation operator result is NULL.
If any operand is null then the concatenation function moves to the next operand, leaving previous and subsequent results as they would had the null operand not been present. Example:
with dscognome (idpersonale,dstitolo ,dsnome ,dscognome) as
( values (1,'a','b','c')
, (2,'d','e',null)
, (3,'f',null,'g')
, (4,null, 'h', 'i')
, (5,null, null, null)
)
select idpersonale
, dstitolo || dsnome || dscognome "using || opreator"
, concat(dstitolo,dsnome,dscognome) "using concat function"
from dscognome;
Since the entire purpose of the case structure is to handle nulls, using the concatenation function entirely negates it's requirement and the query can be written:
with dscognome (idpersonale,dstitolo ,dsnome ,dscognome) as
( values (1,'a','b','c')
, (2,'d','e',null)
, (3,'f',null,'g')
, (4,null, 'h', 'i')
, (5,null, null, null)
)
SELECT concat(cast(idpersonale as varchar),' = '
, dstitolo , ' '
, dsnome, ' '
, dscognome )
from dscognome;

What's wrong with this query? Doubling quotes error

ERROR: Syntax error at or near "''"
I searched about putting single quotes and this way seemed to be correct, doubling quotes, but it's not working
SELECT
cod_rede_credenciada_produto,
rep.des_cnpj_loja,
emf.des_header_estabelecimento,
TRIM(COALESCE (cep.uf_logradouro, '''')),
TRIM(COALESCE(cep.cidade_logradouro, '''')),
TRIM(COALESCE (cep.bairro_ini_logradouro, emf.complresidencia)),
TRIM(COALESCE (cep.desc_tipo_logradouro, '''') || '' '' || COALESCE (cep.titulo_logradouro, '''') || '' '' || COALESCE (cep.nome_logradouro, emf.ruaresidencia) || '', '' || COALESCE (emf.numresidencia, '''')),
pro.des_produto,
des_cnpj_loja
FROM
solucoes.slereprede_credenciada_produto rep
INNER JOIN
solucoes.sleproproduto pro ON rep.cod_produto = pro.cod_produto
LEFT JOIN
empresafilha emf ON emf.cgc = rep.des_cnpj_loja
LEFT JOIN
caecep_cep cep ON cep.cep_logradouro = emf.cepresidencia
This is one problem:
|| '' '' ||
If you want a space:
|| ' ' ||
If you want single quotes with a space between them:
|| ''' ''' ||

How to concatenate cols under special rules?

I want to select concatenated string, max 255 char. Original query, without shortenings, was:
SELECT
b.title
||
CASE
WHEN b.subtitle != '' THEN '. ' || b.subtitle
ELSE ''
END
||
CASE
WHEN b.cover = 'paperback' THEN ' P'
WHEN b.cover = 'hardcover' THEN ' K'
WHEN b.cover = 'spiral' THEN ' S'
ELSE ''
END
||
CASE
WHEN b.year > 0 THEN ' ' || b.year
ELSE ''
END
||
CASE
WHEN b.volume > 0 THEN ' ' || b.volume || '. osa'
ELSE ''
END
||
CASE
WHEN p.name != '' THEN ' ' || p.name
ELSE ''
END
AS name
FROM table b
JOIN table_p p
ON b.id = p.foreign_id;
Rule: concatenated string may be up to 255 chars
Prioritized fields:
b.title (text, up to 250 chars),
b.cover (2 chars),
b.year (4 char + space = 5),
b.volume (int<100 + additional string '. osa' = 7-8 chars).
Less important:
b.subtitle (text, up to 250 chars),
p.name (text, up to 150 chars )
All fields besides b.title may absent/be empty.
One approach I can think of: because three short mandatory fields (cover, year, volume) may be max 15 chars, everything other could be max 240 chars. Because p.name is not so important, I could concatenate and cut afterwards, if needed. So main objective with this approach: b.title + b.subtitle must fit into 240 chars. Looks like that:
SELECT
SUBSTR(
SUBSTR( b.title
||
CASE
WHEN b.subtitle != '' THEN '. ' || b.subtitle
ELSE ''
END,
0, 240 )
||
CASE
WHEN b.cover = 'paperback' THEN ' P'
WHEN b.cover = 'hardcover' THEN ' K'
WHEN b.cover = 'spiral' THEN ' S'
ELSE ''
END
||
CASE
WHEN b.year > 0 THEN ' ' || b.year
ELSE ''
END
||
CASE
WHEN b.volume > 0 THEN ' ' || b.volume || '. osa'
ELSE ''
END
||
CASE
WHEN p.name != '' THEN ' ' || p.name
ELSE ''
END,
0, 255 ) AS name
FROM table b
JOIN table_p p
ON b.id = p.foreign_id;
This approach is Ok-ish.
My question: is there better way to control every aspect of such concatenation?
I am using Postgresql 9.6

Pass column name as a parameter

I need to pass a column name from front end to back end in my code. i'm using c# with oracle and when i pass the column name as a parameter, it gives an error and it's because the column name is used as a string in here and i need to know how to fix this. here is my code,
PROCEDURE PR_GETCLIENTCONTRACTDATA(INSTRFIELD IN VARCHAR2,INSTRCONTRACTNO IN VARCHAR2,CUR_OUTPUT OUT T_CURSOR)--ADDED BY DIDULA 25/10/2017
IS
BEGIN
OPEN CUR_OUTPUT FOR
SELECT c.con_no,
DECODE (a.clm_cori,
'1', a.clm_cltitle || ' ' || a.clm_initialsfull || ' '
|| a.clm_name,
a.clm_name
) cliname,
a.clm_code,
( a.clm_permaddline1
|| '|'
|| a.clm_permaddline2
|| '|'
|| COALESCE (a.clm_permaddline3, a.clm_permaddline4)
|| '|'
|| NULLIF ((a.clm_permaddline4),
COALESCE (a.clm_permaddline3, a.clm_permaddline4)
)
) address
FROM leaseinfo.tblcontracts c, corpinfo.tblclientmain a
WHERE a.clm_code = c.con_clmcode
AND INSTRFIELD = INSTRCONTRACTNO; ***here INSTRFIELD is the column name
that i need to pass***
END PR_GETCLIENTCONTRACTDATA;
Whitelist the column names:
PROCEDURE PR_GETCLIENTCONTRACTDATA(
INSTRFIELD IN VARCHAR2,
INSTRCONTRACTNO IN VARCHAR2,
CUR_OUTPUT OUT T_CURSOR
)
IS
BEGIN
OPEN CUR_OUTPUT FOR
SELECT -- your select clauses
FROM leaseinfo.tblcontracts c,
INNER JOIN corpinfo.tblclientmain a -- ANSI join syntax
ON a.clm_code = c.con_clmcode
WHERE CASE INSTRFIELD
WHEN 'COLUMNA' THEN ColumnA
WHEN 'COLUMNB' THEN ColumnB
WHEN 'COLUMNC' THEN ColumnC
END = INSTRCONTRACTNO;
END PR_GETCLIENTCONTRACTDATA;
/
When you use OPEN cur FOR ... you can pass a string, i.e.
PROCEDURE PR_GETCLIENTCONTRACTDATA(INSTRFIELD IN VARCHAR2,INSTRCONTRACTNO IN VARCHAR2,CUR_OUTPUT OUT T_CURSOR)
IS
BEGIN
OPEN CUR_OUTPUT FOR
'SELECT c.con_no,
DECODE (a.clm_cori,
''1'', a.clm_cltitle || '' '' || a.clm_initialsfull || '' ''
|| a.clm_name,
a.clm_name
) cliname,
a.clm_code,
( a.clm_permaddline1
|| ''|''
|| a.clm_permaddline2
|| ''|''
|| COALESCE (a.clm_permaddline3, a.clm_permaddline4)
|| ''|''
|| NULLIF ((a.clm_permaddline4),
COALESCE (a.clm_permaddline3, a.clm_permaddline4)
)
) address
FROM leaseinfo.tblcontracts c
JOIN corpinfo.tblclientmain a ON a.clm_code = c.con_clmcode
WHERE '||DBMS_ASSERT.SIMPLE_SQL_NAME(INSTRFIELD)||' = :INSTRCONTRACTNO)'
USING INSTRCONTRACTNO;
END PR_GETCLIENTCONTRACTDATA;

Quoting literal causes no results to return

I have the following query
SELECT v.id
FROM visitors v
LEFT JOIN trackings t on v.id = t.visitor_id
WHERE v.app_id = 'xxx'
AND (field = quote_ident('app_name')
AND string_value ILIKE quote_nullable('test_app'))
Problem is that the query fails to return any results when I use quote_nullable on the string_value. It only works with quote_ident, but that is not the appropriate function as far as I understand here.
Example in pseudocode of how I am building the query serverside (using pattern matching on the filter (starts_with, ends_with etc.).
filter = user_params["filter"]
field = user_params["field"]
value = user_params["value"]
...
"starts_with" -> "(field = quote_ident('#{field}') AND string_value ILIKE '#{value}' || '%')"
"ends_with" -> "(field = quote_ident('#{field}') AND string_value ILIKE '%' || '#{value}')"
"contains" -> "(field = quote_ident('#{field}') AND string_value ILIKE '%' || '#{value}' || '%')"
"does_not_contain" -> "(field = quote_ident('#{field}') AND string_value NOT ILIKE '%' || '#{value}' || '%')"
Later in the DB, I invoke a stored procedure that looks something like
filter_length := COALESCE(array_length(filters, 1), 0);
filterstring := array_to_string(filters, ' OR ');
IF filterstring = '' or filterstring is null THEN
filterstring := '1=1';
END IF;
sql := format('
SELECT v.id FROM visitors v
LEFT JOIN trackings t on v.id = t.visitor_id
WHERE v.app_id = app_id and (%s)
group by v.id
having case when %s > 0 then count(v.id) = %s else true end
', filterstring, custom_filter_length, custom_filter_length);
RETURN QUERY EXECUTE sql;
Where filters is an array of conditions (the ones I built earlier)
Any ideas?