SQL Query for a Compare Report from single table - sql

SQL Newb here, I'm having a bit of trouble understanding this problem. How can I write a single SELECT statement where I can have columns with their own WHERE clauses, do a calculation, and group the results.
I can write the query to sum totals and do averages checks grouping by revenue center and fiscal year, but I can't quite grasp how to do side by side compare with a single query.
SALES DATA
| RevenueCenter | FiscalYear | TotalSales | NumChecks |
|---------------|------------|------------|-----------|
| market | 2019 | 2000.00 | 10 |
| restaurant | 2019 | 5000.00 | 25 |
| restaurant | 2020 | 4000.00 | 20 |
| market | 2020 | 3000.00 | 10 |
COMPARE REPORT
| RevenueCenter | TotalSales2020 | TotalSales2019 | %Change | AvgCheck2020 | AvgCheck2019 | %Change |
| market | 3000.00 | 2000.00 | +50% | 300.00 | 200.00 | +50% |
| restaurant | 4000.00 | 5000.00 | -20% | 200.00 | 200.00 | 0% |

Would this help? No big deal, just a self-join with some arithmetic.
SQL> with sales (revenuecenter, fiscalyear, totalsales, numchecks) as
2 -- sample data
3 (select 'market' , 2019, 2000, 10 from dual union all
4 select 'market' , 2020, 3000, 10 from dual union all
5 select 'restaurant', 2019, 5000, 25 from dual union all
6 select 'restaurant', 2020, 4000, 20 from dual
7 )
8 -- query you need
9 select a.revenuecenter,
10 b.totalsales totalsales2020,
11 a.totalsales totalsales2019,
12 --
13 (b.totalsales/a.totalsales) * 100 - 100 "%change totalsal",
14 --
15 b.totalsales / b.numchecks avgcheck2020,
16 a.totalsales / a.numchecks avgcheck2019,
17 --
18 (b.totalsales / b.numchecks) /
19 (a.totalsales / a.numchecks) * 100 - 100 "%change numcheck"
20 from sales a join sales b on a.revenuecenter = b.revenuecenter
21 and a.fiscalyear < b.fiscalyear;
REVENUECEN TOTALSALES2020 TOTALSALES2019 %change totalsal AVGCHECK2020 AVGCHECK2019 %change numcheck
---------- -------------- -------------- ---------------- ------------ ------------ ----------------
market 3000 2000 50 300 200 50
restaurant 4000 5000 -20 200 200 0
SQL>

Related

sql group by monthly sum and sum year by month

