Concatenating Strings whilst catering for nulls - sql

I have the following script :-
select
siteid
address1,
address2,
address3,
address4,
address5
from tblsites
...which may returning something like :-
siteid address1 address2 address3 address4 address5
123 1 New Street NULL New Town NULL Newvile
456 2 Elm Road NULL NULL New York New York
Is it possible in Oracle to reliably concatenate this data, catering for the nulls, and separating the strings with commas. So, the desired ouput for the data above is :-
siteid address
123 1 New Street, New Town, Newvile
456 2 Elm Road, New York, New York

You can use NVL2 to check whether the address components are non-null:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE tblsites ( siteid, address1, address2, address3, address4, address5 ) AS
SELECT 123, '1 New Street', CAST( NULL AS VARCHAR2(50) ), 'New Town', NULL, 'Newvile' FROM DUAL UNION ALL
SELECT 456, '2 Elm Road', NULL, NULL, 'New York', 'New York' FROM DUAL;
Query 1:
SELECT siteid,
RTRIM(
NVL2( address1, address1 || ', ', NULL )
|| NVL2( address2, address2 || ', ', NULL )
|| NVL2( address3, address3 || ', ', NULL )
|| NVL2( address4, address4 || ', ', NULL )
|| NVL2( address5, address5 || ', ', NULL ),
', '
) AS address
FROM tblsites
Results:
| SITEID | ADDRESS |
|--------|---------------------------------|
| 123 | 1 New Street, New Town, Newvile |
| 456 | 2 Elm Road, New York, New York |

SELECT siteid,
REGEXP_REPLACE((address1||','||address2||','||address3||','||address4||','||address5),'[,]+',',') AS address
FROM tblsites
OUTPUT
SITEID ADDRESS
123 1 New Street,New Town,New vile
456 2 Elm Road,New York,New York
DEMO
http://sqlfiddle.com/#!4/414cc/13

Related

how to get separate value from same column on Oracle SQL

