Merge rows with same ID - sql

I have the following table:
ID | variant_name | variant_color
1 | BMW 7-series | Black
2 | Volvo C60 | Gray
1 | BMW 3-series | White
3 | Subaru Forester| Orange
2 | Volvo XC90 | Green
How can I query to gain this result:
ID | variant_name_1 | variant_color_1| variant_name_2 | variant_color_2|
1 | BMW 7-series | Black | BMW 3-series | White |
2 | Volvo C60 | Gray | Volvo XC90 | Green |
3 | Subaru Forester| Orange | | |
Each ID has a maximum number of variants of 2.
Thanks!

It'll work in sql server/posgresql/oracle - use row_number()
http://sqlfiddle.com/#!18/a7540/10424
select id, max(case when rn=1 then variant_name end) as variant_name1,max(case when rn=1 then variant_color end) as variant_color1,
max(case when rn=2 then variant_name end) as variant_name2,max(case when rn=2 then variant_color end) as variant_color2
from
(
select id, variant_name, variant_color, row_number() over(partition by id order by id) as rn
from tablename)a
group by id

You can use row_number() to do conditional aggregation :
select id, max(case when seq = 1 then variant_name end) as variant_name_1,
max(case when seq = 1 then variant_color end) as variant_color_1,
max(case when seq = 2 then variant_name end) as variant_name_2,
max(case when seq = 2 then variant_color end) as variant_color_2
from (select t.*, row_number() over (partition by id order by variant_color) as seq
from table t
) t
group by id;

Try left joining with itself:
select c1.id,
c1.variant_name as variant_name_1,
c1.variant_color as variant_color_1,
c2.variant_name as variant_name_2,
c2.variant_color as variant_color_2
from cars c1
left join cars c2
on c1.id = c2.id
and c1.seq <> c2.seq
group by c1.id

Related

how to join or merge as one row in SQL

I have these 2 tables;
table A
| ID | Name | S_ID |
|----|--------|------|
| 1 | mark | 1 |
| 2 | john | 2 |
table B (rows are not limited to 5 and Scores could be more than 3)
| ID | S_ID | Score |
|-------------------|
| 1 | 1 | 90% |
| 2 | 1 | 80% |
| 3 | 1 | 10% |
| 4 | 2 | 10% |
| 5 | 2 | 12% |
Normally using "GROUP_CONCAT" would work but is there any way to achieve this;
You are asking for a pivot query, either with or without a fixed number of columns. Assuming the former, we can use ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY S_ID ORDER BY ID) rn
FROM tableB
)
SELECT
a.ID,
a.Name,
MAX(CASE WHEN rn = 1 THEN b.S_ID END) AS S_ID_1,
MAX(CASE WHEN rn = 1 THEN b.Score END) AS Score_1,
MAX(CASE WHEN rn = 2 THEN b.S_ID END) AS S_ID_2,
MAX(CASE WHEN rn = 2 THEN b.Score END) AS Score_2,
MAX(CASE WHEN rn = 3 THEN b.S_ID END) AS S_ID_3,
MAX(CASE WHEN rn = 3 THEN b.Score END) AS Score_3
FROM cte
GROUP BY
a.ID,
a.Name;

BigQuery/SQL: How do I join two tables and use a column value as column name?

