Format Column Headers during Pipe Delimited Concatenation (Oracle SQL) - sql

Overview: I am tasked with providing a data extract from an Oracle database as a pipe-delimited output text file. I will be using SQLPlus to do this on the server where the data lives. Ordinarily, this task is not beyond my experience, but this time, the business desires column headers to be present.
Consider the following five columns that I need to output:
SELECT
a.USER_NAME || '|'
|| a.LAST_NAME || '|'
|| a.FIRST_NAME || '|'
|| b.PRODUCT_PURCHASED || '|'
|| c.DATEPURCHASED
FROM ...
WHERE ... ;
This SQL works fine, where the output looks like:
omnusruthius|ruthius|omnus|stackoverflow_prod|19-APR-16
However, the business wants it to look like:
USER_NM|LAST|FIRST|PROD|EFFECTIVE_DATE
omnusruthius|ruthius|omnus|stackoverflow_prod|19-APR-16
Problem: So the objective here is essentially to output the first row with custom-named column headers (aliases), as shown above. So my first approach was to try something like:
SELECT
a.USER_NAME AS USER_NM || '|'
|| a.LAST_NAME AS LAST || '|'
|| a.FIRST_NAME AS FIRST || '|'
|| b.PRODUCT_PURCHASED AS PROD || '|'
|| c.DATEPURCHASED AS EFFECTIVE_DATE
FROM ...
WHERE ...
Unfortunately, I receive:
ORA-00923: FROM keyword not found where expected
I'm not sure how that would help anyway, as the original SQL output without aliases does not show column headers in the first row anyway. Remember, this is through the command line (SQLPlus), not Toad or some other RDMS.
So then I tried:
SELECT
'USER_NM', 'LAST', 'FIRST', 'PROD', 'EFFECTIVE_DATE' FROM DUAL
UNION ALL
SELECT
a.USER_NAME || '|'
|| a.LAST_NAME || '|'
|| a.FIRST_NAME || '|'
|| b.PRODUCT_PURCHASED || '|'
|| c.DATEPURCHASED
FROM ...
WHERE ...
Which gives the following error:
ORA-01789: query block has incorrect number of result columns
I feel so close to the solution, what am I missing here? Any help will be appreciated!
Edit: Just a note to a future reader, both answers here will help you solve this problem, but upon further tweaking, I realize we've all been overthinking the solution. I'm not going to propose a new solution as the change is trivial, but consider doing the following instead:
SELECT 'USER_NM|LAST|FIRST|PROD|EFFECTIVE_DATE' FROM DUAL;
SELECT a.USER_NAME AS USER_NM || '|'
|| a.LAST_NAME AS LAST || '|'
|| a.FIRST_NAME AS FIRST || '|'
|| b.PRODUCT_PURCHASED AS PROD || '|'
|| c.DATEPURCHASED AS EFFECTIVE_DATE
FROM ...
WHERE ...
ORDER BY ... ;
The key here is the use of the semicolons in SQL*Plus. That first SELECT statement is completely independent from the second; no UNION is necessary as the output of the first query is automatically displayed immediately before the output of the second query. Both can have their own rules, which is especially convenient if your latter query is much more complicated. I can confirm the above query is working, and I'm surprised it took me that long to make that realization...

When concatenating make sure the header is a single string. Because you are concatenating the values in columns to be on one row.
If comma separation is used, as you have it in the question, the result block should also have 5 columns,which is not the case.
SELECT
'USER_NM|LAST|FIRST|PROD|EFFECTIVE_DATE' FROM DUAL
UNION ALL
SELECT
a.USER_NAME || '|'
|| a.LAST_NAME || '|'
|| a.FIRST_NAME || '|'
|| b.PRODUCT_PURCHASED || '|'
|| c.DATEPURCHASED
FROM ...
WHERE ...
Edit: The columns can also be sorted.
SELECT 'USER_NM|LAST|FIRST|PROD|EFFECTIVE_DATE' FROM DUAL
UNION ALL
SELECT * FROM (
SELECT
a.USER_NAME || '|'
|| a.LAST_NAME || '|'
|| a.FIRST_NAME || '|'
|| b.PRODUCT_PURCHASED || '|'
|| c.DATEPURCHASED
FROM ...
WHERE ...
ORDER BY DATEPURCHASED) --add any other columns needed

Since you tagged the question with SQL*Plus, you can use the PROMPT command to generate the header, which avoids complications ordering the results with a union:
PROMPT USER_NM|LAST|FIRST|PROD|EFFECTIVE_DATE
SELECT
a.USER_NAME AS USER_NM || '|'
|| a.LAST_NAME AS LAST || '|'
|| a.FIRST_NAME AS FIRST || '|'
|| b.PRODUCT_PURCHASED AS PROD || '|'
|| c.DATEPURCHASED AS EFFECTIVE_DATE
FROM ...
WHERE ...
ORDER BY ...
That takes the headings out of the SQL and into the client realm, where it arguably belongs. This also works in SQL Developer, and other clients may be able to do something similar. It won't work if you run the query on its own from another client, or over JDBC, or whatever; but then whatever is running the query can (and maybe should) provide the header in that case too.
If you aren't already, you could also consider doing SET HEADING OFF or SET PAGESIZE 0 to suppress the column headings from the query itself (though from what you said you're already doing that); and possibly SET EMBED OFF, though I don't think that's needed unless you do a separate query to generate the header line.

