SQL:Pivot table which includes sum and percentage total - sql

I'm trying to recreate a view in Tableau as a view in SQL. It requires me pivoting a table based on month and not only summing the amount but I also need to sum by margin and also create a Margin % row.The desired output is
BUSINESS_UNIT CLASS JANUARY FEBRUARY MARCH
202 Cost of Sales 100 (null) 60
202 Revenue 200 80 (null)
202 Margin x xx xxx
202 Margin % x% xx% xxx%
I can pivot based on Month but how do perform twos sums in one pivot table and how would I go about including a percenatge row also?
Code so far
SELECT
*
FROM
(SELECT
[Business_Unit]
,[Class]
,Month as Period
,[Amount]
--,Margin
FROM [sample_table]
where [Class] in ('Revenue','Cost of Sales') )AS T
PIVOT(SUM(Amount)
FOR Period IN ([January],[February],[March])) as Pvt
I have included my code so far http://www.sqlfiddle.com/#!3/06bafc/6

Not the prettiest SQL I've done. but this seems to work...
http://www.sqlfiddle.com/#!3/06bafc/60/0
What it does is build on what you've done by generating a margin line and adding a total column
Using this line and total we can then calculate the % of margin. Grouping SETS allowed me to generate the multiple rows, subtotals and totals, Since I knew the only additional line generated would have a null class, I was able to set the Name of the class to margin when null.
WITH CTE AS (
SELECT
Business_Unit
,case when class is NULL then 'Margin' else class end as Class
,Sum(January) as January
,Sum(February) as February
,Sum(March) as march
,Sum(coalesce(January,0)+coalesce(February,0)+coalesce(March,0)) as Total
FROM (
SELECT
*
FROM
(SELECT
[Business_Unit]
,[Class]
,Month as Period
,[Amount]
--,Margin
FROM [sample_table]
where [Class] in ('Revenue','Cost of Sales') )AS T
PIVOT(SUM(Amount)
FOR Period IN ([January],[February],[March])) as Pvt
) as Base
GROUP BY Grouping sets
((Business_Unit,Class,January,February,March,
coalesce(January,0)+coalesce(February,0)+coalesce(March,0))
,(Business_Unit)
))
SELECT *
FROM CTE UNION
SELECT Business_Unit
,'Margin %'
,January*100.00/Total
,February*100.00/Total
,March*100.00/Total
,Total*100.00/Total
FROM CTE
WHERE CLASS='Margin'

Related

Selecting a second column with a SUM(SUM(value)) function

I am working on a simple query trying to display the total of the totals for 12 periods. I am using a SUM(SUM(value)) function to retrieve the data that I want, however, I am having a hard time displaying a second column in my result.
SELECT CENTRE, SUM(SUM(AMOUNT)) "TOTAL PAY" FROM AB
WHERE ACCOUNT LIKE 'N%' AND CENTRE = '2001' AND YEAR > 2015 GROUP BY AMOUNT, CENTRE;
The error that I am getting has to do with the grouping of the sentence.
Can you please tell me what I have done wrong. I have solved the problem with a sub-query, but I need to fix this query as well because it is used in a more advanced one as a sub-query.
Your questions is too vague to know for sure what you want. For example, what do you mean by "totals for 12 periods"? Is it that for YEAR > 2015 you have 12 rows? Are you always having CENTRE = in your WHERE clause? If so, this might be what you want:
SELECT
MAX(centre) "CENTRE",
SUM(amount) "TOTAL PAY"
FROM
ab
WHERE
account LIKE 'N%'
AND
centre = '2001'
AND
year > 2015;
Or in case CENTRE = 'smth' might not be in your WHERE clause an you need total values for each CENTRE:
SELECT
centre "CENTRE",
SUM(amount) "TOTAL PAY"
FROM
ab
WHERE
account LIKE 'N%'
/* AND
centre = '2001'*/
AND
year > 2015
GROUP BY
centre;
Or in case for every (or one) CENTRE row you need to have total value of all centres:
SELECT
"CENTRE",
total "TOTAL PAY"
FROM
(
SELECT
centre,
ROW_NUMBER() OVER(PARTITION BY
centre
ORDER BY
0
) rn,
SUM(
amount
) OVER(PARTITION BY
0
) total
FROM
ab
WHERE
account LIKE 'N%'
AND
year > 2015
)
WHERE
rn = 1;

How to subtract totals of 2 different groupings of the same column per month

