how to pivot multiple records - sql

This is my table structure !
create table t(floor int,apt int)
insert into t values(1,1),(1,2),(1,4),(2,5),(2,6),(2,7)
I want to get like this!
floor room1 room2 room3
1 1 2 4
2 5 6 7

Use a PIVOT in this case.
SELECT * FROM
(
SELECT floor,
apt,
NumberedApt = 'room' + CAST(ROW_NUMBER() OVER
(PARTITION BY floor ORDER BY apt) AS NVARCHAR(100))
FROM t
) AS OrderApts
PIVOT (MAX(apt) FOR Numberedapt IN (room1, room2, room3)) AS PivotedApts
Here is and SQLFiddle of the above working.
If you are going to get many more 'room' columns then you might want to consider using a dynamic pivot, but they can be inefficient due to not having a query plan.

More on pivot here
1.you need to use Row_number() partition by floor to get row then pivot to get your requirement
select p.floor,p.[1] as room1,p.[2] as room2,p.[3] as room3 from
(
select floor,apt,row_number() over(partition by floor order by apt) as rn from #t) as t
pivot
(
min(t.apt)
for t.rn in([1],[2],[3])
)as p;
See in Action

Related

Azure SQL Transpose a table with all rows

I use Azure SQL database. I have a table - test_excel_poc_head with the below values which I want to transpose using link id values as the columns
The intended output is below where the column is the 'link_id' values. The link_id values are dynamic
I started using UNPIVOT and PIVOT option and below is my unpivot query and results:
SELECT link_id,head_values
FROM
(SELECT link_id,comp1,comp2,comp3,comp4
FROM [dbo].[test_excel_poc_head]
) AS cp
UNPIVOT
(head_values FOR head_value in (comp1,comp2,comp3,comp4)
) AS up
RESULTS:
Now when I add the PIVOT code, it expects an aggregate function which I do not have as it is a string and it errors out.
If I add MAX as the aggregate function, I do not get the intended result.
SELECT * FROM (
SELECT link_id,head_values
FROM
(SELECT link_id,comp1,comp2,comp3,comp4
FROM [dbo].[test_excel_poc_head]
) AS cp
UNPIVOT
(head_values FOR head_value in (comp1,comp2,comp3,comp4)
) AS up
) temp_results
PIVOT(
MAX(head_values)
FOR link_id
IN (
[1],[2],[3],[4],[5],[6]
)
) AS PivotTable
RESULT:
But this is not my expected result. Is there any other option to achieve PIVOT without the use of agg functions?
Thanks for your time and help.
I tried my luck. Could you check below query if it works,
What I did different to your query is making the result of UNPIVOT distinct by adding row_number to it so that the later PIVOT will take max of each row and display separately. My bad if the explanation doesn't makes sense to you.
select [1],[2],[3],[4],[5],[6]
from
( select link_id,head_values,
row_number() over (partition by link_id order by link_id) rn
from
( select link_id
,cast(comp1 as varchar(255)) as comp1
,cast(comp2 as varchar(255)) as comp2
,cast(comp3 as varchar(255)) as comp3
,cast(comp4 as varchar(255)) as comp4
from [dbo].[test_excel_poc_head]
) as cp
unpivot
(
head_values for head_value in (comp1,comp2,comp3,comp4)
) as up
) temp_results
pivot
(
max(head_values)
for link_id in ([1],[2],[3],[4],[5],[6])
) as pivottable;
db<>fiddle for your reference.

Is there a way to join row values from 3 to 3?

As I mentioned in the title, I need to get values from the first 3 rows, then the second 3 rows, and on, and join them in rows.
Example
Input:
column
-------
Mu1
Mu2
Mu3
Mu4
Mu5
Mu6
Mu7
Mu8
Mu9
Output:
Column
------------
Mu1,Mu2,Mu3
Mu4,Mu5,Mu6
Mu7,Mu8,Mu9
Thank you so much, and I'm sorry if I'm posting something in the wrong way, this is my first question here.
You can use a window function (row_number) inside a CTE to assign a row number to each of your row.
Then you can use a group by with string_agg to concatenate your values 3 rows at a time:
declare #t table (col varchar(5))
insert into #t
values
('Mu1'),
('Mu2'),
('Mu3'),
('Mu4'),
('Mu5'),
('Mu6'),
('Mu7'),
('Mu8'),
('Mu9')
;with grp as
(
select col,
rn = row_number() over (order by col)
from #t
)
select
string_agg(col, ',') as [Column]
from grp
group by (rn -1)/ 3
Results:

Split one row into multiple rows in SQL with amounts divided equally

I have a table that contains ID, an amount column and a count column. For each row I would like to split them into multiple rows, based on the count column. I would then like the amount column to be split evenly between these rows, and create a new id based on the original id and the row count.
This is how the table looks like:
ID Amount Count
1001 8 2
1002 15 3
And this is the desired output
ID Amount
1001-1 4
1001-2 4
1002-1 5
1002-2 5
1002-3 5
Whats the best approach for this?
You can use a recursive CTE. This looks something like:
with recursive cte as (
select id, amount / cnt as amount, cnt, 1 as lev
from t
union all
select id, amount, cnt, lev + 1
from t
where lev < cnt
)
select id || '-' || lev, amount
from cte;
Note that this uses standard syntax; the exact syntax might vary depending on your database.
Unfortunately, Redshift does not support recursive queries.
Here is another option using a temporary table of numbers.
create temp table tmp(n int);
insert into tmp(n) values (1), (2), (3), (4), ...; -- expand as needed
select concat(t.id, '-', p.n) id, t.amount/t.count amount
from mytable t
inner join tmp p on p.n <= t.count
order by t.id, p.n

Get a single value where the latest date

I need to get the latest price of an item (as part of a larger select statement) and I can't quite figure it out.
Table:
ITEMID DATE SALEPRICE
1 1/1/2014 10
1 2/2/2014 20
2 3/3/2014 15
2 4/4/2014 13
I need the output of the select to be '20' when looking for item 1 and '13' when looking for item 2 as per the above example.
I am using Oracle SQL
The most readable/understandable SQL (in my opinion) would be this:
select salesprice from `table` t
where t.date =
(
select max(date) from `table` t2 where t2.itemid = t.itemid
)
and t.itemid = 1 -- change item id here;
assuming your table's name is table and you only have one price per day and item (else the where condition would match more than one row per item). Alternatively, the subselect could be written as a self-join (should not make a difference in performance).
I'm not sure about the OVER/PARTITION used by the other answers. Maybe they could be optimized to better performance depending on the DBMS.
Maybe something like this:
Test data
DECLARE #tbl TABLE(ITEMID int,DATE DATETIME,SALEPRICE INT)
INSERT INTO #tbl
VALUES
(1,'1/1/2014',10),
(1,'2/2/2014',20),
(2,'3/3/2014',15),
(2,'4/4/2014',13)
Query
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY ITEMID ORDER BY [DATE] DESC) AS rowNbr,
tbl.*
FROM
#tbl AS tbl
)
SELECT
*
FROM
CTE
WHERE CTE.rowNbr=1
Try this!
In sql-server may also work in Oracle sql
select * from
(
select *,rn=row_number()over(partition by ITEMID order by DATE desc) from table
)x
where x.rn=1
You need Row_number() to allocate a number to all records which is partition by ITEMID so each group will get a RN,then as you are ordering by date desc to get Latest record
SEE DEMO

