PIVOT with Grand Total - sql-server

I'm working with PIVOT in SQL, something I've only started learning about this week. I'm trying to get a grand total for each Director as an added column at the side. I've looked online and seen its a popular query, but the ways of doing it seem really confusing and a little over-kill for what I need. Can someone show me how I'd add a grand total column at the end of each row that just adds up the monthly numbers for each Director?
SELECT *
FROM (
SELECT
DimDirectoR.Full_Name, YEAR(dim_Date.Date) as [year],left(datename(month,dim_Date.Date),3)as [month],
Fact_Stream.Device_Type_ID as Amount
FROM Fact_Stream INNER JOIN DimDirector ON Fact_Stream.Director_ID=DimDirector.Director_ID
INNER JOIN dim_Date
on Fact_Stream.Request_View_Date = dim_Date.ID
) as Directors
PIVOT
(
COUNT(Amount)
FOR [month] IN (jan, feb, mar, apr,
may, jun, jul, aug, sep, oct, nov, dec)
)AS DirectorsMonthlyStreams
ORDER BY [year], Full_Name
Current output:
I simply want to have a column which says 'Total' at the end of each row displaying the total amount for that directors year...
SQL Server 2012

You can just do a sum in the top level select statement:
SELECT *, jan+feb+mar+apr+may+jun+jul+aug+sep+oct+nov+dec Total
FROM (
SELECT
DimDirectoR.Full_Name, YEAR(dim_Date.Date) as [year],left(datename(month,dim_Date.Date),3)as [month],
Fact_Stream.Device_Type_ID as Amount
FROM Fact_Stream INNER JOIN DimDirector ON Fact_Stream.Director_ID=DimDirector.Director_ID
INNER JOIN dim_Date
on Fact_Stream.Request_View_Date = dim_Date.ID
) as Directors
PIVOT
(
COUNT(Amount)
FOR [month] IN (jan, feb, mar, apr,
may, jun, jul, aug, sep, oct, nov, dec)
)AS DirectorsMonthlyStreams
ORDER BY [year], Full_Name

You could do something like this as well:
SQL - Pivot with Grand Total Column and Row

Related

SQL, Is there a way allowing aggregation of data on multiple active periods based on start and end date fields?

I am still new to SQL and data manipulation and I have no idea if it is possible as I have not come across anything from searching that is the same. I am trying to aggregate data based on a active period between a start and end date. My current code that I have included should hopefully give an idea on the sort of aggregation I would like to perform.
I have tried to search for a way to do this in SQL or Powerbi but so far have come up short. Most of the examples I found are to perform calculations on a single column rather than the entire dataset. My original idea was to do a calculated column giving a list of months active but I failed to then aggregate on that enough to condense down the very large data set.
SELECT [Group ID], [User], [Location], [category], [Setup Date], [End Date], Count([Name]) AS 'Number of Names'
Avg([Duration in Weeks]) AS 'Avg duration',Avg([Days since last production]) AS 'Avg days since last production',Avg([Losses]) AS 'Avg losses',
ROUND(AVG([Number produced]/NULLIF([duration in weeks],0)),2) AS Productivity
FROM [Summary].[dbo].[Summary$]
Group BY [Group ID], [User], [Location], [Category], [Setup Date], [End Date]
For example if the set up date is "2017-01-09" and the end date is "2017-03-30" I would like to be able to aggregate this data in Jan 2018, Feb 2018 and Mar 2018 data all in the same table. I hope I have given enough information and explained clearly but please let me know if I need to provide anything else.
Small snippet of raw data
You can use a CTE to create your periods
DECLARE #yearfrom int = 2017, #yearto int = 2019
;WITH months AS (
SELECT 1 AS MonthNum, DATENAME(Month,DATEFROMPARTS(1,1,1)) AS MonthName
UNION ALL
SELECT MonthNum + 1, DATENAME(Month,DATEFROMPARTS(1, MonthNum + 1, 1)) AS MonthName
FROM months
WHERE MonthNum <= 11
),
years as (
SELECT #yearfrom as year
union all
select year + 1
from years where year < #yearto
),
dates as (
SELECT Year, MonthNum, MonthName, DATEFROMPARTS(Year, MonthNum, 1) AS DateStart, DATEADD(MONTH, 1, DATEFROMPARTS(Year, MonthNum, 1)) AS DateEnd
FROM years
CROSS JOIN months
)
SELECT d.year, d.MonthNum, d.MonthName, [Group ID], [User], [Location], [category], Count([Name]) AS 'Number of Names'
Avg([Duration in Weeks]) AS 'Avg duration',Avg([Days since last production]) AS 'Avg days since last production',Avg([Losses]) AS 'Avg losses',
ROUND(AVG([Number produced]/NULLIF([duration in weeks],0)),2) AS Productivity
FROM
DATES d
LEFT JOIN [Summary].[dbo].[Summary$] ON
([Setup Date] >= d.DateStart AND [Setup Date] < d.DateEnd)
OR ([End Date] >= d.DateStart AND [End Date] < d.DateEnd)
Group BY d.year, d.monthnum, d.monthname, [Group ID], [User], [Location], [Category]