RDBMS - Latest Oracle
I'm out of my element here. I need to organize account transaction information by account and by month, and also use another column to show summed transactions for year to date. Here is a depiction of what I'm trying to get
ACCT_ID | ACCT_MM | FISCAL_YYYY | FISCAL_MM_AMT | YTD_AMT
------------------------------------------------------------
1 | 11 | 2018 | 25 | 100
1 | 12 | 2018 | 50 | 150
1 | 01 | 2019 | 20 | 20
I know you can get FISCAL_MM_AMT with a group by ACCT_MM, FISCAL_YYYY
this is all I have figured out so far.
SELECT ACCT_ID,ACCT_MM,FISCAL_YYYY,SUM(NVL(ACCT_TRNSCTN_AMT,0))
FROM TBL_ACCT_DETAIL
GROUP BY ACCT_ID,ACCT_MM,FISCAL_YYYY
Now how to combine this with the additional column YTD_AMT to that adds up all totals for that year up to the current month is what has me baffled. sql noob ftw.
Try analytical function SUM as following:
SELECT T.*,
SUM(FISCAL_MM_AMT)
OVER (PARTITION BY ACCT_ID, FISCAL_YYYY
ORDER BY ACCT_MM) AS YTD_AMT
FROM
(SELECT ACCT_ID,ACCT_MM,FISCAL_YYYY,SUM(NVL(ACCT_TRNSCTN_AMT,0)) AS FISCAL_MM_AMT
FROM TBL_ACCT_DETAIL
GROUP BY ACCT_ID,ACCT_MM,FISCAL_YYYY);
Cheers!!
You can use the cumulative sum window function:
SELECT ACCT_ID, ACCT_MM, FISCAL_YYYY,
COALESCE(SUM(ACCT_TRNSCTN_AMT), 0),
SUM(SUM(ACCT_TRNSCTN_AMT)) OVER (PARTITION BY ACCT_ID, FISCAL_YYYY ORDER BY ACCT_MM) AS YTD
FROM TBL_ACCT_DETAIL
GROUP BY ACCT_ID, ACCT_MM, FISCAL_YYYY
Do you want to have one additional column bringing the sum of the year with it, and a year-to-date column?
OLAP functions are a prerequisite. But every respectable RDBMS should offer those by now.
Then, I think (adding what I presume should be the input) ... you should go:
WITH
---- this is just the input so I have example data
input( acct_id,acct_mm,fiscal_yyyy,fiscal_mm_amt) AS (
SELECT 1, 1, 2018, 5
UNION ALL SELECT 1, 3, 2018, 5
UNION ALL SELECT 1, 4, 2018, 5
UNION ALL SELECT 1, 5, 2018, 10
UNION ALL SELECT 1, 6, 2018, 10
UNION ALL SELECT 1, 7, 2018, 10
UNION ALL SELECT 1, 8, 2018, 10
UNION ALL SELECT 1, 9, 2018, 10
UNION ALL SELECT 1, 10, 2018, 10
UNION ALL SELECT 1, 11, 2018, 25
UNION ALL SELECT 1, 12, 2018, 50
UNION ALL SELECT 1, 01, 2019, 20
)
---- end of input -----
SELECT
*
, SUM(fiscal_mm_amt) OVER(
PARTITION BY acct_id,fiscal_yyyy
) AS fiscal_yy_amt
, SUM(fiscal_mm_amt) OVER(
PARTITION BY acct_id,fiscal_yyyy
ORDER BY acct_mm
) AS ytd_amt
FROM input;
-- out acct_id | acct_mm | fiscal_yyyy | fiscal_mm_amt | fiscal_yy_amt | ytd_amt
-- out ---------+---------+-------------+---------------+---------------+---------
-- out 1 | 1 | 2018 | 5 | 150 | 5
-- out 1 | 3 | 2018 | 5 | 150 | 10
-- out 1 | 4 | 2018 | 5 | 150 | 15
-- out 1 | 5 | 2018 | 10 | 150 | 25
-- out 1 | 6 | 2018 | 10 | 150 | 35
-- out 1 | 7 | 2018 | 10 | 150 | 45
-- out 1 | 8 | 2018 | 10 | 150 | 55
-- out 1 | 9 | 2018 | 10 | 150 | 65
-- out 1 | 10 | 2018 | 10 | 150 | 75
-- out 1 | 11 | 2018 | 25 | 150 | 100
-- out 1 | 12 | 2018 | 50 | 150 | 150
-- out 1 | 1 | 2019 | 20 | 20 | 20
-- out (12 rows)
-- out
-- out Time: First fetch (12 rows): 76.235 ms. All rows formatted: 76.288 ms

DAX: Avoid summing up repeated numbers

