How to create a pivot tablefor this problem: - sql

This is my original table:
Original table
And I would like it to be as:
CityID | 1 | 2 | 3 | 4 | 5 | 6 | 7
_____________________________________
1024 0800 0900 and so on...
Here is my code, but I get a syntax error near FOR.
select * from
(select SIDURI as CityID, DAY as ArrivalDay, T_FROM as TimeArrival
from RNFIL488) as timingTable
pivot(
timing.SIDURI as CityID
timingTable.T_FROM as TimeArrival
for timing.DAY as ArrivalDay in (
[1],
[2],
[3],
[4],
[5],
[6],
[7]
)
) as pivot_table

Use ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CityID ORDER BY ArrivalDay) rn
FROM RNFIL488
)
SELECT
CityID,
MAX(CASE WHEN rn = 1 THEN TimeArrival END) AS [1],
MAX(CASE WHEN rn = 2 THEN TimeArrival END) AS [2],
MAX(CASE WHEN rn = 3 THEN TimeArrival END) AS [3],
MAX(CASE WHEN rn = 4 THEN TimeArrival END) AS [4],
MAX(CASE WHEN rn = 5 THEN TimeArrival END) AS [5],
MAX(CASE WHEN rn = 6 THEN TimeArrival END) AS [6],
MAX(CASE WHEN rn = 7 THEN TimeArrival END) AS [7]
FROM cte
GROUP BY
CityID;
This assumes that your original source table would always have 7 arrival days per city. If not, then we might have to use a calendar table to bring in the missing data. Also, I am avoiding the PIVOT operator, because often the above approach performs better (and I also find it much easier to read).

I recommend if you don't have a lot data then you can truncate the table and re-fill table. And don't forget your id column should be unique and use auto_increment on id.

Related

How to I convert this column to row format ?

