Concatenate with string but exclude when null - sql

Oracle v11
Exclude a string in a Concat when the value is NULL?
Work
Query:
Select
*
LastName || ',' || FirstName AS Last_First_Name
--I've tried using NVL & NVL2
--NVL(LastName || ',' || FirstName,'') AS Last_First_Name2
FROM TableA
TableA
LastName FirstName ID
Smith Ann 1
2
Output I'm getting
LastName FirstName ID LastName_FirstName
Smith Ann 1 Smith,Ann
2 ,
Expected Output:
LastName FirstName ID LastName_FirstName
Smith Ann 1 Smith,Ann
2

Use CASE
Select *,
CASE WHEN LastName IS NOT NULL AND FirstName IS NOT NULL
THEN LastName || ',' || FirstName AS Last_First_Name
END lastname_firstname
FROM TableA

Something like this... I added simulated inputs to test all four possibilities. However, note that if you may have last name but no first name, and also first name but no last name, the combined column will show just one name but you will not know if it is first or last. (Also, when a comma is added, I also add a space after the comma, as is usual; that can be controlled easily though.)
with
tablea ( lastname, firstname, id ) as (
select 'Smith', 'Ann' , 1 from dual
union all select null , null , 2 from dual
union all select 'Ang' , null , 3 from dual
union all select null , 'Murat', 4 from dual
)
-- End of simulated inputs (for testing only, not part of the solution).
-- SQL query begins BELOW THIS LINE. Use your actual table and column names.
select lastname, firstname, id,
lastname
|| case when lastname is not null and firstname is not null then ', ' end
|| firstname
as last_first
from tablea
;
LASTNAME FIRSTNAME ID LAST_FIRST
---------- ---------- ---------- ------------
Smith Ann 1 Smith, Ann
2
Ang 3 Ang
Murat 4 Murat

One way is to use a CASE statement to conditionally add the comma:
CASE WHEN LastName IS NULL
THEN LastName || ','
ELSE NULL
END
|| FirstName AS Last_First_Name

Oracle supports NULLIF. If both first and last name are null, the result will just be , which can be filtered.
SELECT
NULLIF(LastName ||','|| FirstName, ',') AS FullName
FROM table
Alternately, if you need to allow for when a single name is populated, you can use TRIM to remove leading/trailing commas.
SELECT
TRIM(',', LastName ||','|| FirstName) AS FullName
FROM table

By analogy, you can do with anything, including names.
Consider the example address:
select
SUBSTR(
(CASE WHEN city is null THEN '' ELSE ', ' || city END) ||
(CASE WHEN street is null THEN '' ELSE ', ' || street END) ||
(CASE WHEN house is null THEN '' ELSE ', ' || house END) ||
(CASE WHEN addition is null THEN '' ELSE ', ' || addition END)
, 3) as address
from departments;

You can concatenate any number of fields with delimiters using:
SELECT REGEXP_REPLACE(REGEXP_REPLACE(Col1 || ',' || Col2 || ',' || Col3, ',,*', ','), '^,|,$', '')
The inner REGEXP_REPLACE removes repeated delimiters occurring when you have interior NULL values.
The outer REGEXP_REPLACE removes the leading and trailing delimiters occurring if your list of values starts or ends with NULL.
If all values are NULL, returns NULL.

Related

how to check for duplicate rows of all the columns