I have a table that includes columns for accountID, revenue, quarter and estimated revenue. the issue with this table is that the revenue repeats for each quarter.
The table is perfect if I'm trying to find the estimate, but as soon as I sum the revenue, it basically quadruples for each account.
I can divide by 4, but that gives a wrong revenue number for each quarter.
Is there a DAX function that allows me to show "4000" for each quarter, but at account/company level, it would not quadruple in size?
AccountID | Revenue | Quarter | Estimate
123 | 4000 |Q1 | 4000
123 | 4000 |Q2 | 5000
123 | 4000 |Q3 | 2000
123 | 4000 |Q4 | 4000
456 | 3000 |Q1 | 4000
456 | 3000 |Q2 | 5000
456 | 3000 |Q3 | 1000
456 | 3000 |Q4 | 3000
What I would like to see in pivot
Account ID | Quarter | Sum of Revenue | Sum of Estimate
123 | Q1 |4000 | 4000
123 | Q2 |4000 | 5000
123 | Q3 |4000 | 2000
123 | Q4 |4000 | 4000
123 Total |4000 | 15000
456 | Q1 |3000 | 4000
456 | Q2 |3000 | 5000
456 | Q3 |3000 | 1000
456 | Q4 |3000 | 3000
456 Total |3000 | 13000
Grand Total |7000 | 2800
Create two measures EstimateMeasure and RevenueMeasure:
EstimateMeasure = SUM(Table1[Estimate])
RevenueMeasure = SUMX(VALUES(Table3[Revenue]),Table3[Revenue])
Add previously created measures to Values and AccountID & Quarter in your pivot table.
It should produce the following output in Power BI.
Note I don't have access to PowerPivot in this moment, so I can't post the complete table with subtotals. But you can add subtotals in PowerPivot to get the total per AccountID.
Let me know if this helps.

Postgres: Adjust monthly calculations based on goals set

Below is my table:
practice_id | practice_name | practice_location | practice_monthly_revenue | practice_no_of_patients | date
-------------+-------------------+-------------------+--------------------------+-------------------------+---------------------
6 | Practice Clinic 1 | Location1 | 10000 | 8 | 2016-01-12 00:00:00
7 | Practice Clinic 1 | Location1 | 12000 | 10 | 2016-02-12 00:00:00
8 | Practice Clinic 1 | Location1 | 8000 | 4 | 2016-03-12 00:00:00
9 | Practice Clinic 1 | Location1 | 15000 | 10 | 2016-04-12 00:00:00
10 | Practice Clinic 1 | Location1 | 7000 | 3 | 2016-05-12 00:00:00
11 | Practice Clinic 2 | Location2 | 15000 | 12 | 2016-01-13 00:00:00
12 | Practice Clinic 2 | Location2 | 9000 | 8 | 2016-02-13 00:00:00
13 | Practice Clinic 2 | Location2 | 5000 | 2 | 2016-03-03 00:00:00
14 | Practice Clinic 2 | Location2 | 12000 | 9 | 2016-04-13 00:00:00
----------------------------------------------------------------------------------------------------------------------------------
I am firing below query to get monthly revenue vs monthly goal:-
select [date:month], SUM(practice_monthly_revenue) as Monthly_Revenue, 100000/12 as Goals
from practice_info
where practice_name IN ('Practice Clinic 1')
group by [date:month], practice_name
ORDER BY [date:month] ASC
Where "Monthly_Revenue" refers to exact revenue every month while Goal was the exact revenue expected to be generated.
Now I am having issue to write a sql query to adjust the goals next month if the goals aren't met.
E.g. if in March the revenue generated is below 8k which is the monthly goal then the remaining amount in goal should be adjusted in next months goal.
Will it be possible to achieve this with a sql query or I will have to write a sql procedure for it?
EDIT:- I forgot to add that the db belong to postgres.
Goals can be counted as
with recursive goals(mon, val, rev) as
(select min([pinf.date:month]) as mon /* Starting month */, 8000 as val /* Starting goal value */, pinf.practice_monthly_revenue as rev
from practice_info pinf
where pinf.practice_name IN ('Practice Clinic 1')
union all
select goals.mon + 1 as mon, 8000 + greatest(0, goals.val - goals.rev) as val, pinf.practice_monthly_revenue as rev
from practice_info pinf, goals
where goals.mon + 1 = [pinf.date:month]
and pinf.practice_name IN ('Practice Clinic 1')
)
select * from goals;
Just integrate it with your query to compare goals and revenues. It can be not exactly what you want, but I do believe you'll get the main point.

How to SUM() each row into another column

