Postgres 9.1's concat_ws equivalent in Amazon redshift - sql

I've got a query originally written for pg9.1 that I'm trying to fix for use on redshift as follows
select concat_ws(' | ', p.gb_id, p.aro_id, p.gb_name) c from (
select ca.p_id,
avg(ca.ab) as ab
from public.fca
join temp_s_ids s on ca.s_id = s.s_id
group by ca.p_id
) as x
join public.dim_protein as p on x.protein_id = p.protein_id;";
I've been trying to test it out on my own, but as it is created from temporary tables that are created by a php session, I haven't had any luck yet. However, my guess is that the concat_ws function isn't working as expected in redshift.

I don't believe there is an equivalent in redshift. You will have to roll your own. If there are no NULLS you can just use the concatenation operator ||:
SELECT p.gb_id || ' | ' || p.aro_id || ' | ' || p.gb_name c
FROM...
If you have to worry about nulls (and its separator):
SELECT CASE WHEN p.gb_id IS NOT NULL THEN p.gb_id || ' | ' END || CASE WHEN p.aro_id IS NOT NULL THEN p.aro_id || ' | ' END || COALESCE(p.gb_name, '') c
FROM
Perhaps that can be simplified, but I believe it will do the trick.

To handle NULLs, you can do:
select trim('|' from
coalesce('|' || p.gb_id) ||
coalesce('|' || p.p.aro_id) ||
coalesce('|' || p.gb_name)
)
from . . .

Related

SQL Sylob return a boolean

I am currently discovering the Sylob 5 ERP and I would like some help on one of my queries because the Sylob support is overwhelmed with demands.
I would like my query to display if one of the operations in my fabrication order is late.
I already have a query that displays every operations and if it's late or not:
SELECT
distinct
ordreFabrication.codeOF as Code_OF,
operationOrdreFabrication.libelle as Libelle,
operationOrdreFabrication.centreCharge.libelle || ' (' ||
operationOrdreFabrication.centreCharge.code || ')' as Centre_charge,
operationOrdreFabrication.dateDebutPrevue as Début_prévu,
(
CASE
WHEN current_date() > operationOrdreFabrication.dateDebutPrevue and
operationOrdreFabrication.etatAvancementOperationOF = '0' THEN ('<div style=''color:red''>' ||
'Retard sur le début' || ' </div>')
WHEN current_date() > operationOrdreFabrication.dateFinPrevue and
operationOrdreFabrication.etatAvancementOperationOF != '2' THEN ('<div style=''color:red''>' ||
'Retard sur la fin' || ' </div>')
ELSE ('Aucun retard')
END
) as Retard,
operationOrdreFabrication.dateDebutReelle as Début_Réel,
operationOrdreFabrication.dateFinPrevue as Fin_prévue
FROM
OperationOrdreFabricationEntite as operationOrdreFabrication
left outer join operationOrdreFabrication.ordreFabrication as ordreFabrication
WHERE
operationOrdreFabrication.id not like 'DefaultRecord_%'
AND
operationOrdreFabrication.dateFinValidite is null
AND
ordreFabrication.dateFinValidite is null
AND
operationOrdreFabrication.sousTraitance in ('false')
AND
((current_date() > operationOrdreFabrication.dateDebutPrevue and
operationOrdreFabrication.etatAvancementOperationOF = '0'
) OR (current_date() > operationOrdreFabrication.dateFinPrevue and
operationOrdreFabrication.etatAvancementOperationOF != '2'))
ORDER BY
1 asc
But I want this to return true or false when my case returns anything else than "Aucun retard"
so I can use it as a subquery
Looks like you want EXISTS (NOT EXISTS)
select exists(
select 1
from (
--you original query
) t
where retard = 'Aucun retard') flag

Unable to query multiple tables via XML: Error occurred in XML processing