how to Replace null with zero in pivot SQL query

I am trying to fetch Year wise and month wise sales like
select * from
(
select year(InvDt) as [Year], left(datename(Month,InvDt), 3) as [Month],
InvAmnt as Amount
from tblInvoice where TenantId =-xxxxxxxx
) as Inv
pivot
(sum(Amount) for [Month] in(Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep,
Oct, Nov, [Dec])) as pvt
But here so many null is coming so I want to replace these null with zero.
please help me.
create table tblInvoice (InvDt date, TenantId int, InvAmnt numeric)
insert into tblInvoice values ('20180601',1,1),('20180601',1,1),('20180601',1,1),('20180501',1,1), ('20180401',1,1)
SELECT Year
, COALESCE(Jan ,0) Jan
, COALESCE(Feb ,0) Feb
, COALESCE(Mar ,0) Mar
, COALESCE(Apr ,0) Apr
, COALESCE(May ,0) May
, COALESCE(Jun ,0) Jun
, COALESCE(Jul ,0) Jul
, COALESCE(Aug ,0) Aug
, COALESCE(Sep ,0) Sep
, COALESCE(Oct ,0) Oct
, COALESCE(Nov ,0) Nov
, COALESCE(Dec ,0) Dec
FROM ( SELECT YEAR(InvDt) AS Year
, LEFT(DATENAME(MONTH, InvDt), 3) AS Month
, InvAmnt AS Amount
FROM tblInvoice
WHERE TenantId = 1) AS Inv
PIVOT ( SUM(Amount)
FOR Month IN (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)) AS pvt;
So Answer is this but it's too long, something we must have another solution but I am not getting right now
select [Year], isnull(Jan, 0) as Jan,isnull(Feb,0) as Feb, isnull(Mar, 0) as
Mar, isnull(Apr, 0) as Apr, isnull(May,0) as May,
isnull(Jun,0) as Jun, isnull(Jul,0) as Jul, isnull(Aug,0) as Aug,
isnull(Sep,0) as Sep,isnull(Oct,0) as Oct, isnull(Nov,0) as Nov,
isnull([Dec],0) as Dec
from
(
select year(InvDt) as [Year], left(datename(Month,InvDt), 3) as [Month],
InvAmnt as Amount
from tblInvoice where TenantId =-xxxxxxx
) as Inv
pivot
(sum(Amount) for [Month] in(Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep,
Oct, Nov, [Dec])) as pvt
Thanks Mazhar by your solution my mind was opened and works this.
Just add a coalesce statement in a subquery to replace nulls on zero:
select * from
(
select year(InvDt) as [Year], left(datename(Month,InvDt), 3) as [Month],
coalesce(InvAmnt, 0) as Amount
from tblInvoice where TenantId =-xxxxxxxx
) as Inv
pivot
(sum(Amount) for [Month] in(Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep,
Oct, Nov, [Dec])) as pvt)

Merge two count datename queries