apartment_Table
ID
update_Date
code
address1
address2
city
state_code
zip_code
1
2023/02/15
CO
Apt 320
null
NYC
NY
10012
1
2021/12/03
CO
Apt 105
null
NYC
NY
10012
1
2023/02/15
WK
5th Avenue
null
NYC
NY
10012
2
2023/02/15
CO
Apt 325
null
NYC
NY
10012
2
2022/01/12
CO
Apt 123
null
NYC
NY
10012
2
2023/02/14
WK
4th Avenue
null
NYC
NY
10012
2
2021/02/11
WK
5th Avenue
null
NYC
NY
10012
From the table, WK code is road_name and CO code is apartment number.
update_Date is the latest data when user update in the system. I would like to know How do I get result like below
(latest update_Date WK belong to latest update_Date CO)
ID
tenant_address
1
5th Avenue, Apt 320, NYC, NY, 10012
2
4th Avenue, Apt 325, NYC, NY, 10012
SQL I wrote:
SELECT
View_B_ID AS ViewID,
DECODE(P.address1, NULL, 'Street Not Available, ', SUBSTR(P.address1, 1, 50) || ', ')
|| DECODE(P.address2, NULL, '', SUBSTR(P.address2, 1, 50) || ', ')
|| DECODE(P.CITY, NULL, '', P.CITY || ', ')
|| DECODE(P.STAT_CODE, NULL, '', STAT_CODE || ', ')
|| DECODE(P.ZIP, NULL, '', P.ZIP) AS tenant_address
FROM View_B --assume I get data from View_B
LEFT JOIN (SELECT R.ID,
R.address1,
R.address2,
R.CITY,
R.STAT_CODE,
R.ZIP,
ROW_NUMBER() OVER(PARTITION BY R.ID ORDER BY R.update_Date DESC) AS RN
FROM apartment_Table R
WHERE R.code = 'CO'
) P
ON ViewID = P.ID
AND P.RN = 1
Output:
ID
tenant_address
1
Apt 320, NYC, NY, 10012
2
Apt 325, NYC, NY, 10012
Should I use LISTAGG function ? how to get the correct result ?
please help. thank you so much
You could use max keep dense_rank aggregate function to get the job done as below :
SELECT ID,
REGEXP_REPLACE(MAX(ADDRESS1) /* WK code road_name */
KEEP(DENSE_RANK FIRST ORDER BY
DECODE(CODE, 'WK', 1, 2) ASC,
UPDATE_DATE DESC) || ', ' ||
MAX(ADDRESS1) /* CO code apartment number */
KEEP(DENSE_RANK FIRST ORDER BY
DECODE(CODE, 'CO', 1, 2) ASC,
UPDATE_DATE DESC) || ', ' ||
MAX(ADDRESS2)
KEEP(DENSE_RANK FIRST ORDER BY
DECODE(CODE, 'WK', 1, 2) ASC,
UPDATE_DATE DESC) || ', ' ||
MAX(CITY)
KEEP(DENSE_RANK FIRST ORDER BY
DECODE(CODE, 'WK', 1, 2) ASC,
UPDATE_DATE DESC) || ', ' ||
MAX(STATE_CODE)
KEEP(DENSE_RANK FIRST ORDER BY
DECODE(CODE, 'WK', 1, 2) ASC,
UPDATE_DATE DESC) || ', ' ||
MAX(ZIP_CODE)
KEEP(DENSE_RANK FIRST ORDER BY
DECODE(CODE, 'WK', 1, 2) ASC,
UPDATE_DATE DESC)
, '(,[[:space:]])(,)'
, '\2'
) AS TENANT_ADDRESS
FROM YOUR_TABLE_NAME T
GROUP BY ID;
demo on db<>fiddle
See oracle documentation for more details
You could create a query (tmax) with max dates per ID for both codes 'WK' and 'CO' having them in the same row. Then join your table twice by codes and just concat your tenant's latest address:
SELECT tmax.ID,
twk.ADDRESS1 || ', ' || tco.ADDRESS1 || ', ' || tco.CITY || ', ' || tco.STATE_CODE || ', ' || tco.ZIP_CODE "TENANT_ADDRESS"
FROM ( Select ID,
MAX(CASE WHEN CODE = 'WK' THEN UPD_DATE END) "WK_DATE",
MAX(CASE WHEN CODE = 'CO' THEN UPD_DATE END) "CO_DATE"
From tbl
Group By ID
) tmax
INNER JOIN tbl twk ON(twk.ID = tmax.ID And twk.UPD_DATE = tmax.WK_DATE and twk.CODE = 'WK')
INNER JOIN tbl tco ON(tco.ID = tmax.ID And tco.UPD_DATE = tmax.CO_DATE and tco.CODE = 'CO')
With your sample data:
WITH
tbl (ID, UPD_DATE, CODE, ADDRESS1, ADDRESS2, CITY, STATE_CODE, ZIP_CODE) AS
(
Select 1, To_Date('2023/02/15', 'yyyy/mm/dd'), 'CO', 'Apt 320', Null, 'NYC', 'NY', 10012 From Dual Union All
Select 1, To_Date('2021/12/03', 'yyyy/mm/dd'), 'CO', 'Apt 105', Null, 'NYC', 'NY', 10012 From Dual Union All
Select 1, To_Date('2023/02/15', 'yyyy/mm/dd'), 'WK', '5th Avenue', Null, 'NYC', 'NY', 10012 From Dual Union All
Select 2, To_Date('2023/02/15', 'yyyy/mm/dd'), 'CO', 'Apt 325', Null, 'NYC', 'NY', 10012 From Dual Union All
Select 2, To_Date('2022/01/12', 'yyyy/mm/dd'), 'CO', 'Apt 123', Null, 'NYC', 'NY', 10012 From Dual Union All
Select 2, To_Date('2023/02/14', 'yyyy/mm/dd'), 'WK', '4th Avenue', Null, 'NYC', 'NY', 10012 From Dual Union All
Select 2, To_Date('2021/02/11', 'yyyy/mm/dd'), 'WK', '5th Avenue', Null, 'NYC', 'NY', 10012 From Dual
)
... you should get this as the result
ID TENANT_ADDRESS
---------- --------------------------------------
1 5th Avenue, Apt 320, NYC, NY, 10012
2 4th Avenue, Apt 325, NYC, NY, 10012

Multiple rows to multiple columns for co-applicant name and address for a unique ID