Ensuring only distinct records are returned with DISTINCT

Given the following table:
date_field_one date_field_two arbitrary_value
---------------- ---------------- -----------------
1/1/11 1/3/11 cheese
1/1/11 1/4/11 the color orange
2/2/11 2/3/11 1
2/2/11 2/4/11 2
My problem: I'm not sure how to go about structuring a query using a set based approach that yields the following results:
for each distinct date, the record with the earliest
date_field_two value is returned
Any ideas?
Edit for new response! The solution posted by M.Ali may be the best fit for your specific case as it will ensure you only ever get one row result from your base data, even if there exist multiple candidate rows for your answer ( as in, date_field_one, date_field_two combinations are not distinct ). The following will return multiple results per date_field_one, date_field_two combination in the not-distinct scenario:
SELECT t.date_field_one, t.date_field_two, t.arbitrary_value
FROM ( SELECT date_field_one,
date_field_two = MIN( date_field_two )
FROM dbo.[table]
GROUP BY date_field_one ) dl
LEFT JOIN dbo.[table] t
ON dl.date_field_one = t.date_field_one
AND dl.date_field_two = t.date_field_two;
;WITH CTE
AS
(
SELECT *, rn = ROW_NUMBER() OVER (PARTITION BY date_field_one ORDER BY date_field_two
ASC)
FROM TableName
)
SELECT * FROM CTE
WHERE rn = 1
Something like this:
select date_field_one, min(date_field_two)
from yourtable
group by date_field_one
select date_field_one, min(date_fileld_two)
from table
group by date_field_one
try this for latest...........
select date_field_one ,min(date_field_two) date_field_two
from table group by date_field_one