One of our managers has requested a report with a specific format with employee names as rows, Months of the year as columns, and average revenue per month per employee populating the cells. The dates will ultimately be replaced with #startdate and #enddate variables, but for the sake of building and testing I have hard coded one calendar year. To accomplish this request I have created a PIVOT query, however my result set is incorrect and I seldom use PIVOT queries, so I am not knowledgeable enough to understand the problem, and I am hoping for some help from someone who uses them often.
First, here is my query that pulls the correct result set without the requested formatting:
select d.Name, AVG(gross), s.systemmonth, s.SYSTEMYEAR
from desk d, PAYMENTS s
where d.code = s.DESK
and s.ENTERED between '2017-08-01' and '2017-09-30'
group by name, s.SYSTEMMONTH, s.SYSTEMYEAR
order by name
A few rows of the result set:
Name (No column name) systemmonth SYSTEMYEAR
employee 1 221.5737 8 2017
employee 1 181.2476 9 2017
employee 2 161.62 9 2017
employee 2 321.9311 8 2017
employee 3 249.2245 9 2017
employee 4 328.1208 8 2017
employee 4 198.6748 9 2017
employee 5 76.4833 8 2017
employee 5 96.6896 9 2017
Now for my PIVOT query:
select name, [1] AS January, [2] AS February, [3] AS March, [4] AS April,
[5] AS May, [6] AS June, [7] AS July, [8] AS August, [9] AS September, [10]
AS October, [11] AS November, [12] AS December
from
(select d.Name, s.gross, s.systemmonth, s.SYSTEMYEAR
from desk d, PAYMENTS s
where d.code = s.DESK
and s.ENTERED between '2017-01-01' and '2017-12-31'
group by name AS Employee, s.gross, s.SYSTEMMONTH, s.SYSTEMYEAR) a
PIVOT
(AVG(gross)
FOR systemmonth IN
([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) AS pvt
order by name
The accompanying results, the averages are all incorrect (the NULL is correct however). I've just included 2 months for ease of reading and comparison:
Employee August September
employee 1 279.501 245.478
employee 2 544.9648 235.9713
employee 3 NULL 312.5366
employee 4 403.1505 273.9044
employee 5 129.8883 239.8701
Can anyone tell me why the averages in the PIVOT are not calculating correctly? Thanks for the assistance.
Try removing this line from your pivot query. You are grouping for no reason it seems.
group by name AS Employee, s.gross, s.SYSTEMMONTH, s.SYSTEMYEAR
You could also take your first query and pivot on that and your pivot aggregate would become MAX or MIN at that point because you will only ever have 1 row in the dataset for each employee/month combination.
Related
Select
Datename(Month,[date]) as Month, [Name],
count(Name) Total,
'$' + Cast(Sum(Total_Tax_Exclusive_Price) as varchar(15)) Gross_Revenue
From
dbo.Receipts
Where
[name] Like '%coffee Cake%'
and [date] between '2022-09-15' and '2022-12-20'
Group By
Date, Total_Tax_Exclusive_Price, Name
Order by
month Desc, Total desc
The above code displays what I want however, I'd like to pivot that and don't know how to do that. I can do a simple pivot but my formulas aren't there.
I want to sum the counts and and dollar values for the months in pivot. My initial query may not be set up right to do this. I have included a picture of how it looks.
Working Query Image
I've tried the pivot function but it's not returning any results
I want it to look like this example
Instead of vertical I want it to look like this example with coffee cake in left column
| September | October | November | December
| 35 | 25 | 18 | 36
| 112.56 | 110.54 | 100.34 | 126.39
Select
[name] Like '%coffee Cake%', September, October, November, December
from [dbo].[Receipts]
Pivot
)
sum(count(Name)Total
for Month
IN ([September], [October], [November], [December])
)
AS Pvt_Table
returns error
Msg 156, Level 15, State 1, Line 8
Incorrect syntax near the keyword 'Like'.
Select * From (
Select DATENAME(Month, [Date])Month, [Name]
From [dbo].[Receipts]
Where [Name] Like 'Coffee Cake%'
and [date] Between '2022-09-01'and '2022-12-25'
)t
Pivot(
Count(Name)
For Month
IN ([September],[October],[November], [December])
)
AS Pvt_Table
This gets me pretty close but not quite there yet
It returns
|september|October|November|December|
| 150 | 160 | 151 | 119 |
If the months can be fixed or permanent, I hope this helps.
SELECT Name,
SUM([1]) AS Jan,
SUM([010]) as QtyJan,
SUM([2]) AS Feb,
SUM([20]) as QtyFeb,
SUM([3]) AS Mar,
SUM([30]) as QtyMar,
SUM([4]) AS Apr,
SUM([40]) as QtyApr,
SUM([5]) AS May,
SUM([50]) as QtyMay,
SUM([6]) AS Jun,
SUM([60]) as QtyJun,
SUM([7]) AS Jul,
SUM([70]) as QtyJul,
SUM([8]) AS Aug,
SUM([80]) as QtyAug,
SUM([9]) AS Sep,
SUM([90]) as QtySep,
SUM([10]) AS Oct,
SUM([100]) as QtyOct,
SUM([11]) AS Nov,
SUM([110]) as QtyNov,
SUM([12]) AS Dec,
SUM([120]) as QtyDec
FROM ( SELECT *
FROM ( SELECT Name,
MONTH(Date) as Month,
MONTH(Date)*10 as Month10,
SUM(Tax_Exclusive_Price) Total_Tax_Exclusive_Price,
COUNT(name) as Qty
FROM Receipts GROUP BY Name, MONTH(Date) ) as Tab1
PIVOT ( SUM(Total_Tax_Exclusive_Price) FOR Month IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]) ) TotalPrice
PIVOT ( SUM(Qty) FOR Month10 IN ([010], [20], [30], [40], [50], [60], [70], [80], [90], [100], [110], [120]) ) TotalQuantity
) Tab2
GROUP BY Name
Using MS SQL Server 2019
I have a set of recurring donation records. Each have a First Gift Date and a Last Gift Date associated with them. I need to add a GroupedID to these rows so that I can get the full date range for the earliest FirstGiftDate and the oldest LastGiftDate as long as there is not a break of more than 45 days in between the recurring donations.
For example Bob is a long time supporter. His card has expired multiple times and he has always started new gifts within 45 days. All of his gifts need to be given a single grouped ID. On the opposite side June has been donating and her card expires. She doesn't give again for 6 months, but then continues to give after her card expires. The first gift of Junes should get its own "GroupedID" and the second and third should be grouped together.The grouping count should restart with each donor.
My initial attempt was to join the donation table back to itself aliased as D2. This did work to give me an indicator of which ones were within the 45 day mark but I can't wrap my head around how to then link them. My only thought was to use LEAD and LAG to try analyze each scenario and figure out the different combinations of LEAD and LAG values needed to make it catch each different scenario, but that doesn't seem as reliable as scaleable as I'd like it to be.
I appreciate any help anyone can give.
My code:
SELECT #Donation.*, D2.*
FROM #Donation
LEFT JOIN #Donation D2 ON #Donation.RecurringGiftID <> D2.RecurringGiftID
AND #Donation.Donor = D2.Donor
AND ABS(DATEDIFF(DAY, #Donation.FirstGiftDate, D2.LastGiftDate)) < 45
Table structure and sample data:
CREATE TABLE #Donation
(
RecurringGiftID int,
Donor nvarchar(25),
FirstGiftDate date,
LastGiftDate date
)
INSERT INTO #Donation
VALUES (1, 'Bob', '2017-02-15', '2018-07-01'),
(15, 'Bob', '2018-08-05', '2019-04-01'),
(32, 'Bob', '2019-04-15', '2022-06-15'),
(54, 'June', '2015-05-01', '2016-05-01'),
(96, 'June', '2016-12-15', '2018-02-01'),
(120, 'June', '2018-03-04', '2020-07-01')
Desired output:
RecurringGiftId
Donor
FirstGiftDate
LastGiftDate
GroupedID
1
Bob
2017-02-15
2018-07-01
1
15
Bob
2018-08-05
2019-04-01
1
32
Bob
2019-04-15
2022-06-15
1
54
June
2015-05-01
2016-05-01
1
96
June
2016-12-15
2018-02-01
2
120
June
2018-03-04
2020-07-01
2
use LAG() to detect when current row is more than 45 days from previous and perform a cumulative sum to form the required Group ID
select *,
GroupedID = sum(g) over (partition by Donor order by FirstGiftDate)
from
(
select *,
g = case when datediff(day,
lag(LastGiftDate, 1, '19000101') over (partition by Donor
order by FirstGiftDate),
FirstGiftDate)
> 45
then 1
else 0
end
from #Donation
) d
I have a single table of payments called PYMT, and am trying to wrap my head around using a PIVOT if possible to get a certain arrangement for an output and befuddled as how to do this. In the table are pymt_amount and pymt_date (other columns too, but not necessary). I wish to see the output to look like so:
PayMonth 2007 2008 2009 2010 2011
1 26044.12 82663.79 83583.17 35963.49 100865.94
2 60145.61 35245.06 19173.08 14417.98 21502.71
3 68138.88 88670.16 85319.66 40850.39 31595.43
4 228835.04 215258.84 157905.56 136551.46 166027.30
5 395877.88 348307.58 348506.09 363460.24 298488.22
6 618013.05 662869.88 522233.48 472174.95 385879.94
7 557751.27 363659.66 305363.68 304606.98 349173.75
8 355639.91 173107.60 266235.54 147731.54 251878.49
9 131440.63 173338.90 133869.36 140035.13 109595.83
10 168148.90 127356.25 114818.69 119082.52 139201.50
11 139543.35 138151.22 128667.58 137351.77 107807.27
12 142286.06 136670.64 116980.04 69609.22 85670.84
To get the first column of payment totals is easy - it's doing it for the other years that I can't figure out - I know how to do a basic PIVOT table.
The query for the first 2 columns is
SELECT DATEPART(MM, pymt_Date) AS PayMonth, SUM(pymt_Amount) AS [2007]
FROM PYMT
GROUP BY DATEPART(MM, pymt_Date) , DATEPART(YY, pymt_Date)
HAVING (DATEPART(YY, pymt_Date) = 2007)
ORDER BY PayMonth
How to add the other years (each payMonth is a sum of all payments for the month), but I don't wish to pivot by month (just the years by the month as I show in the output)
I could run a separate query in a cursor per month
Here's an example, but showing the grand total for the year (but it needs to be separated by month)
SELECT * FROM
(SELECT DATEPART(yy, pymt_Date) AS PayYear, pymt_Amount
FROM PYMT
) tbl1
PIVOT
(SUM(pymt_Amount)
FOR
PayYear in ([2007],[2008],[2009],[2010],[2011])
) tbl2
which yields
2007 2008 2009 2010 2011
2891864.70 2545299.58 2282655.93 1981835.67 2047687.22
As you can see - this isn't broken down by month rows
Any ideas?
You missed this DATEPART(month, pymt_Date) AS PayMonth in the tbl1 query.
The complete query should be
SELECT *
FROM
(
SELECT DATEPART(year, pymt_Date) AS PayYear,
DATEPART(month, pymt_Date) AS PayMonth,
pymt_Amount
FROM PYMT
) tbl1
PIVOT
(
SUM(pymt_Amount)
FOR PayYear in ([2007],[2008],[2009],[2010],[2011])
) tbl2
As the title states, we have test scores for students across the year, and need to see how far from the mean the current average score is each week. Each average / deviation value should be inclusive for all the data before, so we would expect that the more data we collect for a single test, the smaller the deviation becomes over time.
This part retrieves the data into a tmp table so I can play with it, getting the score for each student:
declare #tab table(testid int, studentid varchar(20), score int, wk int)
insert #tab
select testid, studentid, sum(mark), datepart(yy, collected) + datepart(isoww,
collected) from MarksTable where testid = 12345
group by testid, studentid,
datepart(yy, collected) + datepart(isoww, collected)
order by datepart(yy, collected) + datepart(isoww, collected)
So now that tmp table has testid, studentid, score and year+wk to give us a week value to group by, it looks like this:
12345 6423-96201 45 2022
12345 6423-96196 34 2022
12345 6421-96147 12 2022
12345 6423-96189 42 2023
12345 6423-96185 4 2023
(hundreds more)
I tried this:
SELECT wk,
STDEV(avg(score)) OVER (ORDER BY wk ROWS UNBOUNDED PRECEDING) AS StdDeviation FROM #tab t
GROUP BY t.wk
but that doesn't give me anything that makes sense.
e.g:
2022 NULL
2023 18.3847763108502
2031 22.1208800307161
2032 18.0739222823013
2033 18.2537667345674
2035 16.3268694692726
etc..
There's no way that can be correct.
What I need to see is the avg and deviation for each week, and these values need to include all the mark data for the weeks PRIOR to them.
Any help would be appreciated.
Stack is Microsoft SQL Server 2016 (SP1-CU15) (KB4495257)
Running this in SQL Server Management Studio v17.4
SELECT ProdName, Sales,
1 AS January, 2 AS February, 3 AS March, 4 AS April, 5 AS May, 6 AS June,
7 AS July, 8 AS August, 9 AS September, 10 AS October,
11 AS November, 12 AS December
FROM
(SELECT actsales as Sales , EXTRACT (month from factdate) as Month, ProdName
FROM factcoffee, prodcoffee
WHERE factcoffee.ProductId = prodcoffee.productid)
PIVOT
(
SUM (Sales)
FOR Month IN
( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 )
);
I get the error for the Sales in line 1. If I remove sales from line 1 only, I get the pivot table of products as rows and the months as columns, as required, but the values are not the sales but are just 1 for january column, 2 for february column and so on.
If I do SELECT * in line 1, I get the correct sales value by month for each product. But the column names are obviously not the month names and just 1,2,...
Your query is fine, but try to make it more readable
SELECT ProdName
, Sales
,[1] AS January
,[2] AS February
,[3] AS March
,[4] AS April
,[5] AS May
,[6] AS June
,[7] AS July
,[8] AS August
,[9] AS September
,[10] AS October
,[11] AS November
,[12] AS December
FROM
(SELECT actsales as Sales , EXTRACT (month from factdate) as Month,
ProdName
FROM factcoffee, prodcoffee
WHERE factcoffee.ProductId = prodcoffee.productid)
PIVOT
(
SUM (Sales)
FOR Month IN
( [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12] )
) as PVT;
Do not forget to rename pivot as PVT or another thing. Sometimes code fails for doing that or forgetting brackets.
Also if you can try to use join instead of FROM factcoffee, prodcoffee. It is more readable