This is the query
SELECT b.ID, e.customername AS "Applicant name",
f.address AS "Applicant address",
x.customername AS "Co-Applicant name",
x.address AS "Co-Applicant address"
FROM table_1 b,
table_2 e,
table_3 f,
(SELECT b.customername, g.agreementid, a.address
FROM table_2 g, table_4 x, table_2 b, table_3 a
WHERE g.ID = x.ID
AND b.customerid = x.custid
AND b.customerid = a.custid
AND x.flag <> 'G') x
WHERE b.custid = e.customerid
AND f.custid = b.lesseeid
AND f.bptype = 'LS'
AND f.mailingaddress = 'Y'
AND b.ID = x.ID
AND b.ID='101'
The data is coming in below format.
+-----+-------+----------+--------------+----------+
| ID | name | address | co-applicant | address |
+-----+-------+----------+--------------+----------+
| 101 | aamir | address1 | rahul | London |
| 101 | aamir | address1 | vijay | Paris |
| 101 | aamir | address1 | sanjay | New York |
+-----+-------+----------+--------------+----------+
I need the data in below format
![ID name address name_1 address name_2 address
101 aamir address1 rahul London vijay Paris
102 Anil address2 Suyash Mumbai Rajesh Delhi Prakash Kolkata]1
You can use PIVOT as following:
SQL> WITH DATAA AS
2 (
3 SELECT 101 ID, 'aamir' name, 'address1' address, 'rahul' co_applicant, 'london' co_address FROM DUAL UNION ALL
4 SELECT 101, 'aamir', 'address1', 'vijay', 'Paris' FROM DUAL UNION ALL
5 SELECT 101, 'aamir', 'address1', 'sanjay', 'New York' FROM DUAL
6 )
7 -- YOUR QUERY STARTS FROM HERE
8 SELECT * FROM
9 (
10 SELECT
11 T.*,
12 ROW_NUMBER() OVER(ORDER BY NULL) AS RN
13 FROM DATAA T
14 ) PIVOT (
15 MAX ( CO_APPLICANT ) AS NAME, MAX ( CO_ADDRESS ) AS ADDRESS
16 FOR RN IN ( 1, 2,3 )
17 );
ID NAME ADDRESS 1_NAME 1_ADDRES 2_NAME 2_ADDRES 3_NAME 3_ADDRES
---------- ----- -------- ------ -------- ------ -------- ------ --------
101 aamir address1 rahul london vijay Paris sanjay New York
SQL>
Note: It will generate only 3 combinations of name and address as oracle do not allow dynamic columns in the query. If there is more than 3 co-applicant exist then it will take only data of 3 co-applicants.
-- UPDATE --
Use PARTITION BY clause in ROW_NUMBER if you multiple IDs as following:
SQL> WITH DATAA AS
2 (
3 SELECT 101 ID, 'aamir' name, 'address1' address, 'rahul' co_applicant, 'london' co_address FROM DUAL UNION ALL
4 SELECT 101, 'aamir', 'address1', 'vijay', 'Paris' FROM DUAL UNION ALL
5 SELECT 101, 'aamir', 'address1', 'sanjay', 'New York' FROM DUAL UNION ALL
6 SELECT 102 ID, 'Tejash' name, 'address2' address, 'chetan' co_applicant, 'london' co_address FROM DUAL UNION ALL
7 SELECT 102, 'Tejash', 'address2', 'nirav', 'Paris' FROM DUAL UNION ALL
8 SELECT 102, 'Tejash', 'address2', 'pulkit', 'New York' FROM DUAL
9 )
10 -- YOUR QUERY STARTS FROM HERE
11 SELECT * FROM
12 (
13 SELECT
14 T.*,
15 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY NULL) AS RN
16 FROM DATAA T
17 ) PIVOT (
18 MAX ( CO_APPLICANT ) AS NAME, MAX ( CO_ADDRESS ) AS ADDRESS
19 FOR RN IN ( 1, 2,3 )
20 );
ID NAME ADDRESS 1_NAME 1_ADDRES 2_NAME 2_ADDRES 3_NAME 3_ADDRES
---------- ------ -------- ------ -------- ------ -------- ------ --------
101 aamir address1 rahul london vijay Paris sanjay New York
102 Tejash address2 chetan london nirav Paris pulkit New York
SQL>
Cheers!!
I guess you can first do the conditional aggregation and then can join back with table -
WITH DATAA AS (SELECT 101 ID, 'aamir' name, 'address1' address, 'rahul' co_applicant, 'london' co_address FROM DUAL
UNION ALL
SELECT 101, 'aamir', 'address1', 'vijay', 'Paris' FROM DUAL
UNION ALL
SELECT 101, 'aamir', 'address1', 'sanjay', 'New York' FROM DUAL
),
TEMP AS (select D.*, ROW_NUMBER() OVER(PARTITION BY name ORDER BY co_applicant) RN from DATAA D)
SELECT ID, NAME, ADDRESS
,MAX(CASE WHEN RN = 1 THEN CO_APPLICANT ELSE NULL END) AS name_1
,MAX(CASE WHEN RN = 1 THEN CO_ADDRESS ELSE NULL END) AS ADDRESS_1
,MAX(CASE WHEN RN = 2 THEN CO_APPLICANT ELSE NULL END) AS name_2
,MAX(CASE WHEN RN = 2 THEN CO_ADDRESS ELSE NULL END) AS ADDRESS_2
,MAX(CASE WHEN RN = 3 THEN CO_APPLICANT ELSE NULL END) AS name_3
,MAX(CASE WHEN RN = 3 THEN CO_ADDRESS ELSE NULL END) AS ADDRESS_3
FROM TEMP
GROUP BY ID, NAME, ADDRESS;
With the clarification as SQL tables represent unordered sets until you specify a deterministic order clause. So I use an order by clause as co_applicant name. Thus from your sample data Sanjay is coming on 2nd col and Vihjay is coming in 3rd col.
Here is the fiddle.