Related

What is the meaning of || '|' || in oracle?

I see a query with this "COUNT(DISTINCT(code || '|' description)":
SELECT ...,..., ...,NULL, COUNT(DISTINCT(code || '|' description)
FROM....
But Im not understanding what this "COUNT(DISTINCT(code || '|' description)" means? Do you know what it is? Thanks
In Oracle, the double pipe (||) stands for string concatenation. This:
select 'a' || 'b' from dual
Yields:
ab
When it comes to this expression: COUNT(DISTINCT(code || '|' description):
it is invalid sql code: parentheses are not balanced, and there is a missing concatenation operator; I suppose you meant COUNT(DISTINCT code || '|' || description)
the latter concatenates code and description with a | separator, and counts distinct resulting values
|| is the string concatenation operator
This code has a slight syntax error but looks like it was supposed to concat the code column with the description column values separated by a pipe
code, description
A123, code for blah blah
Becomes:
A123|code for blahblah
(If the sql is amended to code || '|' || description
Presumably you intend:
COUNT(DISTINCT code || '|' || description)
The purpose of this code is to count the unique combinations of code and description. COUNT(DISTINCT) in Oracle takes only one argument. So, if you want to count distinct combinations, you need to resort to tricks such as concatenating the values together.
This is putting the values together with a vertical bar in-between:
'NY' || '|' || 'New York' --> 'NY|New York'
The vertical bar is a separator that presumably does not occur (or only rarely occurs) in either code or description.

Conditionally concatenating fields in Oracle

What I need to do is to concatenate 4 fields in Oracle SQL Developer. The fields are:
Network, Network2, Network3, Network4
However, sometimes not all of the fields are filled in. This would always happen in sequence; it would never be just Network3 that's empty, it's either they fill in the first one only, the first 2 only, etc...
So, how can I write a Select statement that will ignore any fields that are NULL? I need the end result to look like:
Select Network, Network2, Network3, Network4 as Defect
and it should show Defect as something like "ON1, ON2, ON3, ON4" all in one field. But if only the first 2 are filled in, I don't want it to look like, "ON1, ON2, , , ".
Use NVL2(v, valueIfNotNull, valueIfNull)
SELECT
Network
|| nvl2(Network2, ', ' || Network2, '')
|| nvl2(Network3, ', ' || Network3, '')
|| nvl2(Network4, ', ' || Network4, '') AS Defect
FROM my_table
Use the CONCATENATE || operator and COALESCE() function:
SELECT Network
|| COALESCE(' - ' || Network2, '')
|| COALESCE(' - ' || Network3, '')
|| COALESCE(' - ' || Network4, '')
as Defect

Oracle Query, why double quotes around results, can I prevent them?

Oracle Query, why double quotes around results, can I prevent them?
I am running a query to create a fix length file that I will send to a mainframe to be processed. The results have double quotes that I need to remove before sending the file. I need to prevent the quotes via the query if possible. Here is the query:
SELECT '00000000' ||
CAST(' ' AS CHAR(161)) || '95005000000000000000' ||
CAST(' ' AS CHAR(63)) ||
CAST(CONACCT.CONTACT_ID AS CHAR(24)) ||
CAST(CONACCT.LOCATION_CODE AS CHAR(6)) ||
CAST(CONACCT.ACCT_NUM AS CHAR(18)) ||
CONACCT.NAME_RELATIONSHIP ||
CONACCT.LEAD_CONTACT_IND ||
CONACCT.RESPONSIBLE_PARTY ||
CONACCT.CAS_ADDRESS_IND ||
CAST(CONACCT.EXT_SYS_ID AS CHAR(4)) ||
CONACCT.PREF_CURRENCY
FROM CACS.CONTACT_ACCOUNT CONACCT,
CACS.ACCOUNT ACCT
WHERE CONACCT.ACCT_NUM = ACCT.ACCT_NUM
And the results look like (I removed most of the data for brevity):
"00000000 95005000000000000000 ... USD"
I need the double quotes not to be in the results. Thanks.

Oracle regexp_replace string cleanse in SQL query returning full dataset, not just mismatching rows

I'm using Oracle REGEXP_REPLACE to remove all non standard ascii (special) characters from a CLOB freetext field a client uses to store data, some users copy and paste from Excel which causes issues when reading the text from the CLOB. I've managed to get rid of all special characters and maintain the layout for paragraphs etc.
The issue I'm having is when I compare the cleansed field to the original, after converting to char string of course, is it is returning rows that have no difference between them, I've pulled out both values and there is no difference in most cases.
It's returning the whole dataset instead of just those that have been cleansed.I've run out of ideas
FYI a big part of maintaining the layout means I need to keep the carriage return CHR(13)
Below is the query
select *
from (
select incident, entity,
trim(to_char(textarea)) textarea,
trim(to_char(regexp_replace(textarea,'[^' || CHR(13) || CHR(32) || '-' || CHR(126) || ']',''))) regexp_text
from response
) tab
where tab.regexp_text <> tab.textarea
I've tried a number of different combinations with trimming whitespace and substring smaller strings etc. with no luck
Your pattern is: '[^' || CHR(13) || CHR(32) || '-' || CHR(126) || ']'.
The caret (^) is replacing everything that is not a special character with an empty string. With Oracle's default settings, I would expect this to return the empty string, which is treated as a NULL -- but that would mean nothing is returned.
In any case, try removing the caret:
trim(to_char(regexp_replace(textarea, '[CHR(13) || CHR(32) || '-' || CHR(126) || ']',' '))) as regexp_text
WITH data ( value ) AS (
SELECT 'éklzéiuhkn' FROM DUAL
)
SELECT REGEXP_REPLACE( value, '[^\r -~]', '' )
FROM data;
Outputs:
REGEXP_R
--------
klziuhkn
So your query should be something like:
SELECT *
FROM (
SELECT incident,
entity,
TRIM( textarea ) AS textarea,
TRIM( REGEXP_REPLACE( textarea, '[^\r -~]', '' ) ) AS regexp_text
FROM response
)
WHERE textarea <> regexp_text;
I finally figured it out, the issue was in formatting the comparisson strings in the where clause to include all Ascii characters and match against the regexp I planned to use, it was the only way to eliminate bogus and invisible encoded characters.
select incident, entity,
regexp_replace(textarea,'[^' || CHR(13) || ' -}‘-~À-ü]',''))) regexp_text
regexp_replace(textarea,'[^' || CHR(13) || CHR(32) || '-' || CHR(125) || CHR(145) || '-' || CHR(152) || CHR(192) || '-' || CHR(252) || ']','') regexp_text2
from response
where to_char(regexp_replace(textarea,'[^' || CHR(163) || CHR(1) || '-' || CHR(125) || CHR(145) || '-' || CHR(152) || CHR(192) || '-' || CHR(252) || ']','')) <> to_char(regexp_replace(textarea,'[^' || CHR(1) || '-' || CHR(255) || ']',''))
I included both lines to show the simple regexp of '[^' || CHR(13) || ' -}‘-~À-ü]' as well as the CHR(x) version.
I also needed to include latin characters in the end.
For some reason using only CHR() when specifing ascii characters works 100% of the time, I guess it might have something to do with the environments NLS_LANG setting.
This should work for all those looking to exclude weird encoded characters from their strings

Concatenate string in Oracle SQL? (wm-concat)

I've got some SQL that I'd like to format correctly for a mailout (generated directly from SQL - don't ask!). The code is as follows:
SELECT wm_concat('<br>• ' || FIELD1 || ' ' || FIELD2 || ' : ' || FIELD 3 || ' text') AS "Team"
Okay, so this kinda works - but it places a comma at the end of each line. Silly question, and possibly quite trivial, but is there anyway at all to remove the comma please? I think it's being added by the wm_concat function
Thanks
Yes the WM_CONCAT function puts a comma between each value it concatenates.
If there are no commas in your data you could do this:
SELECT replace (wm_concat('<br>• ' || FIELD1 || ' ' || FIELD2 || ' : '
|| FIELD 3 || ' text'),
',', null) AS "Team"
If you are on 11G you can use the new LISTAGG function instead:
SELECT LISTAGG ('<br>• ' || FIELD1 || ' ' || FIELD2 || ' : '
|| FIELD 3 || ' text')
WITHIN GROUP (ORDER BY <something>) AS "Team"
That will produce a result without commas.
Just trim the string for trailing commas:
RTRIM( wm_concat(...), ',' )
Oracle 10g provides a very convenient function wm_concat used to solve line reclassified demand, very easy to use this function, but the function provides only ',' this kind of delimiter.
In fact, as long as some simple conversion you can use other delimiters separated, the first thought is replace function
with t as( select 'a' x from dual union select 'b' from dual )
select replace(wm_concat(x),',','-') from t;
But taking into account the string itself may contain ',' character, use the above SQL will lead to erroneous results, but also made some changes to the above SQL.
with t as( select 'a' x from dual union select 'b' y from dual)
select substr(replace(wm_concat('%'||x),',%','-'),2) from t;
In the above SQL by a '%' as a separator, and then replace the '%' to remove the error. The program assumes that the string does not exist within the '%' string to replace the '%' in the SQL can also use other special characters.
Source: http://www.databaseskill.com/3400944/
You can create your own aggregate functions in Oracle and use those to aggregate strings.
Or use the StrAgg function written by Tom Kyte: http://www.sqlsnippets.com/en/topic-11591.html
SELECT StrAgg('<br>• ' || FIELD1 || ' ' || FIELD2 || ' : ' || FIELD 3 || ' text') AS "Team"
FROM Abc