SUM(Value) Group By After PIVOT - sql-server

I have the following query for Pivot, that works nicely.
WITH PivotData AS
(
SELECT fechaHora, idWatimetro, semana, hora, minuto,dia,mes ,ano,valor
FROM E_Registros_Watimetros
)
SELECT *
FROM PivotData
PIVOT(SUM(valor) FOR idWatimetro IN (
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
) ) AS P
order by fechaHora, semana,dia,mes;
But now, I must be able to get Sum(valor) for every month or days or weeks or Year, but I´m trying without success, any help will be nice
Thank you for your time.
After suggestion of Pieter Geerkens, What I want to do is like this
The Sum(1),Sum(2),Sum(3)... Sum(10) for every Month.
The Month is an example, becuase this parameter can be, Year , day or weeks
Sorry for not to be very clear, my english is not very good.

You can do this with your query but you need to alter pivot source Query by remove the columns which not needed in final result. Try this.
WITH PivotData
AS (SELECT idWatimetro,
mes,
ano,
valor
FROM E_Registros_Watimetros)
SELECT *
FROM PivotData
PIVOT(Sum(valor)
FOR idWatimetro IN ( [1],
[2],
[3],
[4],
[5],
[6],
[7],
[8],
[9],
[10] ) ) AS P

You can add a second CTE to do the pivot and use the final SELECT to GROUP BY:
WITH PivotData AS
(
SELECT fechaHora, idWatimetro, semana, hora, minuto,dia,mes ,ano,valor
FROM E_Registros_Watimetros where ano = 2012
), tempData AS (
SELECT *
FROM PivotData
PIVOT(SUM(valor) FOR idWatimetro IN ([1],[2],[3],[4],[5]) ) AS P
)
SELECT MONTH(fechaHora) AS [Month],
SUM([1]) AS KW1,
SUM([2]) AS KW2,
SUM([3]) AS KW3
FROM tempData
GROUP BY MONTH(fechaHora)
Edit: The above query works on my SQL Server 2008 box.

Related

Count number of occurrences per hour between two dates SQL Server

