Is there an equivalent to concat_ws in oracle? - sql

I have a ton of columns I am trying to aggregate together and most of them have NULL values. I want to separate values that do appear with a ';' but I cannot find an effective way to do this in oracle. CONCAT_WS would be exactly what I need as it would not add the delimeter between NULL values, but oracle does not support this.
concat_ws(';','dx89','dx90','dx91','dx92') as diagnoses3
ORA-00904: "CONCAT_WS": invalid identifier
Using a function like this is similar but doesn't quite get me what I need as you can see the ';' on the end of the string since dx91 and dx92 are NULL values:
dx89||';'||dx90||';'||dx91||';'||dx92 as diagnoses2
I63.8;I63.9;;
Any help would be greatly appreciated!

You can use NVL2() function specific to Oracle DB together with pipe concatenation operators :
SELECT TRIM(LEADING ';'
FROM dx89||NVL2(dx90,';'||dx90,dx90)||
NVL2(dx91,';'||dx91,dx91)||
NVL2(dx92,';'||dx92,dx92)) AS "Concatenated String"
FROM t
Demo

Related

Teradata: Error 2621 while converting the value - how to find bad characters?

I need to convert varchar value into NUMERIC and receive below error.
Error 2621: Bad characters in format or data of (variable_name)
I want to remove the rows with those bad characters and keep only convertable ones.
How to do that in Teradata? Does Teradata have some function to do that? (something like PRXMATCH in SAS)
Thanks
Simply apply TO_NUMBER which returns NULL for bad data points:
TO_NUMBER(mycol)
If you don't use regular expressions, you can use translate. In Oracle, I would write this as:
select col
from t
where translate(col, 'x0123456789.', 'x') is not null and
col not like '%.%.%';
I think Teradata has a more sensible policy on empty strings, so it would look like:
select col
from t
where otranslate(col, '0123456789.', '') <> '' and
col not like '%.%.%';
Of course, remove the . if you only want integers.

Show special characters in concat with db2 sql