I am writing a query in which I am supposed to subtract total revenue by discounts per month. Problem is that there are multiple codes which represent either revenue or discounts.
To illustrate exactly what I mean, please see the following example.
Month Code Amount
May 4001 $50.05
May 4002 $49.95
May 6005 $15.00
May 6006 $5.00
March 4003 $65.00
Codes for revenue are 4001, 4002 and 4003. Discounts are 6005 and 6006.
In the end I should be seeing:
Month TotalRevenue TotalDiscount Total
May $100.00 $20.00 $80.00
March $65.00 $0.00 $65.00
I have tried CASE but it tells me I can only use 1 argument. I have also tried creating a sub query in the select statement, but I can't seem to be able to use 2 SUM statements (1 in main and 1 in sub). I guess it would work if I had a possibility to use 'join' but there is nothing to join it with.
Well, the first issue is to construct your query so that the Discount Codes are distinguished from the Revenue Codes. If you have table(s) containing the Codes (either one combined Codes table with an indicator to distinguish the two types, or separate tables), this table (or tables) should be used.
Since it's hard to tell the full set of Codes from your question, let's just pretend that Codes 6000-6999 are Discounts and all others are Revenue. Then a query that produces your desired results could look like this:
select Month,
Revenue = sum(
case when Code between 6000 and 6999 then
0
else
Amount
end
),
Discounts = sum(
case when Code between 6000 and 6999 then
Amount
else
0
end
),
Total = sum(
case when Code between 6000 and 6999 then
-1 * Amount
else
Amount
end
)
from MyTable
group by Month
Depending on whatever your actual criteria is for distinguishing the two types of codes, you just need to change the case statements to match and it should work.
This should get you pretty close to the desired result:
Setup (it would be really appreciated if provided in the question :) )
-- drop table Code
create table Code
(
Code INT,
IsRevenue BIT
)
insert into Code VALUES (4001, 1), (4002, 1), (4003, 1), (6005, 0), (6006, 0)
GO
create table MonthData
(
TheMonth VARCHAR(16),
Code INT,
Amount NUMERIC(18, 2)
)
GO
insert into MonthData values ('May', 4001, 50.05), ('May', 4002, 49.95),
('May', 6005, 15.00), ('May', 6006, 5.00), ('March', '4003', 65.00)
GO
select * from MonthData
GO
The query:
SELECT TheMonth,
SUM((CASE WHEN C.IsRevenue = 1 THEN MD.Amount ELSE -MD.Amount END)) AS TotalRevenue
FROM MonthData MD
JOIN Code C ON C.Code = MD.Code
GROUP BY TheMonth

SQL Year over year growth percentage from data same query

How do I calculate the percentage difference from 2 different columns, calculated in that same query? Is it even possible?
This is what I have right now:
SELECT
Year(OrderDate) AS [Year],
Count(OrderID) AS TotalOrders,
Sum(Invoice.TotalPrice) AS TotalRevenue
FROM
Invoice
INNER JOIN Order
ON Invoice.InvoiceID = Order.InvoiceID
GROUP BY Year(OrderDate);
Which produces this table
Now I'd like to add one more column with the YoY growth, so even when 2016 comes around, the growth should be there..
EDIT:
I should clarify that I'd like to have for example next to
2015,5,246.28 -> 346,15942029% ((R2015-R2014) / 2014 * 100)
If you save your existing query as qryBase, you can use it as the data source for another query to get what you want:
SELECT
q1.Year,
q1.TotalOrders,
q1.TotalRevenue,
IIf
(
q0.TotalRevenue Is Null,
Null,
((q1.TotalRevenue - q0.TotalRevenue) / q0.TotalRevenue) * 100
) AS YoY_growth
FROM
qryBase AS q1
LEFT JOIN qryBase AS q0
ON q1.Year = (q0.Year + 1);
Access may complain it "can't represent the join expression q1.Year = (q0.Year + 1) in Design View", but you can still edit the query in SQL View and it will work.
What you are looking for is something like this?
Year Revenue Growth
2014 55
2015 246 4.47
2016 350 1.42
You could wrap the original query a twice to get the number from both years.
select orders.year, orders.orders, orders.revenue,
(select (orders.revenue/subOrders.revenue)
from
(
--originalQuery or table link
) subOrders
where subOrders.year = (orders.year-1)
) as lastYear
from
(
--originalQuery or table link
) orders
here's a cheap union'd table example.
select orders.year, orders.orders, orders.revenue,
(select (orders.revenue/subOrders.revenue)
from
(
select 2014 as year, 2 as orders, 55.20 as revenue
union select 2015 as year, 2 as orders, 246.28 as revenue
union select 2016 as year, 7 as orders, 350.47 as revenue
) subOrders
where subOrders.year = (orders.year-1)
) as lastYear
from
(
select 2014 as year, 2 as orders, 55.20 as revenue
union select 2015 as year, 2 as orders, 246.28 as revenue
union select 2016 as year, 7 as orders, 350.47 as revenue
) orders