I want to check for duplicate rows . and see there column values .
if there were only few columns in my table - 2 for example - I would have done something like:
'''
select col1, col2 ,count(*)
from mytable
group by col1,col2
having count(*) > 1.
'''
but I have dozens of column in my table .... and using the above syntax is tedious to specify all the columns in the table.
trying another approach with select distinct ... will not identify for me the content of duplicated rows .
I tried somthing like
'''
select * , count (*)
from my table
group by *
'''
but that doesn't work.
Write a query which will write a query for you.
For example, "john smith" is a duplicate here:
SQL> select * from my_data order by 1;
FULL_NAME FIRST_NAME LAST_NAME
---------- -------------------- --------------------
h gonzalez h gonzalez
john smith john smith
john smith john smith
rudy chan rudy chan
Query uses user_tab_columns and aggregates all column names, concatenating them to the rest of a select statement:
SQL> SELECT 'select '
2 || LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_id)
3 || ', count(*) cnt '
4 || CHR (10)
5 || ' from '
6 || table_name
7 || CHR (10)
8 || ' group by '
9 || LISTAGG (column_name, ', ') WITHIN GROUP (ORDER BY column_id)
10 || CHR (10)
11 || ' having count(*) > 1;' statement_to_run
12 FROM user_tab_columns
13 WHERE table_name = 'MY_DATA'
14 GROUP BY table_name;
STATEMENT_TO_RUN
--------------------------------------------------------------------------------
select FULL_NAME, FIRST_NAME, LAST_NAME, count(*) cnt
from MY_DATA
group by FULL_NAME, FIRST_NAME, LAST_NAME
having count(*) > 1;
Now, copy/paste the above statement_to_run and get the result:
SQL> select FULL_NAME, FIRST_NAME, LAST_NAME, count(*) cnt
2 from MY_DATA group by
3 FULL_NAME, FIRST_NAME, LAST_NAME having count(*) > 1;
FULL_NAME FIRST_NAME LAST_NAME CNT
---------- -------------------- -------------------- ----------
john smith john smith 2
SQL>
Just write out all the columns.
there are dozens of columns ,about 30 , and there names look like : 'AGtrf-456F_RValue'
Copy-paste.
In SQL*Plus you can use the DESCRIBE command to describe a table and you can copy the column names from the table description.
Or you can list all the columns using:
SELECT '"' || column_name || '",'
FROM user_tab_columns
WHERE table_name = 'MY_DATA'
ORDER BY column_id;
And then copy-paste the output into your query into the SELECT and GROUP BY clauses.
Can you generate the query automatically.
Yes, but it usually is not worth it as it takes longer to write a query to generate the query than it does just to list the columns and copy-paste.
If you have lots of column names that require you to use quoted identifiers (i.e. they are mixed-case or use non-standard characters like -) then you can use:
SELECT EMPTY_CLOB()
|| 'SELECT '
|| LISTAGG('"' || column_name || '"', ',') WITHIN GROUP (ORDER BY column_id)
|| ', COUNT(1) FROM MY_DATA GROUP BY '
|| LISTAGG('"' || column_name || '"', ',') WITHIN GROUP (ORDER BY column_id)
|| ' HAVING COUNT(1) > 1;'
FROM user_tab_columns
WHERE table_name = 'MY_DATA'
ORDER BY column_id;
Which works unless you have too many columns and LISTAGG exceeds 4000 characters then you would need to use something like:
WITH columns (col, pos) AS (
SELECT '"' || column_name || '",',
column_id
FROM user_tab_columns
WHERE table_name = 'MY_DATA'
ORDER BY column_id
)
SELECT sql
FROM (
SELECT 'SELECT ' AS sql, 0 FROM DUAL
UNION ALL
SELECT col, pos FROM columns
UNION ALL
SELECT ' COUNT(1) FROM MY_DATA GROUP BY ', 10000 FROM DUAL
UNION ALL
SELECT col, 10000 + pos FROM columns
UNION ALL
SELECT '1 HAVING COUNT(1) > 1', 20000 FROM DUAL
ORDER BY 2
)
fiddle

CONCAT in Oracle SQL Developer

