How I can convert Rows to Columns in SQL? - 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;

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;

Select Latest 3 records

Using SQL Server 2014. I have data that lists a Unique Identifier, a Sale Data and a Sale Price. I would like to extract into a VIEW the last 3 sales for each unique Id.
Example of data:
+------+-----------+------------+-------------+
| ID | UNIQUE_ID | SaleDate | SalePrice |
+------+-----------+------------+-------------+
| 8210 | 1-5 | 2015-09-29 | 0 |
| 8211 | 1-6 | 2016-11-01 | 485672 |
| 8212 | 1-7 | 1994-06-24 | 120000 |
| 8213 | 1-1 | 1996-09-06 | 170000 |
| 8214 | 1-1 | 2000-01-28 | 265000 |
| 8215 | 1-1 | 2013-10-02 | 305000 |
| 8216 | 1-1 | 2015-11-20 | 1425000 |
| 8217 | 1-3 | 1994-01-12 | 1 |
| 8218 | 1-3 | 2001-04-30 | 1 |
| 8219 | 1-3 | 2004-09-30 | 0 |
+------+-----------+------------+-------------+
The result in the view would list each Unique ID and then 6 fields:
SaleDate1
SalePrice1
SaleDate2
SalePrice2
SaleDate3
SalePrice3
Any hints appreciated.
You can use row_number() :
SELECT t.*
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY UNIQUE_ID ORDER BY SaleDate DESC, SalePrice DESC) AS Seq
FROM table t
) t
WHERE Seq <= 3;
You can use a window function to filter data and then conditional aggregation to get the 6 columns you need:
declare #tmp table(ID int, UNIQUE_ID varchar(50), SaleDate date, SalePrice int)
insert into #tmp values
(8210, '1-5','2015-09-29', 0 )
,(8211, '1-6','2016-11-01', 485672 )
,(8212, '1-7','1994-06-24', 120000 )
,(8213, '1-1','1996-09-06', 170000 )
,(8214, '1-1','2000-01-28', 265000 )
,(8215, '1-1','2013-10-02', 305000 )
,(8216, '1-1','2015-11-20', 1425000)
,(8217, '1-3','1994-01-12', 1 )
,(8218, '1-3','2001-04-30', 1 )
,(8219, '1-3','2004-09-30', 0 )
SELECT t.UNIQUE_ID
,max(case when t.Seq = 1 then SaleDate else null end) as SaleDate1
,sum(case when t.Seq = 1 then SalePrice else null end) as SalePrice1
,max(case when t.Seq = 2 then SaleDate else null end) as SaleDate2
,sum(case when t.Seq = 2 then SalePrice else null end) as SalePrice2
,max(case when t.Seq = 3 then SaleDate else null end) as SaleDate3
,sum(case when t.Seq = 3 then SalePrice else null end) as SalePrice3
FROM (SELECT x.*,
ROW_NUMBER() OVER (PARTITION BY UNIQUE_ID
ORDER BY SaleDate DESC, SalePrice DESC) AS Seq
FROM #tmp x
) t
WHERE t.Seq < 4
group by t.UNIQUE_ID
Results:
The following query return the 3 most recent sold rows of each item
select * from
(
select UNIQUE_ID,SaleDate,SalePrice,rank() over (partition by UNIQUE_ID order by SaleDate desc) as rnk
from salestable
) where rnk<4

Merge rows with same ID

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

SQL Server: Swap two lines depending on criteria

