How to select only distinct fields - sql

Let's say I have a table of customer addresses:
CName | AddressLine
-------------------------------
John Smith | 123 Nowheresville
Jane Doe | 456 Evergreen Terrace
John Smith | 999 Somewhereelse
Joe Bloggs | 1 Second Ave
I want to pick CName with distinct AddressLine means i dont want to pick "John Smith " as it has two addresses. How do i do that?

You can get the distinct rows by doing:
select cname, min(AddressLine) as AddressLine
from t
group by cname
having count(*) = 1;

Alternatively, you could use WHERE NOT EXISTS:
Select CName, AddressLine
From YourTable A
Where Not Exists
(
Select *
From YourTable B
Where A.CName = B.CName
And A.AddressLine <> B.AddressLine
)
Edit to address performance:
Create Table Test (CName Varchar (20), AddressLine Varchar (50));
Insert Test Values
('John Smith', '123 Nowheresville'),
('Jane Doe', '456 Evergreen Terrace'),
('John Smith', '999 Somewhereelse'),
('Joe Bloggs', '1 Second Ave')
select cname, min(AddressLine) as AddressLine
from test
group by cname
having count(*) = 1;
Select CName, AddressLine
From Test A
Where Not Exists
(
Select *
From Test B
Where A.CName = B.CName
And A.AddressLine <> B.AddressLine
);
Execution plans:

Use:
SELECT CName
FROM Addresses
GROUP BY CName
HAVING COUNT(DISTINCT AddressLine) = 1

Try this:
SELECT a.CName
FROM Addresses a
GROUP BY a.CName
HAVING COUNT(a.AddressLine) = 1

Related

How to partially transpose a table

I have to create a table with a lists of contacts (ClientCode, Telephone, Name) from a table structured like this:
ClientCode
Telephone1
Name1
Telephone2
Name2
Telephone3
Name3
1234
55555
John M.
79879
Frank
897987
Paul
9884
84416
Richard
88416
Helen
11594
Katrin
I need to group by ClientCode same persons that work at the same client.
Table expected:
ClientCode
Telephone
Name
1234
55555
John M.
1234
79879
Frank
1234
897987
Paul
9884
84416
Richard
9884
88416
Helen
9884
1159
Katrin
I've tried the following solution (from this answer) but the output is not correct
SELECT UNPVTBL.CLIENTCODE, UNPVTBL.NAME
FROM (SELECT * FROM -ORIGIN_TABLE-) P
UNPIVOT
(NAME FOR CONTACTS IN
(NAME1, NAME2, NAME3)
)UNPVTBL
UNION
SELECT UNPVTBL.CLIENTCODE, UNPVTBL.TELEPHONE
FROM (SELECT * FROM -ORIGIN_TABLE-) G
UNPIVOT
(TELEPHONE FOR TELEPH IN
(TELEPHONE1, TELEPHONE2, TELEPHONE3)
)UNPVTBL
You can try doing it using the UNION ALL operator:
SELECT ClientCode, Telephone1 AS Telephone, Name1 AS Name FROM tab
UNION ALL
SELECT ClientCode, Telephone2 AS Telephone, Name2 AS Name FROM tab
UNION ALL
SELECT ClientCode, Telephone3 AS Telephone, Name3 AS Name FROM tab
Output:
ClientCode
Telephone
Name
1234
55555
John M.
9884
84416
Richard
1234
79879
Frank
9884
88416
Helen
1234
897987
Paul
9884
11594
Katrin
Check the demo here.
A way to do this is VALUES unpivot:
select t.ClientCode,x.name, x.Telephone
from (
VALUES (1234, 55555, N'John M.', 79879, N'Frank', 897987, N'Paul')
, (9884, 84416, N'Richard', 88416, N'Helen', 11594, N'Katrin')
) t (ClientCode,Telephone1,Name1,Telephone2,Name2,Telephone3,Name3)
cross apply (
VALUES (Telephone1, Name1)
, (Telephone2, Name2)
, (Telephone3, Name3)
) x (Telephone, Name)
Hopefully this exercise is to fix you design; if it isn't, it should be.
As for the solution, a VALUES table construct seems to do this simply enough:
SELECT YT.ClientCode,
V.Telephone,
V.[Name]
FROM dbo.YourTable YT
CROSS APPLY(VALUES(Telephone1,Name1),
(Telephone2,Name2),
(Telephone3,Name3))V(Telephone,Name);

Changing record values based on whether there are duplicates when two tables are combined