SQL multiple AS columns from WHERE

I have a table
name | age | city
-------------
joe | 42 | berlin
ben | 42 | munich
anna | 22 | hamburg
pia | 50 | berlin
georg | 42 | munich
lisa | 42 | berlin
Now I would like to get all 42 year old in different columns by city
berlin | munich
-------------
joe | ben
lisa | georg
So I would need something like
SELECT (
SELECT name AS berlin WHERE city = "berlin"
UNION
SELECT name AS munich WHERE city = "munich")
FROM TABLE
WHERE
age = 42
Best from Berlin
Joerg
I think you want aggregation:
select max(case when city = 'berlin' then name end) as berlin,
max(case when city = 'munich' then name end) as munich
from (select t.*, row_number() over (partition by city order by name) as seqnum
from t
where city in ('berlin', 'munich') and age = 42
) t
group by seqnum
order by seqnum;
Using PIVOT for example I did it with SQL Server. Hope this can help you.
DECLARE #columns varchar(MAX);
DECLARE #sql nvarchar(max)
CREATE TABLE #Table
(
idPerson int,
firstName varchar(10),
age int,
city varchar(10)
);
INSERT INTO #Table
SELECT '1', 'joe', '42','berlin' UNION ALL
SELECT '2', 'ben', '42','munich' UNION ALL
SELECT '3', 'Ana', '22','hamburg' UNION ALL
SELECT '4', 'pia', '50','berlin' UNION ALL
SELECT '5', 'george', '42','munich' UNION ALL
SELECT '6', 'lisa', '42','munich'
--SELECT * from #Table
SET #columns = STUFF(
(
SELECT
',' + QUOTENAME(LTRIM(city))
FROM
(SELECT DISTINCT city
FROM #Table
) AS T
ORDER BY
city
FOR XML PATH('')
), 1, 1, '');
SET #sql = N'
SELECT
*
FROM
(
SELECT idPerson ,firstName ,age ,city
FROM #Table
--WHERE age = 42
) AS T
PIVOT
(
MAX(firstName)
FOR city IN (' + #columns + N')
) AS P order by age;';
EXEC sp_executesql #sql;
DROP TABLE #Table;
Thanks for your help! Based on the answer of Gordon Linhoff I got this solution:
SELECT max(case when city = 'berlin' then name end) as berlin,
max(case when city = 'munich' then name end) as munich
FROM TABLE
WHERE age = 42
;