I would like to get a column_name and table_name for a list from all_tab_columns (which is not a problem till here) and also for each given column I want to go to the original table/column and see what is the top value with highest occurence.
With the query below I get the desired value for 1 example of column in 1 table:
select col1
from (SELECT col1, rank () over (order by count(*) desc) as rnk
from T1
Group by col1
)
where rnk = 1
now I want something like this:
select table_name,
column_name,
xmlquery('/ROWSET/ROW/C/text()'
passing xmltype(dbms_xmlgen.getxml( 'select ' || column_name || ' from (select ' || column_name ||', rank () over (order by count(*) desc) as rnk from '
|| table_name || ' Group by ' || column_name || ') where rnk = 1;'))
returning content) as C
from all_tab_columns
where owner = 'S1'
and table_name in ('T1', 'T2', 'T3', 'T4')
;
but it does not work. This is the error I get:
ORA-19202: Error occurred in XML processing
ORA-00933: SQL command not properly ended
ORA-06512: at "SYS.DBMS_XMLGEN", line 176
ORA-06512: at line 1
19202. 00000 - "Error occurred in XML processing%s"
*Cause: An error occurred when processing the XML function
*Action: Check the given error message and fix the appropriate problem
I make an example. These are my two tables, for instance; T1:
col.1 col.2 col.3
----- ---------- -----
y m1
y 22 m2
n 45 m2
y 10 m5
and T2:
col.1 col.2 col.3
----- ------- -----
1 germany xxx
2 england xxx
3 germany uzt
3 germany vvx
8 US XXX
so
from T1/Col.1 I should get 'y'
from T1/col.3 I should get 'm2'
from T2/col.3 I should get 'xxx'
and so on.
The important error in what has been reported to you is this one:
ORA-00933: SQL command not properly ended
Remove the semicolon from the query inside the dbms_xmlgen.getxml() call:
select table_name,
column_name,
xmlquery('/ROWSET/ROW/C/text()'
passing xmltype(dbms_xmlgen.getxml( 'select ' || column_name || ' from (select ' || column_name ||', rank () over (order by count(*) desc) as rnk from '
|| table_name || ' Group by ' || column_name || ') where rnk = 1'))
-------^ no semicolon here
returning content) as C
from all_tab_columns
...
Your XPath seems to be wrong too though; you're looking for /ROWSET/ROW/C, but C is the column alias for the entire expression, not the column being counted. You need to alias the column name within the query, and use that in the XPath:
select table_name,
column_name,
xmlquery('/ROWSET/ROW/COL/text()'
-- ^^^
passing xmltype(dbms_xmlgen.getxml( 'select ' || column_name || ' as col from (select ' || column_name ||', rank () over (order by count(*) desc) as rnk from '
-- ^^^^^^
|| table_name || ' Group by ' || column_name || ') where rnk = 1'))
returning content) as C
from all_tab_columns
...
With your sample data that gets:
TABLE_NAME COLUMN_NAME C
------------------------------ ------------------------------ ----------
T1 col.1 y
T1 col.2 224510
T1 col.3 m2
T2 col.1 3
T2 col.2 germany
T2 col.3 xxx
db<>fiddle
The XMLQuery is returning an XMLtype result, which your client is apparently showing as (XMLTYPE). You can probably change that behaviour - e.g. in Sql Developer from Tool->Preferences->Database->Advanced->DIsplay XMl Value in Grid. But you can also convert the reult to a string, using getStringVal() to return a varchar2 (or getClobVal() if you have CLOB values, which might cause you other issues):
select table_name,
column_name,
xmlquery('/ROWSET/ROW/COL/text()'
passing xmltype(dbms_xmlgen.getxml( 'select ' || column_name || ' as col from (select ' || column_name ||', rank () over (order by count(*) desc) as rnk from '
|| table_name || ' Group by ' || column_name || ') where rnk = 1'))
returning content).getStringVal() as C
-- ^^^^^^^^^^^^^^^
from all_tab_columns
...
As you can see, this doesn't do quite what you might expect when there are ties due to equal counts - in your example, there are found different values for T1."col.2" (null, 10, 22, 45) which each appear once; and the XMLQuery is sticking them all together in one result. You need to decide what you want to happen in that case; if you only want to see one then you need to specify how to decide to break ties, within the analytic order by clause.
I actually want to see all results but I expected to see them in different rows
An alternative approach that allows that is to use XMLTable instead of XMLQuery:
select table_name, column_name, value
from (
select atc.table_name, atc.column_name, x.value, x.value_count,
rank() over (partition by atc.table_name, atc.column_name
order by x.value_count desc) as rnk
from all_tab_columns atc
cross join xmltable(
'/ROWSET/ROW'
passing xmltype(dbms_xmlgen.getxml(
'select "' || column_name || '" as value, count(*) as value_count '
|| 'from ' || table_name || ' '
|| 'group by "' || column_name || '"'))
columns value varchar2(4000) path 'VALUE',
value_count number path 'VALUE_COUNT'
) x
where atc.owner = user
and atc.table_name in ('T1', 'T2', 'T3', 'T4')
)
where rnk = 1;
The inner query cross-joins all_tab_columns to an XMLTable which does a simpler dbms_xmlgen.get_xml() call to just get every value and its count, extracts the values and counts as relational data from the generated XML, and includes the ranking function as part of that subquery rather than within the XML generation. If you run the subquery on its own you'll see all possibel values and their counts, along with each values' ranking.
The outer query then just filters on the ranking, and shows you the relevant columns from the subquery for the first-ranked result.
db<>fiddle