Suppose a table named Sales with this data in SQL Server
--------------------------------------------
Id | Customer_Id | Rate | Pid
--------------------------------------------
180 | 374 | 1 | A01
277 | 374 | 0 | NULL
346 | 785 | 1 | D03
476 | 785 | 0 | NULL
1821 | 1234 | 0 | E07
25951 | 1951 | 1 | K73
How update my table to swap Rate and Pid values between lines having same customer_Id, so I can have a result like this:
--------------------------------------------
Id | Customer_Id | Rate | Pid
--------------------------------------------
180 | 374 | 0 | NULL
277 | 374 | 1 | A01
346 | 785 | 0 | NULL
476 | 785 | 1 | D03
1821 | 1234 | 0 | E07
25951 | 1951 | 1 | K73
How can I achieve this?
If you always have at most two records per customer then you can use the following query:
SELECT ID, Customer_Id,
CASE
-- 2 records per Customer_id -> swap
WHEN COUNT(*) OVER (PARTITION BY Customer_id) = 2 THEN
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Customer_id ORDER BY ID) = 1
THEN LEAD(Rate) OVER (PARTITION BY Customer_id ORDER BY ID)
ELSE LAG(Rate) OVER (PARTITION BY Customer_id ORDER BY ID)
END
-- 1 record per Customer_id -> don't swap
ELSE Rate
END,
CASE
WHEN COUNT(*) OVER (PARTITION BY Customer_id) = 2 THEN
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Customer_id ORDER BY ID) = 1
THEN LEAD(Pid) OVER (PARTITION BY Customer_id ORDER BY ID)
ELSE LAG(Pid) OVER (PARTITION BY Customer_id ORDER BY ID)
END
ELSE Pid
END
FROM Sales
Demo here
Edit:
If you want to UPDATE then you can wrap the above query in a CTE and do the update on the CTE:
;WITH ToUpdate AS (
SELECT ID, Customer_Id, Rate, Pid,
COUNT(*) OVER (PARTITION BY Customer_id) AS cnt,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Customer_id ORDER BY ID) = 1
THEN LEAD(Rate) OVER (PARTITION BY Customer_id ORDER BY ID)
ELSE LAG(Rate) OVER (PARTITION BY Customer_id ORDER BY ID)
END AS NewRate,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Customer_id ORDER BY ID) = 1
THEN LEAD(Pid) OVER (PARTITION BY Customer_id ORDER BY ID)
ELSE LAG(Pid) OVER (PARTITION BY Customer_id ORDER BY ID)
END AS NewPid
FROM Sales)
UPDATE ToUpdate
SET Rate = NewRate, Pid = NewPid
WHERE cnt = 2
Demo here
This will do it, with the caveat that you have at most only two records with the same Customer_Id...
update Sales
set Rate =
(
select Rate from Sales sls
where sls.Customer_Id = Sales.Customer_Id
and sls.Rate <> Sales.Rate
)

Select ONLY row with max(id) in SQL SERVER

I have a table A :
ID | ProductCatId | ProductCode | Price
1 | 1 | PROD0001 | 2
2 | 2 | PROD0005 | 2
3 | 2 | PROD0005 | 2
4 | 3 | PROD0008 | 2
5 | 5 | PROD0009 | 2
6 | 7 | PROD0012 | 2
I want to select ID,ProductCatId,ProductCode,Price with condition :
"if ProductCatId exists same value ,so get ProductCatId with max(ID)", like :
ID | ProductCatId | ProductCode | Price
1 | 1 | PROD0001 | 2
3 | 2 | PROD0005 | 2
4 | 3 | PROD0008 | 2
5 | 5 | PROD0009 | 2
6 | 7 | PROD0012 | 2
Go for window function and row_number()
select ID , ProductCatId , ProductCode , Price
from (
select ID , ProductCatId , ProductCode , Price, row_number() over (partition by ProductCatId order by ID desc) as rn
from myTable
) as t
where t.rn = 1
select
top 1 with ties
ID,ProductCatId,ProductCode,Price
from
table
order by
row_number() over (partition by productcatid order by id desc)
may use row_number():
select t.*
from (select t.*,
row_number() over (partition by ProductCatId order by ID desc) as seqnum
from #Table t
) t
where seqnum = 1
order by ID;
You can try this,
Select Max(ID),ProductCatId,ProductCode,price
From TableName
Group By ProductCatId,ProductCode,price
A little shorter:
SELECT DISTINCT
max(ID) OVER (PARTITION BY ProductCatId,
ProductCode,
Price) AS ID,
ProductCatId,
ProductCode,
Price,
FROM myTable