How do I pivot a query in SQL Server - sql-server

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

Related

How to dynamically allocate all months based to a column on condition in SQL Server?

I have a table in which I am storing the Product Name and it's Renewal Date along with the payment plan (Monthly/Quarterly/Yearly). Now if the payment plan of the product is Yearly or Monthly it will display the get the month of Renewal and show the Rate/Amount against that month but if the payment plan is Monthly it should display the Rate/Amount in front of each month as shown below.
For example if a product named ABC has payment plan of Yearly, subscription rate of 276 and Renewal Date 2019-12-01 and there is another product XYZ with payment plan of Monthly, subscription rate of 17 and Renewal Date 2019-08-15 then the result set I want should something like this
ProductName RenewalMonth Rate
------------------------------------
ABC December 276
XYZ January 17
XYZ February 17
XYZ March 17
XYZ April 17
XYZ May 17
XYZ June 17
XYZ July 17
XYZ August 17
XYZ September 17
XYZ October 17
XYZ November 17
XYZ December 17
Here is the query which I have wrote which is returning data that's present in the database fine but not the months other than December for Product XYZ. Keeping in mind this should only display same rate for all other months provided in the desired dates where Payment Plan is 'Monthly', for other payment plans it should show rate in front of given month as present in the database.
Sample data is as follows:
CREATE TABLE ItemDefinition
(
ID INT NOT NULL,
ProductName VARCHAR(50),
PaymentPlan VARCHAR(50),
RenewalDate DATETIME,
UnitRate NUMERIC(18, 0)
);
INSERT INTO ItemDefinition
VALUES (1, 'ABC', 'Yearly', '2019-12-01', 276),
(1, 'XYZ', 'Monthly', '2019-08-15', 17);
And the query I am writing is
SELECT
ProductName, SUM(UnitRate) Rate,
DATENAME(MONTH , DATEADD(MONTH , MONTH(RenewalDate)-1 , '1900-01-01')) RenewalMonth
FROM
ItemDefinition
WHERE
MONTH(RenewalDate) IS NOT NULL
AND RenewalDate BETWEEN #dtStart AND #dtEnd
GROUP BY
ProductName, MONTH(RenewalDate)
ORDER BY
MONTH(RenewalDate)
It might be something like this:
DECLARE #DateBeg DATE = '2019-01-01'
,#DateEnd DAte = '2020-12-01';
WITH Ranges AS
(
SELECT *
,#DateBeg AS [DateBeg]
,#DateEnd AS [DateEnd]
FROM ItemDefinition DS
)
SELECT *
,DATENAME(MONTH ,ISNULL([GeneratedDate], [RenewalDate])) AS RenewalMonth
FROM Ranges
OUTER APPLY
(
SELECT DATEADD(MONTH, [number], [DateBeg])
FROM
(
select number
from master.dbo.spt_values
where [type] = 'P'
) numbers
WHERE DATEADD(MONTH, [number], [DateBeg]) < [DateEnd]
AND [PaymentPlan] = 'Monthly'
) AutoDates ([GeneratedDate]);
You can change the DateEnd parameter to something less and you will see how less months are generated.
The idea is to have start and end date for each row and depending on it to generate your months.
To get the records for the years use the following:
WITH Ranges AS
(
SELECT *
,#DateBeg AS [DateBeg]
,#DateEnd AS [DateEnd]
FROM ItemDefinition DS
)
SELECT *
,DATENAME(MONTH ,ISNULL([GeneratedDate], [RenewalDate])) AS RenewalMonth
,IIF([PaymentPlan] = 'Monthly', [UnitRate], IIF(CONVERT(VARCHAR(7), [RenewalDate], 121) = CONVERT(VARCHAR(7), [GeneratedDate], 121), [UnitRate], NULL))
FROM Ranges
OUTER APPLY
(
SELECT DATEADD(MONTH, [number], [DateBeg])
FROM
(
select number
from master.dbo.spt_values
where [type] = 'P'
) numbers
WHERE DATEADD(MONTH, [number], [DateBeg]) < [DateEnd]
) AutoDates ([GeneratedDate]);
or the following to get the year rate for the first record:
DECLARE #DateBeg DATE = '2019-01-01'
,#DateEnd DAte = '2020-12-01';
WITH Ranges AS
(
SELECT *
,#DateBeg AS [DateBeg]
,#DateEnd AS [DateEnd]
FROM ItemDefinition DS
)
SELECT *
,DATENAME(MONTH ,ISNULL([GeneratedDate], [RenewalDate])) AS RenewalMonth
,IIF([PaymentPlan] = 'Monthly', [UnitRate], IIF([number] = 0, [UnitRate], NULL))
FROM Ranges
OUTER APPLY
(
SELECT DATEADD(MONTH, [number], [DateBeg])
,[number]
FROM
(
select number
from master.dbo.spt_values
where [type] = 'P'
) numbers
WHERE DATEADD(MONTH, [number], [DateBeg]) < [DateEnd]
) AutoDates ([GeneratedDate], [number]);
My advice is to introduce an additional table that will have a single record for a Yearly plan and 12 records for Monthly plan. For example:
create table PaymentPlanInterval(
PaymentPlan VARCHAR(50),
Interval varchar(50)
)
And perhaps this table may contain 2 records for Semi-annual payment plan and 4 records for quartely plan.
In order to get your desired result you should be joining your ItemDefinition table with PaymentPlanInterval. Voila.