I have this table
| ID_prim | ID (FKey) | Date | Moved Items |
|:-----------|:------------|-------------:|:------------:|
| 1003 | 12_1 | nov 2013 | 2 |
| 1003 | 12_2 | okt 2013 | 3 |
| 1003 | 12_3 | dec 2014 | 5 |
| 1003 | 12_4 | feb 2015 | 10 |
| 1003 | 12_5 | apr 2012 | 1 |
| 1003 | 12_11 | jan 2011 | 5 |
I want to query the same table as follows:
Order the Date by desc
Sum each 'Moved Item" per row
Stop the query if the Sum reaches my desired amount
My desired amount starts from the MAX 'Summed Total' (26) and subtracts the amount I want (16)
Like so
| ID_prim | ID (FKey) | Date | Moved Items | Summed Total |
|:-----------|:------------|-------------:|:------------:|:------------:|
| 1003 | 12_4 | feb 2015 | 10 | 26
| 1003 | 12_3 | dec 2014 | 5 | 16
| 1003 | 12_3 | nov 2013 | 2 | 11 <
| 1003 | 12_4 | okt 2013 | 3 | 9
| 1003 | 12_5 | apr 2012 | 1 | 6
| 1003 | 12_11 | jan 2011 | 5 | 5
I want to stop the query when i reach "Summed Total" (26) - 16 = 10. So Show me everything from 10 >
I would only get these values in the database.
| ID_prim | ID (FKey) | Date | Moved Items | Summed Total |
|:-----------|:------------|-------------:|:------------:|:------------:|
| 1003 | 12_4 | feb 2015 | 10 | 26
| 1003 | 12_3 | dec 2014 | 5 | 16
| 1003 | 12_3 | nov 2013 | 2 | 11
What I have is the following
SELECT
T1.ID_prim, T1.ID as ID (FKey), T1.Moved_Items as Moved Items, t1.Date, SUM(T2.MOVEMENTQTY) AS Summed Total
FROM Table1 T1
INNER JOIN Table1 T2 ON T2.ID <= T1.ID
inner join table2 inout on T1.ID_prim = inout.ID_prim
AND T2.ID_prim = inout.ID_prim
AND T2.ID_prim = T1.ID_prim
where t1.ID_prim = 1003
and t2.ID_prim = 1003
and inout.ISSOTRX = 'N'
GROUP BY T1.ID_prim, T1.Moved Items, t1.Date
HAVING SUM(T2.Moved Items) <= 16
order by t1.UPDATED desc
But the sum doesn't really work.
Can anyone help me out to make the SQL statement for Oracle DB that will print my Desired table?
Based on OP's clarifications via comments on the question, it could be done using SUM() analytic function to get the running total, and then filter it based on the condition.
Table:
SQL> SELECT * FROM t;
ID_PRIM ID DT MOVED
---------- ----- --------- ----------
1003 12_1 01-NOV-13 2
1003 12_2 01-OCT-13 3
1003 12_3 01-DEC-14 5
1003 12_4 01-FEB-15 10
1003 12_5 01-APR-12 1
1003 12_11 01-JAN-11 5
6 rows selected.
SQL>
Running total
SQL> SELECT t.*, SUM(moved) OVER(ORDER BY dt) sm FROM t ORDER BY dt DESC;
ID_PRIM ID DT MOVED SM
---------- ----- --------- ---------- ----------
1003 12_4 01-FEB-15 10 26
1003 12_3 01-DEC-14 5 16
1003 12_1 01-NOV-13 2 11
1003 12_2 01-OCT-13 3 9
1003 12_5 01-APR-12 1 6
1003 12_11 01-JAN-11 5 5
6 rows selected.
SQL>
Desired output
SQL> WITH DATA AS
2 ( SELECT t.*, SUM(moved) OVER(ORDER BY dt) sm FROM t ORDER BY dt DESC
3 )
4 SELECT * FROM data WHERE sm >= 16;
ID_PRIM ID DT MOVED SM
---------- ----- --------- ---------- ----------
1003 12_4 01-FEB-15 10 26
1003 12_3 01-DEC-14 5 16
SQL>
Please note that, nov 2013 is not a date, it is a string. Since you want to sort on the basis of date, you must always use TO_DATE to explicitly convert it into date. Anyway, I used TO_DATE to create the sample data.
Update OP wants to subtract his desired value from the MAX value of the summed up values at run time.
SQL> WITH DATA AS
2 ( SELECT t.*, SUM(moved) OVER(ORDER BY dt) sm FROM t ORDER BY dt DESC
3 )
4 SELECT * FROM DATA t WHERE sm >
5 (SELECT MAX(sm) FROM data
6 ) - 16 ;
ID_PRIM ID DT MOVED SM
---------- ----- --------- ---------- ----------
1003 12_4 01-FEB-15 10 26
1003 12_3 01-DEC-14 5 16
1003 12_1 01-NOV-13 2 11
SQL>
In the updated query, MAX(sm) returns 26, and then the rows are filtered on the condition WHERE sm > MAX(sm) -16 which means return all the rows where the 'sm' value is greater than 26 -16 i.e. 10. You could use a substitution variable to input the value 16 at run time.