I've created a temp table #MB that has a record ID (119 rows), start and end date (partial screenshot of the list is below):
I'm trying to get a count of occurrences that happened each hour for each record ID during the start and end date (or number of occurrences each hour when ID was active between two dates).
I've used this code:
SELECT *
FROM
(SELECT
ISNULL(CAST(part AS VARCHAR(5)), 'Total') AS part,
COUNT(*) AS part_count
FROM
(SELECT DATEPART([HOUR], [Start]) AS part
FROM #MB) grp
GROUP BY
GROUPING SETS((part),())
) pre
PIVOT
(MAX(part_count)
FOR part IN ([0], [1], [2], [3], [4], [5], [6], [7], [8],
[9], [10], [11], [12], [13], [14], [15], [16],
[17], [18], [19], [20], [21], [22], [23], Total)
) pvt;
but it counts only records based on the start date (don't count each hour between two dates) and I stuck on how to generate occurrences per hour for each ID between two dates that I can later use to pre-aggregate and pivot.
first, you need to generate the list of rows for each hour
here i am using a recursive cte query to do it
; with MB as
(
select ID, [Start], [End], [Date] = [Start]
from #MB
union all
select ID, [Start], [End], [Date] = dateadd(hour, 1, convert(date, c.[Date]))
from MB c
where dateadd(hour, 1, c.[Date]) < [End]
)
select *
from MB
so in your pivot query , just change to this
; with MB as
(
select ID, [Start], [End], [Date] = [Start]
from #MB
union all
select ID, [Start], [End], [Date] = DATEADD(HH,DATEPART(HH,[Start]),CAST(CAST([Start] AS DATE) AS DATETIME))
from MB c
where dateadd(hour, 1, c.[Date]) < [End]
)
SELECT *
FROM (
SELECT ISNULL(CAST(part AS VARCHAR(5)), 'Total') AS part,
COUNT(*) AS part_count
FROM (
SELECT DATEPART([HOUR], [Date]) AS part
FROM MB -- changed to the cte
) grp
GROUP BY
GROUPING SETS((part),())
) pre
PIVOT (MAX(part_count) FOR part IN (
[0],[1],[2],[3],[4],[5],[6],[7],[8],
[9],[10],[11],[12],[13],[14],[15],[16],
[17],[18],[19],[20],[21],[22],[23], Total)) pvt;

Error converting data type in a PIVOT query

I want to convert the below data into pivot in SQL. (displaying "total" values for corresponding column "weeks" and corresponding rows "names") And while running the sp, it throws exception:
Error converting data type nvarchar to datetime.
And
The incorrect value "#Week1" is supplied in the PIVOT operator.
Click here to see data
And my query is:
[Name] [nvarchar](255) NULL,
[Weeks] [DATETIME] NULL,
[total] [float] NULL
#Week1 = '12/6/2015'
#Week2 = '12/13/2015'
#Week3 = '12/20/2015'
#Week4 = '12/27/2015'
SELECT Name, #Week1 AS Week1, #Week2 AS Week2, #Week3 AS Week3, #Week4 AS Week4
INTO #TempData
FROM
(
SELECT Name, Weeks, total FROM #TempData2
) Data
PIVOT
(
SUM([total])
FOR [Weeks] IN ([#Week1],[#Week2],[#Week3],[#Week4])
) Piv
I provided datatype as datetime for all columns, but it throws nvarchar to datetime error. I have tried with some solutions but its not working.
Thanks for your help...
Using variables as column place holders in a PIVOT query doesn't work. I can produce the most of the result you are after using the following SQL query:
SELECT
Name,
[2015-12-06 00:00:00] AS '12/6/2015',
[2015-12-13 00:00:00] AS '12/13/2015',
[2015-12-20 00:00:00] AS '12/20/2015',
[2015-12-27 00:00:00] AS '12/27/2015'
FROM
(
SELECT Name, Weeks, total
FROM #TempData
) Data
PIVOT
(
SUM([total])
FOR [Weeks] IN ([2015-12-06 00:00:00], [2015-12-13 00:00:00], [2015-12-20 00:00:00],[2015-12-27 00:00:00])
) Piv
Produces the output shown below, but as you'll see the Total column isn't present.
In order to add the Grand Total column, you'll need to create a derived table that sums the totals and then join to it. Something like:
SELECT T1.*, T2.GrandTotal
FROM
(
SELECT
Name,
[2015-12-06 00:00:00] AS '12/6/2015',
[2015-12-13 00:00:00] AS '12/13/2015',
[2015-12-20 00:00:00] AS '12/20/2015',
[2015-12-27 00:00:00] AS '12/27/2015'
FROM
(
SELECT Name, Weeks, total FROM #TempData
) Data
PIVOT
(
SUM([total])
FOR [Weeks] IN
(
[2015-12-06 00:00:00], [2015-12-13 00:00:00],
[2015-12-20 00:00:00], [2015-12-27 00:00:00]
)
) Piv
) T1
JOIN
(
SELECT Name, SUM(Total) AS GrandTotal
FROM #TempData
WHERE Weeks IN
(
'2015-12-06 00:00:00',
'2015-12-13 00:00:00',
'2015-12-20 00:00:00',
'2015-12-27 00:00:00'
)
GROUP BY Name
) AS T2 ON T2.Name = T1.Name
Result:
You could generalize the SQL so that you don't have to keep typing the dates in many times. In the SQL command below I've assumed that you want adjacent weeks as in your example data:
DECLARE #StartDate datetime = '2015-12-06 00:00:00';
DECLARE #StartWeek int;
SELECT #StartWeek = DATEPART(WEEK, #StartDate);
SELECT Name, [0], [1], [2], [3]
FROM
(
SELECT
Name,
DATEPART(WEEK, Weeks) - #StartWeek AS WeekNumber,
Total
FROM #TempData
WHERE DATEPART(WEEK, Weeks) BETWEEN #StartWeek AND (#StartWeek + 3)
) Data
PIVOT
(
SUM([total])
FOR [WeekNumber] IN ([0], [1], [2], [3])
) Piv
This produces the following result. Although you have lost the column headers as dates, you gain by being able to vary the date by changing a single variable.

Transpose of dynamic data

I'm trying to extract the number of sales operations for every month for a number of variable sale centers.
Using the following TSQL...
;WITH Months(m) AS
(
SELECT 1 m
UNION ALL
SELECT m+1 FROM Months WHERE m < 12
)
SELECT t.Center,m Month, t.Sales FROM Months
CROSS APPLY
(
SELECT C.Center, COUNT(1) Sales FROM Operations C
LEFT JOIN Centers A ON A.Code=C.Center
WHERE Date BETWEEN '01/'+ CONVERT(VARCHAR(2),Months.m) + '/2013' AND DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,'01/'+ CONVERT(VARCHAR(2),Months.m) + '/2013')+1,0))
GROUP BY C.Center
) t
So I get the following output:
Center Month Sales
-----------------------
A 1 20
B 1 30
A 2 25
B 2 30
....
And what I want to end with is:
Center 1 2 ...
----------------------
A 20 25 ...
B 30 30 ...
I'm studying pivot with xmlpath, but It's so complicated that I can't make it work. Anyone has a solution?
Maybe I am missing something with your question but since there are only 12 months in the year, there shouldn't be a reason to use dynamic SQL as you will only ever have 12 columns.
This could easily be accomplished using the following query:
select center,
[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]
from
(
select o.center, month(date) month
from operations o
inner join centers c
on o.center = c.code
where c.date >= '2013-01-01'
and c.date <= '2013-12-31'
) d
pivot
(
count(month)
for month in ([1], [2], [3], [4], [5], [6], [7], [8], [9],
[10], [11], [12])
) piv
See SQL Fiddle with Demo.
If you want to do this dynamically, then you could use the following:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#startdate datetime,
#enddate datetime
set #startdate = '2013-01-01'
set #enddate = '2013-12-31'
;WITH Months(m) AS
(
SELECT 1 m
UNION ALL
SELECT m+1 FROM Months WHERE m < 12
)
select #cols = STUFF((SELECT ',' + QUOTENAME(m)
from Months
group by m
order by m
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT center, ' + #cols + '
from
(
select o.center, month(date) month
from operations o
inner join centers c
on o.center = c.code
where c.date >= '''+convert(varchar(10), #startdate, 120)+'''
and c.date <= '''+convert(varchar(10), #enddate, 120)+'''
) x
pivot
(
count(month)
for month in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo
Don't do it. Why fight Sql Server into something it wasn't meant to do ?
Let your query return data vertically, just like you have now.
Transpose it on the application level.
It will be much easier to write, debug, maintain, and handle special business rules which will undoubtedly pop up.
Also, it will make it easier to paste data into Excel for troubleshooting and pivoting.
I finally implemented the first suggestion of BlueFeet, with slight changes:
;WITH Months(m) AS
(
SELECT 1 m
UNION ALL
SELECT m+1 FROM Months WHERE m < 12
)
SELECT * FROM Months
CROSS APPLY
(
SELECT C.Center, COUNT(1) Sales FROM Operations C
LEFT JOIN Centers A ON A.Code=C.Center
WHERE Date BETWEEN '01/'+ CONVERT(VARCHAR(2),Months.m) + '/2013' AND DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,'01/'+ CONVERT(VARCHAR(2),Months.m) + '/2013')+1,0))
GROUP BY C.Center
) t
pivot
(
max(Sales)
for m in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
) as PIV
What I was looking for it's not to write the month numbers on the pivot statement, so I could only return a quarter, for example. But that makes the work required.
Thanks.