I don't know if I should use comma (,) or "||" as separator in CONCAT function for Oracle SQL.
This one works:
SELECT CONCAT(first_name, last_name) as name
FROM TABLE1
However, none of these below works out (I need to put a space between first_name and last_name, or to extract the initial letter from last_name and wrap this letter with parenthesis):
SELECT CONCAT(first_name || last_name) as name
FROM TABLE1
SELECT CONCAT(first_name || ' ' || last_name) as name
FROM TABLE1
SELECT CONCAT(first_name, ' ', last_name) as name
FROM TABLE1
SELECT CONCAT(first_name, '(', UPPER(STR(last_name, 1,1)), ')') as name
FROM TABLE1
Using double-pipe (||) instead of CONCAT in Oracle SQL is a more efficient way to go. Thanks to the comments that folks provided in here.
SELECT (first_name || ' ' || last_name)as name
FROM Table1;
Output:
John Smith
I also fixed the last script in the above question. Somehow it needs two right parentheses before "as name".
SELECT first_name || '(' || UPPER(SUBSTR(last_name, 1,1)) || ')')) as name, first_name, last_name
FROM TABLE1
Output:
John(S) John Smith

How do I remove CHR(0) from strings when using LISTAGG in Oracle DB?

I am trying to query an oracle database and get a list of names in a single cell of the result.
I have a query like the following which produces the names in separate rows:
SELECT (lastname || ', ' || firstname) as fullname
FROM users
WHERE {some condition}
-- result --
fullname
Anderson, Alex
Baker, Bob
Clark, Carl
However, when I try and use LISTAGG to concatenate those rows I get the following error:
SELECT LISTAGG(fullname, '; ') WITHIN GROUP (ORDER BY fullname) as instructors
FROM
(
SELECT (lastname || ', ' || firstname) as fullname
FROM users
WHERE {some condition}
)
-- desired result --
instructors
Anderson, Alex; Baker, Bob; Clark, Carl
-- actual result --
Error Type: System.Xml.XmlException
Error Message: hexadecimal value 0x00, is an invalid character. Line 1, position 32.
Note that if I use LISTAGG on a string made from concatenating INT values, the query works as expected:
SELECT LISTAGG(fullname, '; ') WITHIN GROUP (ORDER BY fullname) as instructors
FROM
(
SELECT (pk1 || ', ' || pk1) as fullname -- pk1 is the users primary key
FROM users
WHERE {some condition}
)
-- result --
instructors
01, 01; 02, 02; 03, 03
I suspect the problem is that the firstname and lastname fields are null-terminated and LISTAGG is not properly removing the null characters from the result. I'm not sure how I can remove the null characters from the result of the inner query though.
Note that this is a client's database and I am not allowed to modify its contents. I need to be able to read the fields in their current format and produce the desired output.
I had the same problem. The solution for me was:
SELECT replace(LISTAGG(fullname, '; ') WITHIN GROUP (ORDER BY fullname), chr(0)) as instructors

Concat firstname and lastname with space in between in oracle pl sql

I have one requirement to concat user first_ name, and last_name with space in between in Oracle.
Ex: first_name is 'Hopkins' and last_name is 'Joe'.
Full name should be printed as Hopkins Joe.
I'm using Oracle 11g and it is working in SQL query, but not working in stored procedure.
Try this:
SELECT CONCAT(CONCAT(first_name, ' '),last_name)
OR
SELECT first_name || ' ' || last_namefrom;
use this:
TRIM(FIRST_NAME || ' ' || LAST_NAME)
if any of first_name or last_name is blank or null the extra space we are adding will be trimmed.
Try this
select first_name || ' ' || last_name as full_name from table
Example:
SELECT 'Dave' || ' ' || 'Anderson' as full_name
FROM table;
Result: 'Dave Anderson'
No need to use CONCAT function twice. Concat with space will work in this way
SELECT CONCAT(first_name,(' '||last_name)) AS full_name
This will work:
select first_name||' '||last_name
from table_name
where first_name is not null -- "if the first_name can be null"
and last_name is not null -- "if the last_name can be null"
;

Selecting form with SUBSTR

I have a table with name and profession I need to select the name of all employees with the first letter of their profession surrounded by () to be like:
Sara (D)
Jack (E)
Use || to concatenate strings and SUBSTR() to get the first character:
SELECT name || ' (' || SUBSTR( profession, 1,, 1 ) || ')'
FROM your_table;