Select same records that only have different values for a particular column

Below is an example of my customer table. There some records having multiple values in BIRTHDAY DATE (by mistake or so). I only want to select those records that have same values for LASTNAME, MIDDLENAME, FIRSTNAME, SSN but different BIRTHDAY:
Member table
LASTNAME MIDDLENAME FIRSTNAME SSN BIRTHDAY
Jones M Carol 1234 17-DEC-45
Jones M Carol 1234 17-DEC-45
Jones M Carol 4425 20-APR-70
Black S Ted 5555 15-MAY-57
Roberts T Cole 1412 14-MAY-57
Roberts T Cole 1412 20-OCT-57
Roberts S Cole 1412 15-MAY-57
I would like the result to be:
LASTNAME MIDDLEANME FIRSTNAME SSN BIRTHDAY
Roberts T Cole 1412 14-MAY-57
Roberts T Cole 1412 20-OCT-57
Notice that there were few accounts with same SSN or full name in the table, they are not selected since they don't have everything same. Also Jones M. Carol with 1234 as SSN is not selected either since she does not have different Birthday for two different account.
This is my SQL query that I have so far and this is not working well necessarily as it shows the results without duplication which I don't want to see.
SELECT x.FIRST_NM, x.MDL_NM, x.LAST_NM, x.SSN, x.BRTH_DT
FROM Member_table x
WHERE EXISTS
(
SELECT FIRST_NM, MDL_NM, LAST_NM, SSN, COUNT(*)
from Member_table
WHERE CURRENT_RECORD_IN = 'Y'
group by FIRST_NM, MDL_NM, LAST_NM, SSN
having count(distinct BRTH_DT) > 1
)
ORDER BY FIRST_NM ASC, LAST_NM ASC, MDL_NM ASC, SSN ASC;
Any advice for this query?
You can do it in a single table scan using the COUNT(...) OVER (...) analytic function:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE member_table ( LASTNAME, MIDDLENAME, FIRSTNAME, SSN, BIRTHDAY ) AS
SELECT 'Jones', 'M', 'Carol', 1234, DATE '1945-12-17' FROM DUAL UNION ALL
SELECT 'Jones', 'M', 'Carol', 1234, DATE '1945-12-17' FROM DUAL UNION ALL
SELECT 'Jones', 'M', 'Carol', 4425, DATE '1970-04-20' FROM DUAL UNION ALL
SELECT 'Black', 'S', 'Ted', 5555, DATE '1957-05-15' FROM DUAL UNION ALL
SELECT 'Roberts', 'T', 'Cole', 1412, DATE '1957-05-14' FROM DUAL UNION ALL
SELECT 'Roberts', 'T', 'Cole', 1412, DATE '1957-10-20' FROM DUAL UNION ALL
SELECT 'Roberts', 'S', 'Cole', 1412, DATE '1957-05-15' FROM DUAL;
Query 1:
SELECT *
FROM (
SELECT m.*,
COUNT( DISTINCT BIRTHDAY ) OVER (
PARTITION BY LASTNAME, MIDDLENAME, FIRSTNAME, SSN
) AS ct
FROM Member_table m
)
WHERE ct > 1
Results:
| LASTNAME | MIDDLENAME | FIRSTNAME | SSN | BIRTHDAY | CT |
|----------|------------|-----------|------|----------------------|----|
| Roberts | T | Cole | 1412 | 1957-05-14T00:00:00Z | 2 |
| Roberts | T | Cole | 1412 | 1957-10-20T00:00:00Z | 2 |
You need to add your conditions in the EXISTS clause.
SELECT x.FIRST_NM, x.MDL_NM, x.LAST_NM, x.SSN, x.BRTH_DT
FROM Member_table x
WHERE EXISTS
(
SELECT MT.FIRST_NM, MT.MDL_NM, MT.LAST_NM, MT.SSN, COUNT(*)
from Member_table MT
WHERE MT.CURRENT_RECORD_IN = 'Y'
AND x.FIRST_NM = MT.FIRST_NM
AND x.MDL_NM = MT.MDL_NM
AND x.LAST_NM = MT.LAST_NM
AND x.SSN = MT.SSN
group by MT.FIRST_NM, MT.MDL_NM, MT.LAST_NM, MT.SSN
having count(distinct MT.BRTH_DT) > 1
)
ORDER BY FIRST_NM ASC, LAST_NM ASC, MDL_NM ASC, SSN ASC;
I would use EXISTS :
SELECT x.*
FROM Member_table x
WHERE EXISTS (SELECT 1
FROM Member_table x1
WHERE x1.FIRST_NM = x.FIRST_NM AND x1.MDL_NM = x.MDL_NM AND
x1.LAST_NM = x.LAST_NM AND x1.SSN = x.SSN AND
x1.BIRTHDAY <> x.BIRTHDAY
);
For your current query you were not established any relation (i.e. referenced) between two queries which is actually required.