TSQL - Unpivot multiple columns

How can I unpivot multiple columns in "one"?
Right now I have an unpivot for each column but this creates a lot of empty rows.
See the screenshot please.
At the top you see the input data. At the moment I'm at the table in the middle with this code:
SELECT [ID], [RowNumber], [Year], [Sales] FROM (
SELECT ID, RowNumber, [Sales 2013] as [2013], [Sales 2014] as [2014]
FROM mytable) p UNPIVOT (
[Sales] FOR [Year] IN ([2013], [2014]) )AS unpvt ;
But I think it would be much better to get to the bottom table structure since the actual data contains more columns and more years to deal with.
Here's a Fiddle with the sample data.
Hope you can show me a way to get there.
Thank you.
SELECT [ID],
[RowNumber],
[Year],
Sales,
Budget
FROM mytable
CROSS APPLY (VALUES (2013, [Sales 2013], [Budget 2013]),
(2014, [Sales 2014], [Budget 2014]) )
V([Year], Sales, Budget)
SQL Fiddle
One approach is to repivot after unpivoting - like so:
select [Id], [Year], [Sales], [Budget], [Actual] from
(SELECT [Id],
Left([Colhead], charindex(' ',[Colhead])-1) [Category],
Right([Colhead], len([Colhead])-charindex(' ',[Colhead])) [Year],
[Figures]
FROM (SELECT * FROM mytable) p
UNPIVOT ([Figures] FOR [Colhead] IN
([Sales 2013],[Sales 2014],[Budget 2013],[Budget 2014],[Actual 2013],[Actual 2014])
)
AS unpvt) as u
pivot
(max([Figures]) for [Category] in ([Sales], [Budget], [Actual])) as p
SQLFiddle here.

MDX query for average of sums by month and year

I'm stuck on a MDX query... I have a table which looks like:
Date Client Amount
---------------------------------
2010-01-01 1 1000
2010-01-01 2 500
2010-01-02 1 1100
... ... ...
2011-09-30 3 2000
Basically, I'd like to have a MDX equivalent of the following T-SQL:
SELECT [Year], [Month], AVG(DailyTotal)
FROM (
SELECT [Year], [Month], [Day], SUM(Amount) as DailyTotal
FROM Table
GROUP BY [Year], [Month], [Day]
)
GROUP BY [Year], [Month]
I'm trying to get as result like
Jan ... Sept
2010 AVG ... AVG
2011 AVG ... AVG
Thanks in advance
I have a query using Adventure Works:
with member [measures].[Average] as Avg({Descendants([Date].[Calendar].CurrentMember, [Date].[Calendar].[Date])*[Date].[Month of Year].CurrentMember}, [Measures].[Internet Sales Amount])
select
{{[Date].[Month of Year].[Month of Year]}*{[Measures].[Internet Sales Amount], [measures].[Average]}} on columns,
{[Date].[Calendar].[Calendar Year]} on rows
From [Adventure Works]
In this query I'm using 2 hierarchies ([Date].[Calendar] and [Date].[Month of Year]). Maybe, in your case, you could need do something else if your time dimension have others hierarchies.
select *
from
(
select [Year], [Month], avg(DailyTotal) as AVERAGE
from (
select
[Year] = year([date]),
[Month] = month([date]),
[day] = day([date]) ,
[DailyTotal] = sum([Amount])
from [Table]
group by year(date), month(date), day(date)
) Q
group by [Year], [Month]
) K
pivot (max(AVERAGE) for K.[Month] in ([1],[2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])) as S

Resources