Getting Monthly Data Even If Empty

I'm trying to get a report built up from data mining our accounting software.
We have a table that stores the balances of each account in a general ledger for a given period (which is 0-12, 0 being carry over from last year, 1-12 being the corresponding month), the amount, and other data I don't need.
I'm trying unsuccessfully to get a value for each account for each month, however there isn't always a corresponding entry. I've tried left outer joins, cross joins, inner joins, and can't seem to get it to work how I want. I've even tried doing left outer joins with a table containing 'Initial' as item 0 and 12 other entries, one name for each month.
Here's a sample of the data:
GLBalances table:
acct_no | post_prd | post_trn_amt
1011 | 0 | -15000
1011 | 1 | 5000
1011 | 2 | -6000
1011 | 4 | 8000
1020 | 5 | 100
1020 | 12 | 300
1011 | 9 | 500
1011 | 8 | 0
etc...
What I'd like to get out is:
acct_no | post_prd | post_trn_amt
1011 | 0 | -15000
1011 | 1 | 5000
1011 | 2 | -6000
1011 | 3 | 0
1011 | 4 | 8000
1011 | 5 | 0
1011 | 6 | 0
1011 | 7 | 0
1011 | 8 | 0
1011 | 9 | 500
1011 | 10 | 0
1011 | 11 | 0
1011 | 12 | 0
1020 | 0 | 0
1020 | 1 | 0
1020 | 2 | 0
1020 | 3 | 0
1020 | 4 | 0
1020 | 5 | 100
1020 | 6 | 0
1020 | 7 | 0
1020 | 8 | 0
1020 | 9 | 0
1020 | 10 | 0
1020 | 11 | 0
1020 | 12 | 300
etc...
So basically 13 entries for each acct for a particular year even if there's no entry for that period.
I'm sure this is way easier than I'm making it, I'm just struggling since I don't deal with SQL on a daily basis. Any help would be much appreciated.
You can create a sheet of valid accounts and months with cross join. Look for the corresponding "real" row with a left join, and you're set:
;with months as
(
select 0 as Month
union all
select Month + 1 from months where Month < 12
)
select a.acct_no, m.month as post_prd, IsNull(g.post_trn_amt,0)
from months m
cross join (select distinct acct_no from #GLBalances) a
left join #GLBalances g
on m.month = g.post_prd
and a.acct_no = g.acct_no
order by a.acct_no, m.month
The "with months as" construct is a fancy way to create a table containing numbers 0 to 12. You can also create a real table containing those numbers, and do away with the "recursive common table expression" construct.
Here's the test data I used:
declare #GLBalances table (acct_no int, post_prd int, post_trn_amt int)
insert into #GLBalances
select 1011,0,-15000
union all select 1011, 1, 5000
union all select 1011, 2, -6000
union all select 1011, 4, 8000
union all select 1020, 5, 100
union all select 1020, 12, 300
union all select 1011, 9, 500
union all select 1011, 8, 0