How can I separate results from query?

I have a table where I want to catch duplicate values.
After catching them I want to separate those results into separate rows.
The table:
CREATE TABLE [dbo].[ContactTable](
[contactid] [int] IDENTITY(1,1) NOT NULL,
[telephone1] [varchar](100) NULL,
[telephone2] [varchar](100) NULL,
[fullname] [varchar](100) NOT NULL
) ON [PRIMARY]
A few values:
insert into ContactTable(telephone1, telephone2,fullname) values('123','1234','danny')
insert into ContactTable(telephone1, telephone2,fullname) values('123','1234','danny1')
insert into ContactTable(telephone1, telephone2,fullname) values('123*','1234#','martin')
insert into ContactTable(telephone1, telephone2,fullname) values('1243*','15234#','martin')
insert into ContactTable(telephone1, telephone2,fullname) values('1243','15234','martin1')
The query for catching the duplicate values:
SELECT Phones, COUNT(Phones) AS CountPhones
FROM
(
SELECT Phones, contactid
FROM
(
SELECT telephone1 AS Phones, contactid
FROM ContactTable
WHERE (telephone1 IS NOT NULL)
UNION ALL
SELECT REPLACE(telephone1, '*', '') AS Phones, contactid
FROM ContactTable
WHERE (telephone1 IS NOT NULL) AND (telephone1 LIKE '%*')
UNION ALL
SELECT REPLACE(telephone1, '#', '') AS Phones, contactid
FROM ContactTable
WHERE (telephone1 IS NOT NULL) AND (telephone1 LIKE '%#')
UNION ALL
SELECT telephone2 AS Phones, contactid
FROM ContactTable
WHERE (telephone2 IS NOT NULL)
UNION ALL
SELECT REPLACE(telephone2, '*', '') AS Phones, contactid
FROM ContactTable
WHERE (telephone2 IS NOT NULL) AND (telephone2 LIKE '%*')
UNION ALL
SELECT REPLACE(telephone2, '#', '') AS Phones, contactid
FROM ContactTable
WHERE (telephone2 IS NOT NULL) AND (telephone2 LIKE '%#')
)as Tel
GROUP BY Phones, contactid
) as T
GROUP BY Phones
HAVING (COUNT(*) > 1)
ORDER BY CountPhones DESC
The results:
telephone Count
--------- -----
123 3
1234 3
1243 2
15234 2
But I want to get the results like this:
id telephone fullname
-- --------- --------
1 123 danny
2 123 danny1
3 123 martin
3 1234 martin
1 1234 danny
2 1234 danny1
5 1243 martin1
4 1243 martin
4 15234 martin
5 15234 martin1
How can I to do it?
SELECT * FROM (
SELECT
contactid AS id,
REPLACE(REPLACE(telephone1, '*', ''), '#', '') AS telephone,
fullname
FROM
ContactTable
UNION ALL
SELECT
contactid AS id,
REPLACE(REPLACE(telephone2, '*', ''), '#', '') AS telephone,
fullname
FROM ContactTable) AS all_phones
WHERE all_phones.telephone in (
SELECT phone FROM (
SELECT
COUNT(*) AS pnumber,
phone
FROM (
SELECT
REPLACE(REPLACE(telephone1, '*', ''), '#', '') AS phone
FROM
ContactTable
UNION ALL
SELECT
REPLACE(REPLACE(telephone2, '*', ''), '#', '') AS phone
FROM
ContactTable ) AS phones
GROUP BY phone)
AS phone_numbers WHERE pnumber > 1)