I am running query like this for db2 sql
SELECT CONCAT(CONCAT('Order_no is =', Order_no), ' for line') FROM orders;
And result is coming like this:
Order_no is =123456 for line
But I want to fetch result as
Order_no is ='6640354' for line
I need to apply special characters to output, so can you please help me in this.
Use two single quotes together to escape a single quote:
SELECT CONCAT(CONCAT('Order_no is =''', Order_no), ''' for line')
FROM orders;
You can also use this;
select 'Order_no is=''' || trim(Order_no) || ''' for line' from orders;
Not sure why the use of nested CONCAT scalar is shown so pervasively in db2-tagged discussions, to concatenate more than one value.? Perhaps caused by how sometimes the documentation separates expressions and scalar functions, and in the latter docs might only offer a tiny Note: 'The CONCAT function is identical to the CONCAT operator. For more information, see "Expressions".'
I personally find the following use of the CONCAT operator, to be a much more readable way to compose the same character-string expression:
'Order_no is =''' CONCAT Order_no CONCAT ''' for line'
You can escape special character using \ or using another single quote like
select CONCAT( CONCAT('Order_no is =\'', Order_no), '\' for line') from orders;
Check DB2 documentation on Escaping special characters

How to use regex replace in Postgres function?

I have postgres function in which i am appending values in query such that i have,
DECLARE
clause text = '';
after appending i have some thing like,
clause = "and name='john' and age='24' and location ='New York';"
I append above in where clause of the query i already have. While executing query i am getting "and" just after "where" result in error
How to use regex_replace so that i remove the first "and" from clause before appending it to the query ?
Instead of fixing clause after the fact, you could avoid the problem by using
concat_ws (concatenate with separator):
clause = concat_ws(' and ', "name='john'", "age='24'", "location ='New York'")
will make clause equal to
"name='john' and age='24' and location ='New York'"
This can be even simpler. Use right() with a negative offset.
Truncates the first n characters and you don't need to specify the length of the string. Faster, simpler.
Double quotes (") are for identifiers in Postgres (and standard SQL) and incorrect in your example. Enclose string literals in single quotes (') and escape single quotes within - or use dollar quoting:
Insert text with single quotes in PostgreSQL
Since this is a plpgsql assignment, use the proper assignment operator :=. The SQL assignment operator = is tolerated, too, but can lead to ambiguity in corner cases.
Finally, you can assign a variable in plpgsql at declaration time. Assignments in plpgsql are still cheap but more expensive than in other programming languages.
DECLARE
clause text := right($$and name='john' and age='24' ... $$, -5)
All that said, it seems like you are trying to work with dynamic SQL and starting off on the wrong foot here. If those values can change, rather supply them as values with the USING clause of EXECUTE and be wary of SQL injection. Read some of the related questions and answers on the matter:
https://stackoverflow.com/search?q=[plpgsql]+[dynamic-sql]+EXECUTE+USING
You do not need regex:
clause = substr(clause, 5, 10000);
clause = substr(clause, 5, length(clause)- 4); -- version for formalists
concat_ws sounds like the best option, but as a general solution for things like this (or any sort of list with a delimiter) you can use logic like (pseudocode):
delim = '';
while (more appendages)
clause = delim + nextAppendage;
delim = ' AND ';
If you want to do it with regular expression try this:
result = regexp_replace(clause, '^and ', '')

Select query that displays Joined words separately, not using a function

I require a select query that adds a space to the data based on the placement of the capital letters i.e. 'HelpMe' using this query would be displayed as 'Help Me' . Note i cannot use a stored function to do this the it must be done in the query itself. The Data is of variable length and query must be in SQL. Any Help will be appreciated.
Thanks
You need to use user defined function for this until MS give us support for regular expressions. Solution would be something like:
SELECT col1, dbo.RegExReplace(col1, '([A-Z])',' \1') FROM Table
Aldo this would produce leading space that you can remove with TRIM.
Replace regular expresion function:
http://connect.microsoft.com/SQLServer/feedback/details/378520
About dbo.RegexReplace you can read at:
TSQL Replace all non a-z/A-Z characters with an empty string
Assume if you are using Oracle RDBMS, you use the following,
REGEX_REPLACE
SELECT REGEXP_REPLACE('ILikeToWatchCSIMiami',
'([A-Z.])', ' \1')
AS RX_REPLACE
FROM dual
;
Managed to get this output: * SQLFIDDLE
But as you see it doesn't treat well on words such as CSI though.

result of string concatenation is too long after using to_clob

I am trying to create a table view by running the sql below
SELECT IACM1.CMNT_REAS_TYP,
TO_CLOB(LPAD (
LISTAGG (IACM1.CMNT_TXT, ' ')
WITHIN GROUP (ORDER BY IACM1.LN_NUM),
4000,
LISTAGG (IACM1.CMNT_TXT, ' ')
WITHIN GROUP (ORDER BY IACM1.LN_NUM)
))
FROM FT_T_IACM IACM1, FT_T_IACM IACM2
WHERE IACM1.ISSACT_ID = IACM2.ISSACT_ID
AND IACM1.CMNT_REAS_TYP = IACM2.CMNT_REAS_TYP
GROUP BY IACM1.cmnt_reas_typ;
But I am getting the error below
ORA-01489: result of string concatenation is too long
01489. 00000 - "result of string concatenation is too long"
*Cause: String concatenation result is more than the maximum size.
*Action: Make sure that the result is less than the maximum size.
I looked up and I found suggestions to use to_clob but it is still throwing this error. I am using oracle 11g. Thanks for the help in advance.
The longest a concatenated string in a LISTAGG can be is 4000 characters. In this query, the sum of the lengths of CMNT_TXT for one or more CMNT_REAS_TYP values appears to be more than 4000. The LISTAGG builds the string before the LPAD truncs it to 4000 characters - so the LPAD has no effect in this case. Also, the TO_CLOB has no impact because the LISTAGG goes to a varchar2 before anything else happens.
One way to fix this would be to put additional fields in your Group By if possible. If that's not an option, you might try using COLLECT instead of LISTAGG - you'll have more issues with getting datatypes to match up but it's doable.
Here's a link with some comparisons between LISTAGG and COLLECT and a little bit on how to use COLLECT: http://www.oracle-developer.net/display.php?id=515
No need to create custom function. Oracle has provided xmlagg function already to do that.
All you need is to convert the output into clob by GetClobVal and also need to rtrim as it will return delimiter in the end of the results.
SELECT RTRIM(XMLAGG(XMLELEMENT(E,colname,',').EXTRACT('//text()') ORDER BY colname).GetClobVal(),',') from tablename;