I have these tables:
Foods
| food_id | title |
| 1 | soy milk |
| 2 | banana |
| 3 | apple |
Nutrients
| food_id | nutrient_id | amount |
| 1 | n1 | 0.05 |
| 1 | n2 | 2 |
| 1 | n3 | 34 |
...
I need this:
| food_id | title | n1 | n2 | n3 |
| 1 | soy milk | 0.05 | 2 | 34 |
| 2 | banana | | | |
| 3 | apple | | | |
Struct would also work.
I know all the joins, but can't wrap my head around this... how do I put nutrient_id into a column title or a Struct key?
If you have a fixed list of nutrients, then you can use join and group by:
select f.food_id, f.title,
max(case when n.nutrient_id = 1 then n.amount end) as nutrient_1,
max(case when n.nutrient_id = 2 then n.amount end) as nutrient_2,
max(case when n.nutrient_id = 3 then n.amount end) as nutrient_3
from foods left join
nutrients n
on n.food_id = f.food_id
group by f.food_id, f.title;
Note: This uses a left join in case your data has foods like Twinkies which have no known nutritional value.
If you don't know the full list of nutrients, then you don't know what columns are in the result set. I would suggest using JSON or arrays to represent the values.
Use ROW_NUMBER with pivoting logic:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY food_id ORDER BY nutrient_id) rn
FROM Nutrients
)
SELECT
f.food_id,
f.title,
MAX(CASE WHEN t.rn = 1 THEN t.amount END) AS n1,
MAX(CASE WHEN t.rn = 2 THEN t.amount END) AS n2,
MAX(CASE WHEN t.rn = 3 THEN t.amount END) AS n3
FROM Foods f
LEFT JOIN cte
ON f.food_id = t.food_id
GROUP BY
f.food_id,
f.title;
Below is for BigQuery Standard SQL and assumes that number of nutrients is not fixed per food so pivot'ing approach will not be simple and rather answering below question :
how do I put nutrient_id into ... a Struct key?
#standardSQL
SELECT *
FROM `project.dataset.Foods`
LEFT JOIN (
SELECT food_id, ARRAY_AGG(STRUCT(nutrient_id, amount)) nutrients_facts
FROM `project.dataset.Nutrients`
GROUP BY food_id
)
USING(food_id)
If to apply above to sample data from your question - result is
Try this
Select food_id, title,
max( case when nutrient_id =
'n1' then
amount end) as n1,
max( case when nutrient_id =
'n2' then
amount end) as n2,
max( case when nutrient_id =
'n3' then
amount end) as n3
from table1 t1 join
Table2 t2
on t1.food_id=t2.food_id
Group by food_id, title

How I can convert Rows to Columns in SQL?

I have a table like this:
Phones
------------------------------------------------------
| CustomerID | PhoneID | PhoneNum |
-----------------------------------------------------
| 1 | 101 | 09811111 |
| 1 | 102 | 09822222 |
| 1 | 103 | 09833333 |
| 2 | 104 | 09844444 |
| 2 | 105 | 09855555 |
-------------------------------------------------
I want query that give me bellow result:
--------------------------------------------------------------------------
| CustomerID | PhoneNum1 | PhoneNum2 | PhoneNum3 |
--------------------------------------------------------------------------
| 1 | 09811111 | 09822222 | 09833333 |
| 2 | 09844444 | 09855555 | NULL |
---------------------------------------------------------------------------
How can I build the result?
We can handle this requirement with the help of ROW_NUMBER and a pivot query:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY PhoneID) rn
FROM Phones
)
SELECT
CustomerID,
MAX(CASE WHEN rn = 1 THEN PhoneNum END) AS PhoneNum1,
MAX(CASE WHEN rn = 2 THEN PhoneNum END) AS PhoneNum2,
MAX(CASE WHEN rn = 3 THEN PhoneNum END) AS PhoneNum3
FROM cte
GROUP BY
CustomerID
ORDER BY
CustomerID;
Demo
The query above was very useful. But when I use the Where, the result is not right
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY PhoneID) rn
FROM Phones
)
SELECT
CustomerID,
MAX(CASE WHEN rn = 1 THEN PhoneNum END) AS PhoneNum1,
MAX(CASE WHEN rn = 2 THEN PhoneNum END) AS PhoneNum2,
MAX(CASE WHEN rn = 3 THEN PhoneNum END) AS PhoneNum3
FROM cte
where PhoneNum ='09811111'
GROUP BY
CustomerID
ORDER BY
CustomerID;
Result:
--------------------------------------------------------------------------
| CustomerID | PhoneNum1 | PhoneNum2 | PhoneNum3 |
--------------------------------------------------------------------------
| 1 | 09811111 | NULL | NULL |
---------------------------------------------------------------------------
I find a way for my asked
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY PhoneID) rn
FROM Phones
)
SELECT
CustomerID,
MAX(CASE WHEN rn = 1 THEN PhoneNum END) AS PhoneNum1,
MAX(CASE WHEN rn = 2 THEN PhoneNum END) AS PhoneNum2,
MAX(CASE WHEN rn = 3 THEN PhoneNum END) AS PhoneNum3
FROM cte
where CustomerID = ( select CustomerID from cte where PhoneNum ='09811111' )
GROUP BY
CustomerID
ORDER BY
CustomerID;