I get an invalid identifier error in SQL while trying to make a PIVOT table

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

Deriving averages correctly in a PIVOT

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.

get sum of hours grouped by hours as Rows and month as column?

i try to get sum of Hours as Rows and split it by month as column to get result final as:
Hours Jan Feb Mar
08:00 To 08:59 1000 3500 2000
09:00 To 09:59 5000 6000 12000
ect ....................
the field i work around is DateTime and values on it as:
2014-01-01 08:02:34.000
i try it by query:
SELECT EntryDate, sum(LTOT) AS InvoiceTotal,
sum(DATEPART(SECOND, EntryDate) + 60 *
DATEPART(MINUTE, EntryDate) + 3600 *
DATEPART(HOUR, EntryDate)
) as 'TotalTime'
FROM AZTRH WHERE TR_TY = '102' AND
LOC = '011' AND
(CAST(AZTRH.EntryDate AS DATE) BETWEEN '2014-01-01' AND '2014-01-02') AND
(CAST(AZTRH.EntryDate AS TIME) BETWEEN '08:00:00.000' AND '09:59:59.000')
GROUP BY AZTRH.EntryDate, LTOT
ORDER BY EntryDate
so please how can i get result as i need ?
Try following query:
;WITH PivotSource
AS (
SELECT DATEPART(HOUR, t.EntryDate) AS EntryHour,
MONTH(t.EntryDate) AS EntryMonth,
t.LTOT
FROM ...
WHERE ...
)
SELECT STR(p.EntryHour, 2, 0) + ':00 To' + STR(p.EntryHour, 2, 0)+':59' AS [Hours],
p.[1] AS Jan,
p.[2] AS Feb,
...
FROM PivotSource AS s
PIVOT( SUM(s.LTOT) FOR s.EntryMonth IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]) ) AS p
It uses:
DATEPART(HOUR, D/T value) to extract hour part from EntryDate,
MONTH(D/T value) to extract month number (1, 2, ..., 12),
and PIVOT operator to compute SUM for every hour and month.

Find the min and max dates between multiple sets of dates

Given the following set of data, I'm trying to determine how I can select the start and end dates of the combined date ranges, when they intersect with each other.
For instance, for PartNum 115678, I would want my final result set to display the date ranges 2012/01/01 - 2012/01/19 (rows 1, 2 and 4 combined since the date ranges intersect) and 2012/02/01 - 2012/03/28 (row 3 since this ones does not intersect with the range found previously).
For PartNum 213275, I would want to select the only row for that part, 2012/12/01 - 2013/01/01.
Edit:
I'm currently playing around with the following SQL statement, but it's not giving me exactly what I need.
with DistinctRanges as (
select distinct
ha1.PartNum "PartNum",
ha1.StartDt "StartDt",
ha2.EndDt "EndDt"
from dbo.HoldsAll ha1
inner join dbo.HoldsAll ha2
on ha1.PartNum = ha2.PartNum
where
ha1.StartDt <= ha2.EndDt
and ha2.StartDt <= ha1.EndDt
)
select
PartNum,
StartDt,
EndDt
from DistinctRanges
Here are the results of the query shown in the edit:
You're better off having a persisted Calendar table, but if you don't, the CTE below will create it ad-hoc. The TOP(36000) part is enough to give you 10 years worth of dates from the pivot ('20100101') on the same line.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table data (
partnum int,
startdt datetime,
enddt datetime,
age int
);
insert data select
12345, '20120101', '20120116', 15 union all select
12345, '20120115', '20120116', 1 union all select
12345, '20120201', '20120328', 56 union all select
12345, '20120113', '20120119', 6 union all select
88872, '20120201', '20130113', 43;
Query 1:
with Calendar(thedate) as (
select TOP(36600) dateadd(d,row_number() over (order by 1/0),'20100101')
from sys.columns a
cross join sys.columns b
cross join sys.columns c
), tmp as (
select partnum, thedate,
grouper = datediff(d, dense_rank() over (partition by partnum order by thedate), thedate)
from Calendar c
join data d on d.startdt <= c.thedate and c.thedate <= d.enddt
)
select partnum, min(thedate) startdt, max(thedate) enddt
from tmp
group by partnum, grouper
order by partnum, startdt
Results:
| PARTNUM | STARTDT | ENDDT |
------------------------------------------------------------------------------
| 12345 | January, 01 2012 00:00:00+0000 | January, 19 2012 00:00:00+0000 |
| 12345 | February, 01 2012 00:00:00+0000 | March, 28 2012 00:00:00+0000 |
| 88872 | February, 01 2012 00:00:00+0000 | January, 13 2013 00:00:00+0000 |

Resources