Query
SELECT
DATENAME(dw, O1.CreationDateTime) AS 'Day of the week',
DATENAME(hh, O1.CreationDateTime) AS hour,
COUNT(*) AS 'Nr. Orders'
FROM
Orders AS O1
WHERE
O1.CreationDateTime >= '2015-10-01'
GROUP BY
DATENAME(dw, O1.CreationDateTime), DATENAME(hh, O1.CreationDateTime)
ORDER BY
[Day of the week], CAST(DATENAME(hh, O1.CreationDateTime) as int)
Gives the total number of orders for each hour of each weekday
Result
Weekday Hour Nr.Orders
Friday 0 10
Friday 1 11
Friday 2 2
The goal is to get the AVERAGE amount of orders for each hour of the day. However some days have no orders those days should not be counted.
To solve this problem I wrote the select distinct query below.
Query
SELECT
DATENAME(dw,Formatteddate.Date) AS "Day of the week",
COUNT(*) AS "Weekdays in period"
FROM
(SELECT DISTINCT
((CONVERT (date,O2.CreationDateTime,112))) AS Date
FROM Orders AS O2
WHERE
O2.CreationDateTime >= '2015-10-01'
) AS Formatteddate
GROUP BY DATENAME(dw,Formatteddate.Dates)
It counts for example the number of fridays with orders like shown below.
Result
Day of the week | Week days in period (with order)
Friday 32
Monday 30
Saturday 21
Basically my questions is how two merge those two query's I used outer
apply as a workaround. However it is very slow due to the fact it
checks each row. I think I need to use the over clause but I have no
idea how with the datename function.
The end result should look like this to make counting the average easy.
Weekday Hour Nr.Orders Weekdays in period
Friday 0 10 32
Friday 1 11 32
Friday 2 2 32
Friday 3 2 32
You can join the two tables together on the day of the week:
SELECT t1.[Day of the week] AS Weekday,
t1.hour,
t1.[Nr. Orders],
COALESCE(t2.[Weekdays in period], 0)
FROM
(
SELECT DATENAME(dw, O1.CreationDateTime) AS 'Day of the week',
DATENAME(hh, O1.CreationDateTime) AS hour,
COUNT(*) AS 'Nr. Orders'
FROM Orders AS O1
WHERE O1.CreationDateTime >= '2015-10-01'
GROUP BY DATENAME(dw, O1.CreationDateTime),
DATENAME(hh, O1.CreationDateTime)
) t1
LEFT JOIN
(
SELECT DATENAME(dw,Formatteddate.Date) AS "Day of the week",
COUNT(*) AS "Weekdays in period"
FROM
(
SELECT DISTINCT CONVERT (date,O2.CreationDateTime, 112) AS Date
FROM Orders AS O2
WHERE O2.CreationDateTime >= '2015-10-01'
) AS Formatteddate
GROUP BY DATENAME(dw,Formatteddate.Dates)
) t2
ON t1.[Day of the week] = t2.[Day of the week]
Try joining on the day:
WITH DayO AS (
SELECT
DATENAME(dw,Formatteddate.Date) AS DayOfWeek,
COUNT(*) AS PeriodDays
FROM
(SELECT DISTINCT
((CONVERT (date,O2.CreationDateTime,112))) AS Date
FROM Orders AS O2
WHERE
O2.CreationDateTime >= '2015-10-01'
) AS Formatteddate
GROUP BY DATENAME(dw,Formatteddate.Dates)
)
, HourO AS (
SELECT
DATENAME(dw, O1.CreationDateTime) AS DayOfWeek,
DATENAME(hh, O1.CreationDateTime) AS OrdHour,
COUNT(*) AS NumOrd
FROM
Orders AS O1
WHERE
O1.CreationDateTime >= '2015-10-01'
GROUP BY
DATENAME(dw, O1.CreationDateTime), DATENAME(hh, O1.CreationDateTime)
ORDER BY
[DayOfWeek], CAST(DATENAME(hh, O1.CreationDateTime) as int)
)
SELECT ho.DayOfWeek as Weekday, ho.OrdHour as Hour, ho.NumOrd as [Nr. Orders], DayO.PeriodDays as [Weekdays in period]
from HourO ho
inner join DayO
on ho.DayOfWeek = DayO.DayOfWeek

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

SQL Server: Top 10 salespeople per week - and previous rankings

select *
from
(
select year,
week,
salesperson,
count(*) as transactions,
rank() over(partition by week order by count(*) desc) as ranking
from sales
where year = '2010',
group by year,
week,
salesperson
) temp
where ranking <= 10
The query returns a list of the top 10 salespeople (in terms of # of transactions) for each week of the year.
How can I go about adding columns to my results for:
Previous week's ranking for that
salesperson
Total weeks in the Top 10 this year
Consecutive weeks in the Top 10 (starting at week 1)
Consecutive weeks in the Top 10 (starting in previous year, if possible)
Can you give any general advice on how to go about these sorts of problems?
PS: Using SQL server 2008
Actually, I'm not convinced that Views are the best way to go. You can do this sort of logic in CTE's and combine the entire thing into a single query. For example, here is what I have for everything except the consecutive logic:
;With
SalesDateParts As
(
Select DatePart(wk, SaleDate) As WeekNum, DatePart(yy, SaleDate) As [Year], SalesPersonId
From #Sales
)
, SalesByWeek As
(
Select [Year], WeekNum, SalesPersonId, Count(*) As SaleCount
, RANK() OVER( PARTITION BY [Year], [WeekNum] ORDER BY Count(*) DESC ) As SaleRank
From SalesDateParts
Group By [Year], WeekNum, SalesPersonId
)
, PrevWeekTopSales As
(
Select [Year], [WeekNum], SalesPersonId, SaleCount
From SalesByWeek
Where [Year] = DatePart(yyyy, DateAdd(d, -7, CURRENT_TIMESTAMP))
And WeekNum = DatePart(wk, DateAdd(d, -7, CURRENT_TIMESTAMP))
)
, WeeksInTop10 As
(
Select SalesPersonId, Count(*) As Top10Count
From SalesByWeek
Where SaleRank <= 10
Group By SalesPersonId
)
Select *
From Salespersons
Left Join WeeksInTop10
On WeeksInTop10.SalesPersonId = SalesPersons.SalesPersonId
Left Join PrevWeekTopSales
On PrevWeekTopSales.SalesPersonId = SalesPersons.SalesPersonId
The logic for "consecutive" is probably going to require a calendar table which contains a value for every day along with columns for the given date's year and week.
My advice is to do the other queries separately in views and then join them in by saleperson (which I assume is key)
The logic is this query is nice and clean and easy to follow. Otherwise - I think the way to attack this would be to start writing TSQL functions to calculate the other values, but I think those functions will have the queries in them anyway.

Resources