How to display data horizontally in SQL Server without using pivot and unpivot?

Is there any way to display Horizontal data in sql without using pivot and unpivot method ? Below image is a sample of using pivot and unpivot but I want to know is there any other method of doing it ?
Example
Original Data
+----+-------+-----------------+
| id | Place | Location |
+----+-------+-----------------+
| 1 | CP01 | 3.1415,101.7231 |
| 2 | CP02 | 3.2314,101.3254 |
| 3 | CP03 | 3.9415,101.0192 |
| 4 | CP04 | 3.5490,102.0435 |
| 5 | CP05 | 3.2562,101.2597 |
| 6 | CP06 | 3.1134,102.5915 |
+----+-------+-----------------+
Horizontal Data
+----------+-----------------+-----------------+-----------------+-----+
| Cols | 1 | 2 | 3 | ... |
+----------+-----------------+-----------------+-----------------+-----+
| Location | 3.1415,101.7231 | 3.2314,101.3254 | 3.9415,101.0192 | ... |
| Place | CP01 | CP02 | CP03 | ... |
+----------+-----------------+-----------------+-----------------+-----+
Image for clarification
You may looking for something like this:
DECLARE #GeoData TABLE (Id INT, Place VARCHAR(10), [Location] VARCHAR(100))
INSERT INTO #GeoData
SELECT 1,'CP01','3.1415,101.7231'
UNION ALL
SELECT 2,'CP02','3.2314,101.3254'
SELECT 'Location' AS Cols,
MAX(CASE WHEN t.Id = 1 THEN t.Location END) AS [1],
MAX(CASE WHEN t.Id = 2 THEN t.Location END) AS [2],
MAX(CASE WHEN t.Id = 2 THEN t.Location END) AS [3]
FROM #GeoData t
UNION ALL
SELECT 'Place' AS Cols,
MAX(CASE WHEN t.Id = 1 THEN t.Place END) AS [1],
MAX(CASE WHEN t.Id = 2 THEN t.Place END) AS [2],
MAX(CASE WHEN t.Id = 2 THEN t.Place END) AS [3]
FROM #GeoData t
The similar way is you could use of cross apply with some conditional aggregation in order to achieve pivot and unpivot way
SELECT a.Col1, MAX(case when a.ID = 1 then a.Value end) [1],
MAX(case when a.ID = 2 then a.Value end) [2],
MAX(case when a.ID = 3 then a.Value end) [3]
FROM table T
CROSS APPLY (VALUES ('Place', T.place, T.ID),
('Location', T.Location, T.ID)
) as a(Col1, Value, ID)
group by a.Col1

Inconsistent Transpose

Given a table A has the following data:
+----------+-------+
| Supplier | buyer |
+----------+-------+
| A | 1 |
| A | 2 |
| B | 3 |
| B | 4 |
| B | 5 |
+----------+-------+
My question is, can I transpose the second column so the resultant table will be like:
+----------+--------+--------+--------+
| Supplier | buyer1 | buyer2 | buyer3 |
+----------+--------+--------+--------+
| A | 1 | 2 | |
| B | 3 | 4 | 5 |
+----------+--------+--------+--------+
Assuming the maximum number of buyers is known as three.
You could use a common table expression to give each buyer an order within the supplier, and then just do a regular case to put them in columns;
WITH cte AS (
SELECT supplier, buyer,
ROW_NUMBER() OVER (PARTITION BY supplier ORDER BY buyer) rn
FROM Table1
)
SELECT supplier,
MAX(CASE WHEN rn=1 THEN buyer END) buyer1,
MAX(CASE WHEN rn=2 THEN buyer END) buyer2,
MAX(CASE WHEN rn=3 THEN buyer END) buyer3
FROM cte
GROUP BY supplier;
An SQLfiddle to test with.
You may consider using PIVOT clause:
select *
from (
select supplier, buyer, row_number() over (partition by supplier order by buyer) as seq
from a
)
pivot (max(buyer) for seq in (1 as buyer1, 2 as buyer2, 3 as buyer3));
SQLFiddle here.