I know I can join Table #1 and Table #2 with a UNION and then filter out duplicate Id's using DISTINCT. However, for the duplicate contacts I'd like to change DrinkPreference to Coke/Pepsi.
Is this possible?
Starting Table #1
Id
FirstName
LastName
DrinkPreference
123
Tom
Bannon
Pepsi
124
Sarah
Smith
Pepsi
Starting Table #2
id
FirstName
LastName
DrinkPreference
125
Jim
Henry
Coke
123
Tom
Bannon
Coke
Table? #3 - combined with DrinkPreference set to Coke/Pepsi where contact exists in both tables?
Id
FirstName
LastName
DrinkPreference
125
Jim
Henry
Coke
123
Tom
Bannon
Coke/Pepsi
124
Sarah
Smith
Pepsi
You can try this one
SELECT coalesce(t1.firstname, t2.firstname) AS firstname,coalesce(t1.lastname,t2.lastname) AS lastname, CASE WHEN t1.drinkpreferences IS NULL THEN t2.drinkpreferences WHEN t2.drinkpreferences IS NULL THEN t1.drinkpreferences
ELSE t1.drinkpreferences || '/' || t2.drinkpreferences END AS drinkpreferences FROM table1 t1 FULL JOIN table2 t2 ON t1.id = t2.id
Achievable using multiple unions and joins.
select distinct FirstName, LastName, case when ct = 2 then 'Coke/Pepsi' else DrinkPreference end
from (
select FirstName, LastName, DrinkPreference, Id from table1
union all
select FirstName, LastName, DrinkPreference, Id from table2) a
left join
(
select count(1)ct, Id from
(select Id from table1
union all
select Id from table2) t1
group by Id
) b on b.Id = a.Id

Remove duplicate rows in Postgres

I have two tables:
Employee:
ID
Name
Surname
143
Amy
Flowers
245
Natasha
Smith
365
John
Alexander
445
Natasha
Smith
565
Monica
Withhouse
644
Amy
Flowers
1023
Amy
Alexander
And employee_details:
ID
Employee_id
Document_numer
1
644
XXXXXXXXX
2
245
XXXXXX
3
365
XXXXXX
I need to remove duplicate records that are in the Employee table and that are not related to the employee_details table. In the example data, I would like to delete the employee doublet with the id 143 and 445.
And I must admit that I have no idea how to do it.Could you give me a hint?
The base is postgres
Delete from Employee
Where id not in (
Select Employee_id
from employee_details
)
and name in (
Select name
from Employee
Group by name having count(name) > 1
)
Though the question is already answered I am adding two different answers here using cte.
create table Employee(ID int, Name varchar(50), Surname varchar(50));
insert into Employee values(143, 'Amy', 'Flowers');
insert into Employee values(245, 'Natasha', 'Smith');
insert into Employee values(365, 'John', 'Alexander');
insert into Employee values(445, 'Natasha', 'Smith');
insert into Employee values(565, 'Monica', 'Withhouse');
insert into Employee values(644, 'Amy', 'Flowers');
insert into Employee values(1023, 'Amy', 'Alexander');
create table employee_details ( ID int, Employee_id int, Document_numer varchar(50));
insert into employee_details values(1, 644, 'XXXXXXXXX');
insert into employee_details values(2, 245, 'XXXXXX');
insert into employee_details values(3, 365, 'XXXXXX');
Delete query 1:
with duplicate_employees as
(
select * , count(id)over(partition by name,surname) duplicate_count from Employee
)
delete from Employee where id in(
select id from duplicate_employees de
where duplicate_count >1
and not exists
(
select 1 from employee_details e where e.Employee_id = de.ID
)
)
select * from employee
Output:
id
name
surname
245
Natasha
Smith
365
John
Alexander
565
Monica
Withhouse
644
Amy
Flowers
1023
Amy
Alexander
db<>fiddle here
Delete query 2:
with cte as
(
Select *, count(*)over(partition by name,surname) duplicate_count,
(case when exists
(
select 1 from employee_details ed where ed.Employee_id = e.ID
)
then 1 else 0 end) exist_in_details
from Employee e
)
delete from Employee where id in (select id from cte where duplicate_count>1 and exist_in_details=0 )
select * from Employee
Output:
id
name
surname
245
Natasha
Smith
365
John
Alexander
565
Monica
Withhouse
644
Amy
Flowers
1023
Amy
Alexander
db<>fiddle here

Filtering SQL rows based on values from other rows