SELECT with lower case + unaccent + multiple columns in PostgreSQL

Table schools
id | address | name
1 | Rybničná 59, Bratislava | Stredná odborná škola elektrotechnická
2 | Ul. Sibírska 1, Trnava | Stredná odborná škola elektrotechnická
What I want
From client If I want to type:
Stredná odborná
stredná odborná
stredna odborna
It must find rows with id 1 and 2
If I want to type Bratislava or bratis It must find row with id 1
What I have
SELECT * FROM schools WHERE unaccent(address) LIKE ('%' || 'bratis' || '%');
I need to select from 2 columns (address and name)
To make the search case insentive, use ILIKE instead of LIKE. Then, you would want to remove the accents from the input string as well. At last, just use AND or OR to combine the two criteria (note that you could use the same search term for both columns - use OR in this case)
SELECT * FROM schools
WHERE unaccent(address) ILIKE ('%' || unaccent('bratis') || '%')
AND unaccent(name) ILIKE ('%' || unaccent('Stredná odborná') || '%')
I hope this works
SELECT * FROM schools
WHERE unaccent(address|| ' ' ||name) ILIKE ('%' || 'bratis' || '%');

Oracle query to check for failure more than 90 %

I have a situation where I need to write a monitoring query to run every 2 hour to raise alert when processed count becomes less than 90%.
Lets say we have a Table Incoming Message where all incoming messages are captured and another table where all processed messages are captured.
This is what I came up with, this works but I am wondering if there is better way of doing this?
SELECT (CASE WHEN PROCESSEDCOUNT <= INCOMINGCOUNT * .9
THEN 'ALERT:: Process Count ' || PROCESSEDCOUNT || ' is less than 90% of Incoming count ' || INCOMINGCOUNT || '. '
ELSE 'FINE:: Process Count ' || PROCESSEDCOUNT || ' is more than or equal to 90% of Incoming count ' || INCOMINGCOUNT || '. '
END ) as Status
from
(SELECT
(SELECT COUNT(*)
FROM INCOMING_TABLE D WHERE INSERTION_TIME > SYSDATE - (1/12)
AND EXISTS (SELECT * FROM PROCESSED_TABLE C WHERE ( D.MESSAGE_ID = C.MESSAGE_ID)
AND C.PROCESSED_TIME > SYSDATE- (1/12))) AS PROCESSEDCOUNT,
(SELECT COUNT(*) FROM INCOMING_TABLE WHERE INSERTION_TIME > SYSDATE - (1/12)) AS INCOMINGCOUNT
FROM DUAL);
PROCESSED_TABLE used for storing other records as well, that is the
reason I need to use EXISTS to figure out process count.
I understand as time captured in two tables may not fall into same
time duration. We are not worried about that right now, just want to
make sure majority of records processed.
We are using oracle 11g, if that helps.
You are querying the same data from INCOMING_TABLE twice, which isn't really efficient ;-)
One possibility could be to outer join:
SELECT
CASE
WHEN COUNT(C.MESSAGE_ID) <= COUNT(*) * .9
THEN 'ALERT:: Process Count ' || COUNT(C.MESSAGE_ID) || ' is less than 90% of Incoming count ' || COUNT(*) || '. '
ELSE 'FINE:: Process Count ' || COUNT(C.MESSAGE_ID) || ' is more than or equal to 90% of Incoming count ' || COUNT(*) || '. '
END as Status
FROM INCOMING_TABLE D
LEFT OUTER JOIN PROCESSED_TABLE C
ON C.MESSAGE_ID = D.MESSAGE_ID
AND C.PROCESSED_TIME > SYSDATE- (1/12)
WHERE D.INSERTION_TIME > SYSDATE - (1/12)
/
That will work if you can be sure either zero or one record exists in PROCESSED_TABLE for each message_id. Maybe you can add a AND C.PROCESS_TYPE = ... or something to make that condition come true.
If you cannot guarantee that a join to PROCESSED_TABLE returns at most one row, you can move your EXISTS to inside a COUNT instead of the WHERE clause and thereby again avoid accessing INCOMING_TABLE twice:
SELECT (CASE WHEN PROCESSEDCOUNT <= INCOMINGCOUNT * .9
THEN 'ALERT:: Process Count ' || PROCESSEDCOUNT || ' is less than 90% of Incoming count ' || INCOMINGCOUNT || '. '
ELSE 'FINE:: Process Count ' || PROCESSEDCOUNT || ' is more than or equal to 90% of Incoming count ' || INCOMINGCOUNT || '. '
END ) as Status
from
(
SELECT COUNT(*) INCOMINGCOUNT
, COUNT(
CASE
WHEN EXISTS (SELECT * FROM PROCESSED_TABLE C
WHERE D.MESSAGE_ID = C.MESSAGE_ID
AND C.PROCESSED_TIME > SYSDATE- (1/12))
THEN 1
END
) PROCESSEDCOUNT
FROM INCOMING_TABLE D
WHERE D.INSERTION_TIME > SYSDATE - (1/12)
)
/
(PS. If you are at the start of writing a lot of code to handle a messaging queue, I would also suggest like #DARK_A to look into Advanced Queues instead of building your own. There is a lot of issues you need to handle in a messaging system, so why have that trouble if you can use what Oracle has already built ;-)

Query on a AS tablename in Oracle 10g

Hey all, how can i go about query off a made AS Tablename in a query of mine?
(its a long query so i have shortened it to the needed lines for this example)
SELECT
TO_CHAR(SYSDATE, 'YYYYMMDD') AS CURDATE,
TO_CHAR(A.DUE_DATE, 'YYYYMMDD') AS DUE,
UGRP.GROUP_CODE AS UGRPCODE,
A.DETAILS,
TRIM(STF.IDENTIFIER_1) || ', ' || TRIM(STF.IDENTIFIER_2) || ' ' || TRIM(STF.IDENTIFIER_3) AS STAFF,
FROM REC.CUSTOM_ATTRIBUTES E
INNER JOIN REC.SERVICE_REQUESTS SR
INNER JOIN REC.IDENTIFIERS STF ON A.ASSIGNED_STAFF_EID = STF.OWNER_EID ON E.OWNER_EID = SR.EID
WHERE (TYP.TYPE_CODE = 'SRSRTYPE')
AND (A.ASSIGNED_STAFF_EID <> 2000478)
AND STAFF = 'BARKER, BOB'
ORDER BY SR.SERVICE_REQUEST_NUM
Naturally the AND STAFF = 'BARKER, BOB" will not work for me.
My question is how can I query on that column?
Thanks!
David
Repeat the formula in your WHERE clause.
... AND TRIM(STF.IDENTIFIER_1) || ', ' || TRIM(STF.IDENTIFIER_2) || ' ' || TRIM(STF.IDENTIFIER_3) = 'BARKER, BOB' ...