Join two Queries so that the second query becomes a row in the results of query 1

I have two queries that I would like to combine so i can make a chart out of the results.
The results have to be very specific or the chart will not display the information properly
I am using MS SQL in Crystal Reports 11
Below is the results I am looking for.
Date Invoice Type Amount
2012/08 Customer Payment 500
2012/08 Customer Invoice 1000
2012/08 Moving Balance 1500
2012/09 Customer Invoice 400
2012/09 Moving Balance 1900
2012/10 Interest 50
2012/10 Moving Balance 1950
So the First query returns the following results
Date Invoice Type Amount
2012/08 Customer Payment 500
2012/08 Customer Invoice 1000
2012/09 Customer Invoice 400
2012/10 Interest 50
and the second query returns
Date Invoice Type Amount
2012/08 Moving Balance 1500
2012/09 Moving Balance 1900
2012/10 Moving Balance 1950
The second query is very long and complicated with a join .
What is the best way of joining these two queries
so that I have one column called invoice Type ( as the chart is based on this field)
that covers all the invoice types plus the moving balance
I assume that the place of the Moving Balance rows inside the result set is important.
You can do something like this:
select date, invoice_type, amount
from
(
select date, invoice_type, amount from query1
union all
select date, invoice_type, amount from query2
)
order by date, case invoice_type when 'Moving Balance' then 1 else 0 end
This first appends the results of the second query to the results of the first query and then reorders the resulting list first by date and then by the invoice type in such a way that the row with Moving balance will come last.
With the actual queries you have given, it should look something like this:
select date, invoice_type, amount
from
(
SELECT
CONVERT(VARCHAR(7),case_createddate, 111) AS Date,
case_invoicetype as invoice_type,
Sum(case_totalexvat) as amount
FROM cases AS ca
WHERE case_primaryCompanyid = 2174 and
datediff(m,case_createddate,getDate())
union all
select
CONVERT(VARCHAR(7),ca.case_createddate, 111) AS Date,
'Moving Balance' as Invoice_Type,
sum(mb.Amount) as Amount
from
cases as ca
left join (
select
case_primaryCompanyId as ID,
case_createdDate,
case_TotalExVat as Amount
from
cases
) mb
on ca. case_primaryCompanyId = mb.ID
and ca.case_createdDate >= mb.case_CreatedDate
where
ca.case_primaryCompanyId = 2174 and
ca.case_createdDate > DATEADD(m, -12, current_timestamp)
group by
case_primaryCompanyId,
CONVERT(VARCHAR(7),ca.case_createddate, 111)
order by ca.case_primaryCompanyid, CONVERT(VARCHAR(7),ca.case_createddate, 111)
)
order by date, case invoice_type when 'Moving Balance' then 1 else 0 end
You can use Union and can use Order by clause
Select * from (Query 1
Union
Query 2
) as a Order by a.Date Asc

MDX Count over time (years - not within a year)

I'd like to be able to rollup the count of commitments to a product over years -
The data for new commitments in each year looks like this:
Year | Count of new commitments | (What I'd like - count of new commitments to date)
1986 4 4
1987 22 26
1988 14 40
1989 1 41
I know that within a year you can do year to date, month to date etc, but I need to do it over multiple years.
the mdx that gives me the first 2 columns is (really simple - but I don't know where to go from here):
select [Measures].[Commitment Count] on 0
, [Date Dim].[CY Hierarchy].[Calendar Year] on 1
from [Cube]
Any help would be great
In MDX something along the line:
with member [x] as sum(
[Date Dim].[CY Hierarchy].[Calendar Year].members(0) : [Date Dim].[CY Hierarchy].currentMember,
[Measures].[Commitment Count]
)
select [x] on 0, [Date Dim].[CY Hierarchy].[Calendar Year] on 1 from [Cube]
Use a common table expression:
with sums (year,sumThisYear,cumulativeSum)
as (
select year
, sum(commitments) as sumThisYear
, sum(commitments) as cumulativeSum
from theTable
where year = (select min(year) from theTable)
group by year
union all
select child.year
, sum(child.commitments) as sumThisYear
, sum(child.commitments) + parent.cumulativeSum as cumulativeSum
from sums par
JOIN thetable Child on par.year = child.year - 1
group by child.year,parent.cumulativeSum
)
select * from sums
There's a bit of a "trick" in there grouping on parent.cumulativeSum. We know that this will be the same value for all rows, and we need to add it to sum(child.commitments), so we group on it so SQL Server will let us refer to it. That can probably be cleaned up to remove what might be called a "smell", but it will work.
Warning: 11:15pm where I am, written off the top of my head, may need a tweak or two.
EDIT: forgot the group by in the anchor clause, added that in