SQL Server convert row values to columns - sql

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

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

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)

Turning rows to columns in postgres

I have one table with rental contracts. (Postgres v10.18)
Like this:
Table Rental
Id Start Main_tenant_id Obect_id
101011 1.1.2021 1000 200
100222 1.1.2021 2050 210
If the Object has more than one Tenant the other ones a saved in a separate Table like this:
Table Rental_extra
Id rental_id xtra_tenant
20001 100222 3000
20002 100222 2700
20003 100222 2800
And i have a Person table like this:
Table Person
Id first_name last_name
1000 Peter Mcdonald
2050 Dan Hunt
3000 Steve Cole
2700 Ian Wright
2800 William Pears
Now i need to get this output:
Goal
Id tenant 1 tenant 2 tenant 3 tenant 4
101011 Peter Mcdonald null null null
100222 Dan Hunt Steve Cole Ian Wright William Pears
What's the best way? I tried with some crosstab example but couldn't make it work.
SELECT *
FROM crosstab(
$$
SELECT t.id, tenant_nr, concat_ws(' ', p.first_name, p.last_name)
FROM (
SELECT id, 0 AS tenant_nr, main_tenant_id AS tenant_id
FROM rental
UNION ALL
SELECT rental_id, row_number() OVER (PARTITION BY rental_id ORDER BY id), xtra_tenant
FROM extra_tenant
) t
JOIN person p ON p.id = t.tenant_id
ORDER BY 1 DESC, t.tenant_nr
$$
) AS goal(id int, tenant_1 text, tenant_2 text, tenant_3 text, tenant_4 text);
db<>fiddle here
Detailed explanation here:
PostgreSQL Crosstab Query
Postgres - Transpose Rows to Columns

sql "group by" same PersonID, different PersonNames. Eliminate duplicates

I have a (rather dirty) datasource (excel) that looks like this:
ID | Name | Subject | Grade
123 | Smith, Joe R. | MATH | 2.0
123 | Smith, Joe Rodriguez | FRENCH | 3.0
234 | Doe, Mary Jane D.| BIOLOGY | 2.5
234 | Doe, Mary Jane Dawson| CHEMISTRY | 2.5
234 | Doe, Mary Jane | FRENCH | 3.5
My application's output should look like this:
Smith, Joe R.
123
MATH | 2.0
FRENCH | 3.0
So basically I want to do query (just for the ID/Person parent 'container') something like:
SELECT DISTINCT ID, Name FROM MyTable<br/>
or
SELECT ID, Name FROM MyTable GROUP BY ID
Of course both of the above are invalid and won't work.
I would like to 'combine' the same ID's and ignore/truncate the other records with the same ID/different Name (because we all know they're the same person since ID is our identifier and clearly it's just a typo/dirty data).
Can this be done by a single SELECT query?
If you don't really care which value shows up in the name field, use MAX() or MIN():
SELECT ID,
MAX(Name) AS Name
FROM [YourTable]
GROUP BY ID
Here's a working example to play with: https://data.stackexchange.com/stackoverflow/q/116699/
You can find the MIN or MAX Value of Name
SELECT ID, Max(Name)
FROM MyTable
GROUP BY ID
SELECT A.ID, A.NAME, T.Subject, T.Grade
FROM (SELECT ID, MIN(NAME) AS NAME
FROM MyTable
GROUP BY ID) A
LEFT JOIN MyTable T on A.ID = T.ID
Will give you something like
123 Smith, Joe R. Math 2.0
123 Smith, Joe R. FRENCH 3.0
234 Doe, Mary Jane BIOLOGY 2.5
234 Doe, Mary Jane CHEMISTRY 2.5
234 Doe, Mary Jane FRENCH 3.5
If you don't care which name you keep, you can use a MAX() or MIN() aggregate to pick just one name:
SELECT ID, MAX(Name) as Name
FROM MyTable GROUP BY ID

SQL: Add counters in select

I have a table which contains names:
Name
----
John Smith
John Smith
Sam Wood
George Wright
John Smith
Sam Wood
I want to create a select statement which shows this:
Name
'John Smith 1'
'John Smith 2'
'Sam Wood 1'
'George Wright 1'
'John Smith 3'
'Sam Wood 2'
In other words, I want to add separate counters to each name. Is there a way to do it without using cursors?
Use ROW_NUMBER():
SELECT Name, ROW_NUMBER() OVER(Partition BY Name ORDER BY Name) as [Rank]
FROM MyTable
Doing:
select name, count(*) as total from table group by name;
will get you something that looks like this:
name | total
-------------+------------
John Smith | 2
-------------+------------
Sam Wood | 2
-------------+------------
George Wright| 1
This isn't what you really wanted though - ROW_NUMBER(), as ck pointed out, is what you want, but not all databases support it - mysql doesn't, for example. If you're using MySQL, this might help:
ROW_NUMBER() in MySQL