How to partially transpose a table - sql

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);

Related

Dynamic Pivoting in SQL - Snowflake

I have an input file
Customer PhoneNum Location Brand
John 1234 ABC Oppo
John 1234 DEF MI
John 1234 KLM RealMe
John 1234 LKM 1+
Joe 9934 ABC Apple
Joe 9934 DEF Samsung
The same phone number can be listed to multiple phone brands and the number of brands per phone number can be dynamic i.e. some can have 2 brands some can have 4 some 8 etc. I can pass the list of unique brands in the pivot query but that would create columns which might not have values.
the result i want is
Customer PhoneNum Brand1 Brand1Location Brand2 Brand2Location Brand3 Brand3Location Brand4 Brand4Location
John 1234 Oppo ABC MI DEF RealMe KLM 1+ LKM
Joe 9934 Apple ABC Samsung DEF
```
Here i dont need the list of brands but if i know the maximum record per number is say 4 i can have the output in above format, which I believe is a good way to read the result.
Is there any way in SQL to get the above result.
select * from phone_multiple_make
pivot(max(location),max(brand) for brand in
('MI','Oppo','RealMe') )as p;
If you're ok with not using a pivot function you can acheive your results like this:
WITH CTE AS (
SELECT 'John' CUSTOMER,1234 PHONENUM,'ABC' LOCATION,'Oppo' BRAND
UNION
SELECT 'John' CUSTOMER,1234 PHONENUM,'DEF' LOCATION,'MI' BRAND UNION
SELECT 'John' CUSTOMER,1234 PHONENUM,'KLM' LOCATION,'RealMe' BRAND UNION
SELECT 'John' CUSTOMER,1234 PHONENUM,'LKM' LOCATION,'1+' BRAND UNION
SELECT 'Joe' CUSTOMER,9934 PHONENUM,'ABC' LOCATION,'Apple' BRAND UNION
SELECT 'Joe' CUSTOMER,9934 PHONENUM,'DEF' LOCATION,'Samsung' BRAND )
SELECT CUSTOMER, PHONENUM
,J:BRAND1:BRAND::STRING BRAND1, J:BRAND1:LOCATION::STRING LOCATION1
,J:BRAND2:BRAND::STRING BRAND2, J:BRAND2:LOCATION::STRING LOCATION2
,J:BRAND3:BRAND::STRING BRAND3, J:BRAND3:LOCATION::STRING LOCATION3
,J:BRAND4:BRAND::STRING BRAND4, J:BRAND4:LOCATION::STRING LOCATION4
FROM (
SELECT CUSTOMER, PHONENUM, OBJECT_AGG(KEY,OBJ) J FROM (
SELECT CUSTOMER, PHONENUM
,'BRAND'||ROW_NUMBER()OVER(PARTITION BY CUSTOMER,PHONENUM ORDER BY BRAND)::STRING KEY
,OBJECT_CONSTRUCT( 'LOCATION', LOCATION, 'BRAND',BRAND) OBJ FROM CTE) GROUP BY 1,2)

Assign value to a new column for all rows associate with unique value in another column

I need to assign name to a new 'responsible' column for all rows associate with customer.
If part of the string in 'codes' consist 'manager', manager's name should be assigned to the 'responsible' column. If there is no 'manager' in the codes column, 'responsible' columns should be populated with the 'empl_name' associate with the row.
I assume case and group by should be used?
table looks like:
cust_name empl_name codes
john mike empl, office
liza nick manager_1, remote
john kate empl, remote
john mike empl, remote
liza mike empl, office
david kate empl, remote
john mike empl, remote
liza mike empl, office
david mike empl, remote
chris jennifer manager_2, office
output should be:
cust_name empl_name codes responsible
john mike empl, office mike
liza nick manager_1, remote nick
john kate empl, remote kate
john mike empl, remote mike
liza mike empl, office nick
david kate empl, remote kate
john mike empl, remote mike
liza mike empl, office nick
david mike empl, remote mike
chris jennifer manager_2, office jennifer
My code (googled everything):
SELECT
c.cust_name,
e.emp_name,
a.codes,
FROM Billing as b
--- Code Labels in 1 single row, separated by comma
OUTER APPLY (
SELECT STUFF((
(SELECT ', ' + y.CodeLabelName
FROM CodeToLabelBridge x
JOIN CodeLabel y
ON y.CodeLabelId = x.CodeLabelId
WHERE x.CodeId = b.billing_code_id
FOR XML PATH(''), TYPE).value('.', 'varchar(max)')),1,1,''
) AS codes
) AS a
--- JOINS
JOIN Client as c
ON (b.billing_cust_id = c.cust_id)
JOIN Employer as e
ON (b.billing_emp_id = e.emp_id)
JOIN Code as sc
ON (b.billing_code_id = sc.codes_id)
--- Table with Client and associate Manager
WITH cte AS (
SELECT * ,
row_number() OVER(PARTITION BY t.cust_name, t.empl_name ORDER BY t.cust_name desc) AS [rn]
FROM t
WHERE t.codes LIKE '%manager%'
)
Select cust_name, empl_name from cte WHERE [rn] = 1
Then I'm stuck. I thought to JOIN cte table and main table on 'cust_name' field, however having issues with that.
It sounds like you want to get who is 'ultimately' responsible for a customer, if the data has a row for each contact/rep the customer has, and showing the manager, if exists. This (assuming that your table is Tbl) would do that:
select
a.*,
Responsible=coalesce((select min(b.empl_name)
from Tbl b
where a.cust_name=b.cust_name
and b.codes like '%manager%'), a.empl_name)
from Tbl a
I used min() to avoid errors which may occur if the customer had more than one row with 'manager' in Codes.
Coalesce takes the current row's empl_name if there is no other record with manager; because the select subquery would return NULL.

SQL Server convert row values to columns

I have an SQL table like this
Name1 Name2 Department1 Department2 Location1 Location2
----------------------------------------------------------------------
Jhon Alex IT Marketing London Seattle
Mark Dan Sales R&D Paris Tokyo
How can I query these results in this format:
Name Department Location
---------------------------------------
Jhon IT London
Alex Marketing Seattle
Mark Sales Paris
Dan R&D Tokyo
Use cross apply
DEMO
select name,department,location
from t
cross apply
(
values(name1,department1,location1),(name2,department2,location2)
)cc (name, department,location)
OUTPUT:
name department location
Jhon IT London
Alex Marketing Seattle
Mark Sales Paris
Dan R&D T Tokyo
You could try to use SQL Server's UNPIVOT operator, but honestly a plain union query might even perform better:
SELECT Name1 AS Name, Department1 AS Department, Location1 AS Location FROM yourTable
UNION ALL
SELECT Name2, Department2, Location2 FROM yourTable;
Regarding your expected ordering, there is no sort of id column in your original table which maintains to which name pair each record belongs. So, what I have written above might be the best we can do here.
Try This:
DECLARE #TestDemo AS TABLE(Name1 VARCHAR(10),Name2 VARCHAR(10),Department1 VARCHAR(10),Department2 VARCHAR(10),Location1 VARCHAR(10),Location2 VARCHAR(10))
INSERT INTO #TestDemo VALUES('Jhon','Alex','IT','Marketing','London','Seattle')
INSERT INTO #TestDemo VALUES('Mark','Dan','Sales','R&D','Paris','Tokyo')
SELECT Name1 'Name',Department1 'Department',Location1 'Location' FROM #TestDemo
UNION ALL
SELECT Name2 'Name',Department2 'Department',Location2 'Location' FROM #TestDemo

How to select only distinct fields

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

select rows from a database table with common field in sqlite

i have a table as follows:
create table table1(id integer,firstname text,lastname text);
firstname lastname======== =========
1 ben taylor
2 rob taylor
3 rob smith
4 rob zombie
5 peter smith
6 ben smith
7 peter taylor
I want to select rows with a lastname , where the lastname must be shared by ben and rob and firstnames must be ben and rob.
Hence in the above table the result of the select query must be:
1 ben taylor
2 rob taylor
3 rob smith
6 ben smith
what must be the sql query to get the above results?
I tried - select * from table1 as a,table1 as b where a.firstname='ben' and b.firstname='rob' and a.lastname=b.lastname this joined all the resultant rows which is not what i inteneded.
You can use two filtering joins to demand that the lastname is shared with both a Ben and a Rob:
select *
from Table1 t1
join Table1 rob
on rob.firstname = 'rob'
and t1.lastname = rob.lastname
join Table1 ben
on ben.firstname = 'ben'
and t1.lastname = ben.lastname
where t1.firstname in ('ben', 'rob')
Live example at SQL Fiddle.
select *
from table1 where first_name = 'ben' or first_name = 'rob'
and last_name
in (select last_name from table1 where first_name = 'rob' and last_name
in (select last_name from table1 where first_name = 'ben'))