Using a SQL Server 2016 SP1 database I have this data in table name AgentsTable:
SELECT * FROM AgentsTable;
user_id first_name last_name agent_id agent_status agent_code
2003015038088 John Brown 22307 Retired 12345
2003015038088 John Brown 22307 Death 12399
4432442556456 Mary Jane 24667 Active 32133
7746234737464 Harry Smith 29981 Retired 42354
3455555657677 Mark Aguy 29654 Active 34655
5436546674465 Sally Sam 22584 Retired 45464
The second row shows that 'John Brown' is dead (agent_status = 'Death'), so I would like to exclude all rows for that agent_id who is dead. (Note that John Brown has two different agent_codes, so there's one row for each agent_code. That's how the source data is...)
This query:
SELECT * FROM AgentsTable WHERE agent_status = 'Retired';
Will return this:
user_id first_name last_name agent_id agent_status agent_code
2003015038088 John Brown 22307 Retired 12345
7746234737464 Harry Smith 29981 Retired 42354
5436546674465 Sally Sam 22584 Retired 45464
The result I want, where John Brown (22307) is excluded, is:
user_id first_name last_name agent_id agent_status agent_code
7746234737464 Harry Smith 29981 Retired 42354
5436546674465 Sally Sam 22584 Retired 45464
How can I achieve that? In other words, how can I exclude one row based on a value in another related row?
You can use CTE and get agents who are not deceased as given below:
;WITH CTE_deceasedagents AS
(
SELECT agent_id
FROM AgentsTable
WHERE agent_status = 'Death'
)
SELECT *
FROM AgentsTable as a
WHERE NOT EXISTS
(
SELECT agent_id
FROM CTE_deceasedagents as c
WHERE c.agent_id = a.agent_id
);
GO
SELECT * FROM AgentsTable t1 WHERE agent_status = 'Retired' where NOT EXISTS (SELECT 1 FROM AgentsTable t2 WHERE t2.agent_status = 'Death' and t2.agent_code = t1. agent_code );
I would just use not exists with a correlated subquery to ensure that no other record exists with the same user_id and a 'Death' status:
select a.*
from agentsTable a
where not exists (
select 1
from agentsTable a1
where a1.user_id = a.user_id and a1.agent_status = 'Death'
)

SQL problem - one name 2 address in the same table

CName | AddressLine
-------------------------------
John Smith | 123 Nowheresville
Jane Doe | 456 Evergreen Terrace
John Smith | 999 Somewhereelse
Joe Bloggs | 1 Second Ave
If i have this table is possible to do a select to put like this
CNAME | Address1 | Address2
John Smith | 123 Nowheresville | 999 Somewhereelse
I'm using oracle
It is considered a bad design (inefficient memory usage) to add a new column for appearance of duplications in just some rows . Maybe you should consider using inner-join and a separate table for the address column!
As your table stands, you cannot use a simple self-join to reduce this to a single line. You can bring back rows that have all of the addresses (so long as you hard-code for a particular maximum number of addresses), but you will always have the same number of rows as there are addresses for a given user (unless you have a way of identifying a single address as "primary").
In order to reduce your result set to a single line, you'll have to provide some way of marking a "first" address. With SQL Server (or similar professional-grade RDBM's), you could use a common table expression with ranking/row numbering functions to do this:
with Addresses as
(select
CName,
AddressLine,
row_number() over (partition by CName order by AddressLine) as RowNum
from YourTable)
select
a1.CName,
a1.AddressLine as Address1,
a2.AddressLine as Address2,
a3.AddressLine as Address3
from Addresses a1
left join Addresses a2 on a2.CName = a1.CName and a2.RowNum = 2
left join Addresses a3 on a3.CName = a1.CName and a3.RowNum = 3
where a1.RowNum = 1
temp = your table name
select distinct cname, addressline as [address1],
(
ISNULL((select addressline from temp where cname = t.cname and addressline != t.addressline), '')
) as address2
from
temp t
The problem is resolve, Frank Kulash in oracle forum solved the problem
Here is the solution:
WITH got_r_num AS
(
SELECT cname, addressline
, ROW_NUMBER () OVER ( PARTITION BY cname
ORDER BY addressline
) AS r_num
FROM table_x
-- WHERE ... -- If you need any filtering, put it here
)
SELECT cname
, MIN (CASE WHEN r_num = 1 THEN addressline END) AS addressline1
, MIN (CASE WHEN r_num = 2 THEN addressline END) AS addressline2
FROM got_r_num
GROUP BY cname
Tanks to all for the help