I can use Pivot but my table here has only 2 columns so I don't know how to go about with it. A class has maximum of 5 UserIDs so I want to have a ClassID and associated 5 user names.
UserID ClassID
RK980 5
LO567 5
YY667 5
RT223 5
LT987 3
What I need is :
ClassID User1 User2 User3 User4 User5
5 RK980 LO567 YY667 RT223 NULL
3 LT987 NULL NULL NULL NULL
Thank you !
You can use row_number(). I would then tend to go for conditional aggregation rather than pivot:
select classid,
max(case when seqnum = 1 then userid end) as user1,
max(case when seqnum = 2 then userid end) as user2,
max(case when seqnum = 3 then userid end) as user3,
max(case when seqnum = 4 then userid end) as user4,
max(case when seqnum = 5 then userid end) as user5
from (select t.*, row_number() over (partition by classid order by userid) as seqnum
from t
) t
group by classid;
you can use pivot with row_number
DECLARE #MyTable TABLE (UserID VARCHAR(10), ClassID INT)
INSERT INTO #MyTable VALUES
('RK980', 5 ),
('LO567', 5 ),
('YY667', 5 ),
('RT223', 5 ),
('LT987', 3 )
SELECT ClassID, [1] User1, [2] User2, [3] User3, [4] User4, [5] User5 FROM
(SELECT * ,
ROW_NUMBER() OVER(PARTITION BY ClassID ORDER BY UserID ) AS RN
FROM #MyTable ) SRC
PIVOT(MAX(UserID) FOR RN IN ([1], [2], [3], [4], [5])) PVT
Result:
ClassID User1 User2 User3 User4 User5
----------- ---------- ---------- ---------- ---------- ----------
3 LT987 NULL NULL NULL NULL
5 LO567 RK980 RT223 YY667 NULL

Pivot table with more then one record

I have data that is structured as follow:
LocationId, GroupId, DayOfWeek, Count, DatetimeValue15Min
2 9 4 5 2014-01-02 08:15:00.000
2 9 4 5 2014-01-02 09:15:00.000
I want to calculate the mode for each day, the data above already contains the count to know the mode. I have written a query with a pivot.
SELECT
pvt.LocationId, pvt.GroupId, [1], [2], [3], [4],[5]
FROM
#TempResult
PIVOT
(min ([DatetimeValue15Min])
FOR DayOfWeek IN ( [1], [2], [3], [4],[5])) AS pvt
In this case I have two modes but i want to show them both. My query returns in this case just the mode with the minimum value. I know that I can make a second query with the max value but what if i have more than two modes?
The output should be like:
LocationId GroupId 1 2 3 4 5
2 9 08:15, 09:15
I am using SQL Server 2005.
You are almost there. You just need to build the comma-separated list. A little xml type abuse works really well for that.
;WITH
t1 AS ( --Add a grouping id for quick reference
SELECT
[LocationId],[GroupId],[DayOfWeek],[DatetimeValue15Min],
DENSE_RANK() OVER(ORDER BY [LocationId],[GroupId],[DayOfWeek]) [i]
FROM #TempResult
),
t2 AS ( --Build a comma-separated list of all [DatetimeValue15Min] with same grouping id
SELECT [LocationId],[GroupId],[DayOfWeek],
CAST(REPLACE((SELECT CONVERT(time, [DatetimeValue15Min]) AS a FROM t1 WHERE [i] = t.[i] FOR xml PATH('')),'</a><a>',',') AS xml).value('a[1]','varchar(max)') [dtv_list]
FROM t1 t
)
SELECT pvt.LocationId, pvt.GroupId, [1], [2], [3], [4],[5]
FROM t2
PIVOT
(
min ([dtv_list])
FOR DayOfWeek IN ( [1], [2], [3], [4],[5])
) AS pvt
The xml trick works like this:
SELECT [DatetimeValue15Min] FOR XML -> <a>08:15</a><a>09:15</a>
replace '</a><a>' with ',' -> <a>08:15,09:15</a>
extract first node from xml -> '08:15,09:15'
SELECT LocationId, GroupId
, STUFF (
MAX (CASE WHEN DayOfWeek = 1 AND num = 1 THEN Value ELSE '' END)
+ MAX (CASE WHEN DayOfWeek = 1 AND num = 2 THEN Value ELSE '' END), 1, 2, '') AS [1]
, STUFF (
MAX (CASE WHEN DayOfWeek = 2 AND num = 1 THEN Value ELSE '' END)
+ MAX (CASE WHEN DayOfWeek = 2 AND num = 2 THEN Value ELSE '' END), 1, 2, '') AS [2]
, STUFF (
MAX (CASE WHEN DayOfWeek = 3 AND num = 1 THEN Value ELSE '' END)
+ MAX (CASE WHEN DayOfWeek = 3 AND num = 2 THEN Value ELSE '' END), 1, 2, '') AS [3]
, STUFF (
MAX (CASE WHEN DayOfWeek = 4 AND num = 1 THEN Value ELSE '' END)
+ MAX (CASE WHEN DayOfWeek = 4 AND num = 2 THEN Value ELSE '' END), 1, 2, '') AS [4]
, STUFF (
MAX (CASE WHEN DayOfWeek = 5 AND num = 1 THEN Value ELSE '' END)
+ MAX (CASE WHEN DayOfWeek = 5 AND num = 2 THEN Value ELSE '' END), 1, 2, '') AS [5]
FROM (
SELECT LocationId, GroupId, DayOfWeek, ', ' + CONVERT (VARCHAR(5), DatetimeValue15Min, 8) AS Value
, ROW_NUMBER() OVER(PARTITION BY LocationId, GroupId, DayOfWeek ORDER BY DatetimeValue15Min) AS num
FROM #TempResult
) T
GROUP BY LocationId, GroupId
UPD. Solution for unlimited number of values per day.
WITH cte AS (
SELECT LocationId, GroupId, DayOfWeek, CONVERT (VARCHAR(5), DatetimeValue15Min, 8) AS Value
, ROW_NUMBER() OVER(PARTITION BY LocationId, GroupId, DayOfWeek ORDER BY DatetimeValue15Min) AS num
FROM #TempResult
)
SELECT LocationId, GroupId
, MIN (CASE DayOfWeek WHEN 1 THEN Value END) AS [1]
, MIN (CASE DayOfWeek WHEN 2 THEN Value END) AS [2]
, MIN (CASE DayOfWeek WHEN 3 THEN Value END) AS [3]
, MIN (CASE DayOfWeek WHEN 4 THEN Value END) AS [4]
, MIN (CASE DayOfWeek WHEN 5 THEN Value END) AS [5]
FROM (
SELECT DISTINCT c1.LocationId, c1.GroupId, c1.DayOfWeek
, STUFF ( (
SELECT ', ' + c2.Value
FROM cte c2
WHERE c1.LocationId = c2.LocationId AND c1.GroupId = c2.GroupId AND c1.DayOfWeek = c2.DayOfWeek
FOR XML PATH ('')
), 1, 2, '') AS Value
FROM cte c1
) t
GROUP BY LocationId, GroupId

SQL Server 2008: Converting rows into columns

I have two tables:
CREATE TABLE #A (id int, cond_id int)
INSERT INTO #A (id, cond_id)
VALUES (101,20),
(101,22),
(101,24),
(102,23),
(102,22)
Now, each id can have max of 4 cond_ids. I want to populate table #B so that there is one id and all cond_ids will be populated in the columns as one row according to cond_id ascending.
like for id 102, cond_id 22 goes in cond_id and 23 goes in cond_id2.
create table #B (id int, cond_id1 int, cond_id2 int, cond_id3 int, cond_id4 int)
Desired result:
Table #B
id cond_id1 cond_id2 cond_id3 cond_id4
101 20 22 24 null
102 22 23 null null
Thanks in advance!
Because you know the maximum number of columns, one option is to use row_number, max and case:
with cte as (
select row_number() over (partition by id order by cond_id) rn, id, cond_id
from a)
select id,
max(case when rn = 1 then cond_id end) cond_id1,
max(case when rn = 2 then cond_id end) cond_id2,
max(case when rn = 3 then cond_id end) cond_id3,
max(case when rn = 4 then cond_id end) cond_id4
from cte
group by id
SQL Fiddle Demo
Or you could look at Pivot:
select id, [1] cond_id1, [2] cond_id2, [3] cond_id3, [4] cond_id4
from
(select row_number() over (partition by id order by cond_id) rn, id, cond_id
from a) t
pivot
(
max(cond_id)
for rn in ([1], [2], [3], [4])
) p
More Fiddle

Another SQL 'Rows to Columns' Question

Here is my data:
ID
Model
Year
1
Civic
2008
1
Accord
2010
2
Mustang
2011
3
Tahoe
2011
I would like to get this result:
ID
Model1
Year1
Model2
Year2
1
Civic
2008
Accord
2010
2
Mustang
2011
3
Tahoe
2011
Up to 4 cars can be present under each ID and no more. I have spent a lot of time researching this but have not found a good solution that fits my example exactly. Perhaps because I don't know how exactly to word my search. Thanks...
You should use PIVOT tables. It's ugly, but it works:
if object_id('tempdb..#RepeatingGroup') is not null drop table #RepeatingGroup
select 1 as ID, 'Civic' as Model, '2008' as [Year] into #RepeatingGroup union all
select 1, 'Accord', '2010' union all
select 2, 'Mustang', '2011' union all
select 3, 'Tahoe', '2011'
if object_id('tempdb..#tmp') is not null drop table #tmp
select
ID,
Model,
Year,
row_number() over (partition by x.ID order by x.Model) as Ordinal
into
#tmp
from
#RepeatingGroup x
select
pvtMd.ID,
pvtMd.[1] as Model1,
pvtYr.[1] as Year1,
pvtMd.[2] as Model2,
pvtYr.[2] as Year2,
pvtMd.[3] as Model3,
pvtYr.[3] as Year3,
pvtMd.[4] as Model4,
pvtYr.[4] as Year4
from
(select ID, Model, Ordinal from #tmp t) t
pivot (
min(Model) for Ordinal in ([1], [2], [3], [4])
) as pvtMd,
(select ID, Year, Ordinal from #tmp t) t2
pivot (
min([Year]) for Ordinal in ([1], [2], [3], [4])
) as pvtYr
where
pvtMd.ID = pvtYr.ID
order by
1
There's not a really great way to do this in SQL. You might try and struggle with a pivot table, but each entity would need a sequence.
Your best bet would be to arrange it how you want in your output language which will have much better tools available for this sort of thing.
Add a column to each row called sequence (which has 1, 2, 3, and 4)
SELECT
id,
Max(case when seq = 1 then model end) as model1,
max(case when seq = 1 then year end) as year1,
Max(case when seq = 2 then model end) as model2,
max(case when seq = 2 then year end) as year2,
Max(case when seq = 3 then model end) as model3,
max(case when seq = 3 then year end) as year3,
Max(case when seq = 4 then model end) as model4,
max(case when seq = 4 then year end) as year4
group by id

Query i need like this

This is my query
SELECT CAL.CALENDAR_NAME,CAL.CALENDAR_ID,CALDAY.CALENDARDAY_DAYID
FROM lms_calendar AS CAL
LEFT JOIN LMS_CALENDARDAY AS CALDAY
ON CAL.CALENDAR_ID = CALDAY.CALENDARDAY_CALENDARID
and I get results like this
CALENDAR_NAME CALENDAR_ID CALENDARDAY_DAYID
-------------------------------------------------- ----------- -----------------
Test 1 1
Test 1 2
Test 1 3
Test 1 4
Test 1 6
But I need like this
calendar_name calendar_dayid calendar_dayid calendar_dayid calendar_dayid calendar_dayid
test 1 2 3 4
Here is a query that uses the PIVOT operator
SELECT calendar_name,
[1] AS calendar_dayid,
[2] AS calendar_dayid,
[3] AS calendar_dayid,
[4] AS calendar_dayid,
[5] AS calendar_dayid
FROM (
SELECT CAL.CALENDAR_NAME,CAL.CALENDAR_ID,CALDAY.CALENDARDAY_DAYID
FROM lms_calendar AS CAL
LEFT JOIN LMS_CALENDARDAY AS CALDAY
ON CAL.CALENDAR_ID = CALDAY.CALENDARDAY_CALENDARID
) AS src
PIVOT (
MAX(calendarday_dayid)
FOR calendarday_dayid IN ([1], [2], [3], [4], [5])
) AS pvt