SQL order pivot result by month - sql

I need to order the rows based on a month or order rather than alphabecially.
When I run the query below I get the following result
Col Mon Tue Wed Thu Fri
--- --- --- --- --- ---
Feb 1 2 3 4 5
Jan 2 3 4 5 6
Mar 3 4 5 6 7
How can I order it as follows:
Col Mon Tue Wed Thu Fri
--- --- --- --- --- ---
Jan 2 3 4 5 6
Feb 1 2 3 4 5
Mar 3 4 5 6 7
My query:
select *
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
col,
value
from DataTable
cross apply
(
values ('Jan', JData), ('Feb', FData), ('Mar', MData)
) c (col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv
At the moment I'm replacing the value's line as:
values ('1.Jan', JData), ('2.Feb', FData), ('3.Mar', MData)
I guess there is a better solution

Try this:
select * from DataTable
order by convert(datetime, '2012/'+ Col +'/01')
SQLFiddle

Thanks Wawrzyniec Sz., I figure it out the solution. Adding an additional column does the trick.
For months the code will be as follows:
select col, Mon, Tue, Wed, Thu, Fri
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
ord,
col,
value
from DataTable
cross apply
(
values ('1','Jan', AVol_Offered), ('2','Feb', OVol_Offered), ('3','Mar', AAHT)
) c (ord, col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv
order by ord
This would apply to other values, for example, if I want to maintain the order listed as C,A,B (which you can not order it asc or des) you will can also use the same code as follows:
select col, Mon, Tue, Wed, Thu, Fri
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
ord,
col,
value
from DataTable
cross apply
(
values ('1','C', AVol_Offered), ('2','A', OVol_Offered), ('3','B', AAHT)
) c (ord, col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv
order by ord
Thanks for the help/hints

Related

Sum of numbers in CTE

For this CTE show counts of working/not working days.
with a(id, MON, TUE, WED, THUR, FRI, SAT, SUN) as (values (1,0,0,1,1,1,0,0),(2,1,1,1,1,0,0,0))
select * from a
I got this result but I changed CTE.
My request:
with a(id, days) as (values (1,0),(1,0),(1,1),(1,1),(1,1),(1,0),(1,0),(2,1),(2,1),(2,1),(2,1),(2,0),(2,0),(2,0))
select id, 'Working' as day_type, sum(days) "COUNT" from a group by shop_id union select id, 'Non-working' as day_type, count(days) - sum(days) "COUNT" from a group by id order by id, day_type
If I understand your question, you want to be able to generate the output from your second query without altering the initial CTE a (which presumably represents an actual table or something).
There will be multiple ways of doing this, but one possibility is something like:
with a(shop_id, MON, TUE, WED, THUR, FRI, SAT, SUN) as (values (1,0,0,1,1,1,0,0),(2,1,1,1,1,0,0,0))
, b as (SELECT shop_id, MON + TUE + WED + THUR + FRI + SAT + SUN cnt FROM a)
SELECT shop_id, 'Working', cnt
FROM b
GROUP BY shop_id, cnt
UNION ALL
SELECT shop_id, 'Non-working', 7-cnt
FROM b
GROUP BY shop_id, cnt
Here, the CTE b contains the sum of working days for each shop_id. This is then unioned together with the non-working days (7-working). Using the second CTE isn't necessary, but lets us avoid repeating the bit that adds all the days together. If you prefer, you could do:
with a(shop_id, MON, TUE, WED, THUR, FRI, SAT, SUN) as (values (1,0,0,1,1,1,0,0),(2,1,1,1,1,0,0,0))
SELECT shop_id, 'Working', MON + TUE + WED + THUR + FRI + SAT + SUN cnt
FROM a
UNION ALL
SELECT shop_id, 'Non-working', 7-(MON + TUE + WED + THUR + FRI + SAT + SUN)
FROM a
Another options is to actually unpivot the table (convert the columns into rows) and then group by the shop_id and day_type. Something like:
with a(shop_id, MON, TUE, WED, THUR, FRI, SAT, SUN) as (values (1,0,0,1,1,1,0,0),(2,1,1,1,1,0,0,0))
,
b AS (SELECT shop_id, CASE P.w WHEN 0 THEN 'Non-working' ELSE 'Working' END day_type
FROM a, TABLE (VALUES(a.MON),
(a.TUE),
(a.WED),
(a.THUR),
(a.FRI),
(a.SAT),
(a.SUN)) AS P(w))
SELECT shop_id, day_type, count(*) cnt
FROM b
GROUP BY shop_id, day_type
In all cases above, you can use an ORDER BY to get the rows in the order you want them.
Here is a Fiddle of these working.

Sum Non Null Values Block in SQL

How to add Non Null Values block by block without any grouping criteria :
Example input :
Machine Value DateTime
a null 1 Dec 2021 8:34AM
a 2 1 Dec 2021 8:35AM
a 1 1 Dec 2021 9:34AM
a 3 1 Dec 2021 10:11AM
a null 1 Dec 2021 11:14AM
a null 1 Dec 2021 11:16AM
a 5 1 Dec 2021 11:58AM
a 6 1 Dec 2021 11:59AM
Example output :
Machine Value DateTime SumValue
a null 1 Dec 2021 8:34AM
a 2 1 Dec 2021 8:35AM
a 1 1 Dec 2021 9:34AM
a 3 1 Dec 2021 10:11AM 6
a null 1 Dec 2021 11:14AM
a null 1 Dec 2021 11:16AM
a 5 1 Dec 2021 11:58AM
a 6 1 Dec 2021 11:59AM 11
I don't have any other grouping criteria other than device column , but I want sum block wise
You need to define the groups and use windowed SUM():
Table:
SELECT *
INTO Data
FROM (VALUES
('2021-12-12T09:00:01', 'a', null),
('2021-12-12T09:00:02', 'a', 2),
('2021-12-12T09:00:03', 'a', 1),
('2021-12-12T09:00:04', 'a', 3),
('2021-12-12T09:00:05', 'a', null),
('2021-12-12T09:00:06', 'a', null),
('2021-12-12T09:00:07', 'a', 5),
('2021-12-12T09:00:08', 'a', 6)
) v (Date, Machine, Value)
Statement:
SELECT
Date, Machine, Value,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Machine, GroupNumber ORDER BY Date DESC) = 1
THEN SUM(Value) OVER (PARTITION BY Machine, GroupNumber ORDER BY (SELECT NULL))
END AS SumValue
FROM (
SELECT
*,
SUM(CASE WHEN Value IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY Machine ORDER BY Date) AS GroupNumber
FROM Data
) t
ORDER BY Machine, Date
Result:
Date Machine Value SumValue
2021-12-12T09:00:01 a
2021-12-12T09:00:02 a 2
2021-12-12T09:00:03 a 1
2021-12-12T09:00:04 a 3 6
2021-12-12T09:00:05 a
2021-12-12T09:00:06 a
2021-12-12T09:00:07 a 5
2021-12-12T09:00:08 a 6 11

SQL - SHOW ALL DATES between two dates

I have following data saved as dates in my [OccuredAtUtc] that look like this:
-- Spoiler ALERT: "2017-04-26" and "2017-04-29" are missing.
Original dates in [OccuredAtUtc]:
2017-04-24 12:16:58.5080000
2017-04-24 18:11:53.3090000
2017-04-25 18:34:18.3090000
2017-04-27 20:42:28.8570000
2017-04-28 21:10:36.7070000
2016-04-28 10:37:57.5970000
2016-04-30 10:38:55.7010000
2016-04-30 10:48:19.0390000
2016-04-31 10:48:19.2990000
.
.
.
And I have this code that returns correctly data from two intervals (previous week).
SELECT
[MessageType].[Name] AS [Channel],
CONVERT(VARCHAR(11), [OccuredAtUtc], 106) AS [Time],
COUNT(*) AS [Count]
FROM #table1
INNER JOIN #table2 ON ... = ...
WHERE ( [OccuredAtUtc] > '2017-04-24'
AND [OccuredAtUtc] < '2017-04-30' )
GROUP BY (CONVERT(VARCHAR(11), [OccuredAtUtc], 106)),
[MessageType].[Name]
ORDER BY [Time] ASC
But the output won't show a row of the "26 Apr 2017" and "29 Apr 2017" because there are not records on these days in my DB.
OLD OUTPUT : with missing 26th & 29th Apr.
[Channel] [Time] [Count]
------------------------------------
FTP 24 Apr 2017 7
HTTP 24 Apr 2017 9
FTP 25 Apr 2017 6
HTTP 25 Apr 2017 2
------MISSING 26 Apr--------
FTP 27 Apr 2017 56
HTTP 27 Apr 2017 12
FTP 28 Apr 2017 5
------MISSING 29 Apr--------
HTTP 28 Apr 2017 17
FTP 30 Apr 2017 156
HTTP 30 Apr 2017 19
I would like to show rows WITH THE MISSING DATE even if there was not an incident saved on this day...
So the new OUTPUT should look like this.
WANTED OUTPUT :
[Channel] [Time] [Count]
------------------------------------
FTP 24 Apr 2017 7
HTTP 24 Apr 2017 9
FTP 25 Apr 2017 6
HTTP 25 Apr 2017 2
0 26 Apr 2017 0 -- here we go
FTP 27 Apr 2017 56
HTTP 27 Apr 2017 12
FTP 28 Apr 2017 5
HTTP 28 Apr 2017 17
0 29 Apr 2017 0 -- here we go
FTP 30 Apr 2017 156
HTTP 30 Apr 2017 19
I know there are answered question like mine and I was trying to remake my code but I failed.
( SHOW ALL Dates data between two dates; if no row exists for particular date then show zero in all columns )
( How to generate all dates between two dates )
Similar to #DhruvJoshi's answer but using a recursive CTE to generate the dates instead:
DECLARE #MinDate DATE = '20170424',
#MaxDate DATE = '20170430';
WITH allDates AS
(
SELECT #MinDate AS dates
UNION ALL
SELECT DATEADD(DAY, 1, ad.[dates] )
FROM allDates AS ad
WHERE ad.[dates] < #MaxDate
)
SELECT
ISNULL([MessageType].[Name],0) AS [Channel],
dates AS [Time],
COUNT([MessageType].[Name]) AS [Count]
FROM
(
SELECT dates
FROM allDates
) AS T
LEFT JOIN
#table1 ON T.dates=CONVERT(VARCHAR(11), #table1.[OccuredAtUtc], 106)
LEFT JOIN #table2 ON ... = ...
GROUP BY dates,
[MessageType].[Name]
ORDER BY [Time] ASC
You can use something like a Tally table to generate all dates between certain time interval.
SELECT
ISNULL([MessageType].[Name],0) AS [Channel],
dates AS [Time],
COUNT([MessageType].[Name]) AS [Count]
FROM
(
SELECT
TOP (DATEDIFF(d,'2017-04-24','2017-04-30')+1)
DATEADD(d,ROW_NUMBER() OVER( ORDER BY (SELECT 1))-1,'2017-04-24') dates
FROM sys.objects a CROSS JOIN sys.objects b
)T
LEFT JOIN
#table1 ON T.dates=CONVERT(VARCHAR(11), #table1.[OccuredAtUtc], 106)
LEFT JOIN #table2 ON ... = ...
AND ( [OccuredAtUtc] > '2017-04-24'
AND [OccuredAtUtc] < '2017-04-30' )
GROUP BY dates,
[MessageType].[Name]
ORDER BY [Time] ASC
For more explanation on Tally tables please read this article
declare #t table ( i int identity , b bit, d as dateadd (dd, i - 1, 0 ))
insert into #t (b)
VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0)
insert into #t (b)
select 0
from #t t1
cross apply ( select b from #t) as t2
cross apply ( select b from #t) as t3
cross apply ( select b from #t) as t4
cross apply ( select b from #t) as t5
select t.d, isnull(y.channel,0), count(y.[date])
from #t t
left join yourtable y on y.[date] = t.d
where d between getdate() - 30 and getdate()
group by t.d, isnull(y.channel,0)

Pivot Multiple Columns into Rows Without Aggregation in SQL [duplicate]

I need to do the following transpose in MS SQL
from:
Day A B
---------
Mon 1 2
Tue 3 4
Wed 5 6
Thu 7 8
Fri 9 0
To the following:
Value Mon Tue Wed Thu Fri
--------------------------
A 1 3 5 7 9
B 2 4 6 8 0
I understand how to do it with PIVOT when there is only one column (A) but I can not figure out how to do it when there are multiple columns to transpose (A,B,...)
Example code to be transposed:
select LEFT(datename(dw,datetime),3) as DateWeek,
sum(ACalls) as A,
Sum(BCalls) as B
from DataTable
group by LEFT(datename(dw,datetime),3)
Table Structure:
Column DataType
DateTime Datetime
ACalls int
BCalls int
Any help will be much appreciated.
In order to transpose the data into the result that you want, you will need to use both the UNPIVOT and the PIVOT functions.
The UNPIVOT function takes the A and B columns and converts the results into rows. Then you will use the PIVOT function to transform the day values into columns:
select *
from
(
select day, col, value
from yourtable
unpivot
(
value
for col in (A, B)
) unpiv
) src
pivot
(
max(value)
for day in (Mon, Tue, Wed, Thu, Fri)
) piv
See SQL Fiddle with Demo.
If you are using SQL Server 2008+, then you can use CROSS APPLY with VALUES to unpivot the data. You code would be changed to the following:
select *
from
(
select day, col, value
from yourtable
cross apply
(
values ('A', A),('B', B)
) c (col, value)
) src
pivot
(
max(value)
for day in (Mon, Tue, Wed, Thu, Fri)
) piv
See SQL Fiddle with Demo.
Edit #1, applying your current query into the above solution you will use something similar to this:
select *
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
col,
value
from DataTable
cross apply
(
values ('A', ACalls), ('B', BCalls)
) c (col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv

SQL transpose full table

I need to do the following transpose in MS SQL
from:
Day A B
---------
Mon 1 2
Tue 3 4
Wed 5 6
Thu 7 8
Fri 9 0
To the following:
Value Mon Tue Wed Thu Fri
--------------------------
A 1 3 5 7 9
B 2 4 6 8 0
I understand how to do it with PIVOT when there is only one column (A) but I can not figure out how to do it when there are multiple columns to transpose (A,B,...)
Example code to be transposed:
select LEFT(datename(dw,datetime),3) as DateWeek,
sum(ACalls) as A,
Sum(BCalls) as B
from DataTable
group by LEFT(datename(dw,datetime),3)
Table Structure:
Column DataType
DateTime Datetime
ACalls int
BCalls int
Any help will be much appreciated.
In order to transpose the data into the result that you want, you will need to use both the UNPIVOT and the PIVOT functions.
The UNPIVOT function takes the A and B columns and converts the results into rows. Then you will use the PIVOT function to transform the day values into columns:
select *
from
(
select day, col, value
from yourtable
unpivot
(
value
for col in (A, B)
) unpiv
) src
pivot
(
max(value)
for day in (Mon, Tue, Wed, Thu, Fri)
) piv
See SQL Fiddle with Demo.
If you are using SQL Server 2008+, then you can use CROSS APPLY with VALUES to unpivot the data. You code would be changed to the following:
select *
from
(
select day, col, value
from yourtable
cross apply
(
values ('A', A),('B', B)
) c (col, value)
) src
pivot
(
max(value)
for day in (Mon, Tue, Wed, Thu, Fri)
) piv
See SQL Fiddle with Demo.
Edit #1, applying your current query into the above solution you will use something similar to this:
select *
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
col,
value
from DataTable
cross apply
(
values ('A', ACalls), ('B', BCalls)
) c (col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv