SQL sum of two fields - sql

I have a table with following fields
vchnrno credit debit amount
JV1 BA10 0 100
JV1 BA11 0 10
JV1 0 BC10 90
JV1 0 BC11 20
Usually sum of credit side= sum of debit side for every JV
here
credit = sum of BA10+ BA11 = 110
debit = sum of BC10 + BC 11 = 110
I want to find all JV's whose sum of debit - sum of credit >0

Looks like SUM + CASE might do the job. Here's how:
SQL> with test (vchnrno, credit, debit, amount) as
2 (select 'JV1', 'BA10', '0' , 100 from dual union all
3 select 'JV1', 'BA11', '0' , 10 from dual union all
4 select 'JV1', '0' , 'BC10', 90 from dual union all
5 select 'JV1', '0' , 'BC11', 20 from dual union all
6 --
7 select 'xxx', 'XX20', '0' , 50 from dual union all
8 select 'xxx', '0' , 'xx30', 70 from dual
9 )
10 select vchnrno, sum_credit, sum_debit, sum_debit - sum_credit diff
11 from (select vchnrno,
12 sum(case when credit <> '0' then amount end) sum_credit,
13 sum(case when debit <> '0' then amount end) sum_debit
14 from test
15 group by vchnrno
16 )
17 where sum_debit - sum_credit > 0;
VCH SUM_CREDIT SUM_DEBIT DIFF
--- ---------- ---------- ----------
xxx 50 70 20
SQL>
I included vchnrno = xxx into sample data because JV1 doesn't match criteria (sum of debit = sum of credit) so it wouldn't be returned anyway.

Related

Count average with multiple conditions

I'm trying to create a query which allows to categorize the average percentage for specific data per month.
Here's how my dataset presents itself:
Date
Name
Group
Percent
2022-01-21
name1
gr1
5.2
2022-01-22
name1
gr1
6.1
2022-01-26
name1
gr1
4.9
2022-02-01
name1
gr1
3.2
2022-02-03
name1
gr1
8.1
2022-01-22
name2
gr1
36.1
2022-01-25
name2
gr1
32.1
2022-02-10
name2
gr1
35.8
...
...
...
...
And here's what I want to obtain with my query (based on what I showed of the table):
Month
<=25%
25<_<=50%
50<_<=75%
75<_<=100%
01
1
1
0
0
02
1
1
0
0
...
...
...
...
...
The result needs to:
Be ordered by month
Have the average use for each name counted and categorized
So far I know how to get the average of the Percent value per Name:
SELECT Name,
AVG(Percent)
from `table`
where Group = 'gr1'
group by Name
and how to count iterations of Percent in the categories created for the query:
SELECT EXTRACT(MONTH FROM Date) as Month,
COUNT(CASE WHEN Percent <= 25 AND Group = 'gr1' THEN Name END) `_25`,
COUNT(CASE WHEN Percent > 25 AND Percent <= 50 AND Group = 'gr1' THEN Name END) `_50`,
COUNT(CASE WHEN Percent > 50 AND Percent <= 75 AND Group = 'gr1' THEN Name END) `_75`,
COUNT(CASE WHEN Percent > 75 AND Percent <= 100 AND Group = 'gr1' THEN Name END) `_100`,
FROM `table`
GROUP BY Month
ORDER BY Month
but this counts all iterations of every name where I want the average of those values.
I've been struggling to figure out how to combine the two queries or to create a new one that answers my need.
I'm working with the BigQuery service from Google Cloud
This query produces the needed result, based on your example. So basically this combines your 2 queries using subquery, where the subquery is responsible to calculate AVG grouped by Name, Month and Group, and the outer query is for COUNT and "categorization"
SELECT
Month,
COUNT(CASE
WHEN avg <= 25 THEN Name
END) AS _25,
COUNT(CASE
WHEN avg > 25
AND avg <= 50 THEN Name
END) AS _50,
COUNT(CASE
WHEN avg > 50
AND avg <= 75 THEN Name
END) AS _75,
COUNT(CASE
WHEN avg > 75
AND avg <= 100 THEN Name
END) AS _100
FROM
(
SELECT
EXTRACT(MONTH from Date) AS Month,
Name,
AVG(Percent) AS avg
FROM
table1
GROUP BY Month, Name, Group
HAVING Group = 'gr1'
) AS namegr
GROUP BY Month
This is the result:
Month
_25
_50
_75
_100
1
1
1
0
0
2
1
1
0
0
See also Fiddle (BUT on MySql) - http://sqlfiddle.com/#!9/16c5882/9
You can use this query to Group By Month and each Name
SELECT CONCAT(EXTRACT(MONTH FROM Date), ', ', Name) AS DateAndName,
CASE
WHEN AVG(Percent) <= 25 THEN '1'
ELSE '0'
END AS '<=25%',
CASE
WHEN AVG(Percent) > 25 AND AVG(Percent) <= 50 THEN '1'
ELSE '0'
END AS '25<_<=50%',
CASE
WHEN AVG(Percent) > 50 AND AVG(Percent) <= 75 THEN '1'
ELSE '0'
END AS '50<_<=75%',
CASE
WHEN AVG(Percent) > 75 AND AVG(Percent) <= 100 THEN '1'
ELSE '0'
END AS '75<_<=100%'
from DataTable /*change to your table name*/
group by EXTRACT(MONTH FROM Date), Name
order by DateAndName
It gives the following result:
DateAndName
<=25%
25<_<=50%
50<_<=75%
75<_<=100%
1, name1
1
0
0
0
1, name2
0
1
0
0
2, name1
1
0
0
0
2, name2
0
1
0
0

Need to SUM all negative charges and create new column and duplicate with positive charges

I am trying to create a financial report with one account number that shows a total "debit" (which are positive or >0 amounts) and "credit" (which are negative or <0) for that particular account number.
Currently I am pulling all transactions for the account number with both negative and positive's in the amount column. I need to split that and SUM all the negatives and create a new column called 'Credits' and SUM all the positives and create a new column called 'Debits'.
I saw one other post on here but that query was not working for me.
> SELECT acc.account, acc.account_ty, coa.DESCR, dis.Amount, acc.create_dt,
> FROM GL_COA_ACCOUNT acc
> JOIN GL_COA_CHART coa
> ON acc.account = coa.account
> JOIN AR_GL_DISTRIB dis
> ON dis.GLCHART_SERNO = coa.GLCHART_SERNO
> JOIN AR_RCT_LEDGER led
> ON led.DISTRIB_SERNO = dis.distrib_serno
> JOIN AR_RCT_INVHDR inv
> ON led.invoice_num = inv.invoice_num
**Account account_ty DESCR Amount create_dt DEBITS CREDITS NET AMOUNT**
XXX09 ASSET XXX09-00-000-00 A/R Membership Dues -55 09-AUG-16
XXX23 LIABILITY XXX23-00-000-00 Checks / Cash Clearing 55 07-SEP-16
XXX09 ASSET XXX09-00-000-00 A/R Membership Dues 55 09-AUG-16
XXX02 INCOME XXX02-01-000-00 Predoctoral Student D -55 07-SEP-16
XXX09 ASSET XXX09-00-000-00 A/R Membership Dues -55 09-AUG-16
XXX23 LIABILITY XXX23-00-000-00 Checks / Cash Clearing 55 07-SEP-16
XXX09 ASSET XXX09-00-000-00 A/R Membership Dues 55 09-AUG-16
What you need is conditional aggregation:
select ......, sum(case when amount < 0 then amount end) as credits,
sum(case when amount > 0 then amount end) as debits
from ......
group by account_number
I would suggest something like that:
SELECT account,
sum((amount>0)*amount) as debit,
sum((amount<0)*amount) as credit
FROM table
GROUP BY account
Create an Expression Which Checks for Sign of Amount (and NULLS) and Assigns a Non-NULL Value and Sums
I just make sure that my expression is non-NULL (all records have a non-NULL numeric assignment) and then I sum.
Being primarily an Oracle SQL developer, I am biased towards Oracle functions such as decode and sign.
SCOTT#dev>WITH test_data AS (
2 SELECT
3 4.32 amount, '100' account
4 FROM
5 dual
6 UNION ALL
7 SELECT 3.23, '100'
8 FROM
9 dual
10 UNION ALL
11 SELECT
12 -3.22, '100'
13 FROM
14 dual
15 UNION ALL
16 SELECT
17 -4.22, '100'
18 FROM
19 dual
20 UNION ALL
21 SELECT
22 4.22, '200'
23 FROM
24 dual
25 UNION ALL
26 SELECT
27 0, '200'
28 FROM
29 dual
30 UNION ALL
31 SELECT
32 NULL, '200'
33 FROM
34 dual
35 ) SELECT
36 account,
37 SUM(DECODE( sign(nvl( amount, 0) ), 1, amount, 0) ) debits,
38 SUM(DECODE( sign(nvl( amount, 0) ), -1, amount, 0) ) credits
39 FROM
40 test_data
41 GROUP BY
42 account;
ACCOUNT DEBITS CREDITS
100 7.55 -7.44
200 4.22 0

Finding Avg of following dataset

Following is the data.
select * from (
select to_date('20140601','YYYYMMDD') log_date, null weight from dual
union
select to_date('20140601','YYYYMMDD')+1 log_date, 0 weight from dual
union
select to_date('20140601','YYYYMMDD')+2 log_date, 4 weight from dual
union
select to_date('20140601','YYYYMMDD')+3 log_date, 4 weight from dual
union
select to_date('20140601','YYYYMMDD')+4 log_date, null weight from dual
union
select to_date('20140601','YYYYMMDD')+5 log_date, 8 weight from dual);
Log_date weight avg_weight
----------------------------------
6/1/2014 NULL 0 (0/1) Since no previous data, I consider it as 0
6/2/2014 0 0 ((0+0)/2)
6/3/2014 4 4/3 ((0+0+4)/3)
6/4/2014 4 2 (0+0+4+4)/4
6/5/2014 NULL 2 (0+0+4+4+2)/5 Since it is NULL I want to take previous day avg = 2
6/6/2014 8 3 (0+0+4+4+2+8)/6 =3
So the average for the above data should be 3.
How can I achieve this in SQL instead of PLSQL. Appreciate any help on this.
I just learned how to use recursive CTEs today, really excited! Hope this helps...
; WITH RawData (log_Date, Weight) AS (
select cast('2014-06-01' as SMALLDATETIME)+0, null
UNION ALL select cast('2014-06-01' as SMALLDATETIME)+1, 0
UNION ALL select cast('2014-06-01' as SMALLDATETIME)+2, 4
UNION ALL select cast('2014-06-01' as SMALLDATETIME)+3, 4
UNION ALL select cast('2014-06-01' as SMALLDATETIME)+4, null
UNION ALL select cast('2014-06-01' as SMALLDATETIME)+5, 8
)
, IndexedData (Id, log_Date, Weight) AS (
SELECT ROW_NUMBER() OVER (ORDER BY log_Date)
, log_Date
, Weight
FROM RawData
)
, ResultData (Id, log_Date, Weight, total, avg_weight) AS (
SELECT Id
, log_Date
, Weight
, CAST(CASE WHEN Weight IS NULL THEN 0 ELSE Weight END AS FLOAT)
, CAST(CASE WHEN Weight IS NULL THEN 0 ELSE Weight END AS FLOAT)
FROM IndexedData
WHERE Id = 1
UNION ALL
SELECT i.Id
, i.log_Date
, i.Weight
, CAST(r.total + CASE WHEN i.Weight IS NULL THEN r.avg_weight ELSE i.Weight END AS FLOAT)
, CAST(r.total + CASE WHEN i.Weight IS NULL THEN r.avg_weight ELSE i.Weight END AS FLOAT) / i.Id
FROM ResultData r
JOIN IndexedData i ON i.Id = r.Id + 1
)
SELECT Log_Date, Weight, avg_weight FROM ResultData
OPTION (MAXRECURSION 0)
This gives the output:
Log_Date Weight avg_weight
----------------------- ----------- ----------------------
2014-06-01 00:00:00 NULL 0
2014-06-02 00:00:00 0 0
2014-06-03 00:00:00 4 1.33333333333333
2014-06-04 00:00:00 4 2
2014-06-05 00:00:00 NULL 2
2014-06-06 00:00:00 8 3
Note that in my answer, I modified the "Data" section of your question as it didn't compile for me. It's still the same data though, hope it helps.
Edit: By default, MAXRECURSION is set to 100. This means that the query will not work for more than 101 rows of Raw Data. By adding the OPTION (MAXRECURSION 0), I have removed this limit so that the query works for all input data. However, this can be dangerous if the query isn't tested thoroughly because it might lead to infinite recursion.

How to find regions where total of their sale exceeded 60%

I have a table interest_summary table with two columns:
int_rate number,
total_balance number
example
10.25 50
10.50 100
10.75 240
11.00 20
My query should return in 2 columns or a string like 10.50 to 10.75 because adding their total exceed 60% of total amount added together
Could you suggest a logic in Oracle?
select
min(int_rate),
max(int_rate)
from
(
select
int_rate,
nvl(sum(total_balance) over(
order by total_balance desc
rows between unbounded preceding and 1 preceding
),0) as part_sum
from interest_summary
)
where
part_sum < (select 0.6*sum(total_balance) from interest_summary)
fiddle
I'm assuming that you're selecting the rows based on the following algorithm:
Sort your rows by total_balance (descending)
Select the highest total_balance row remaining
If its total_balance added to the running total of the total balance is under 60%, add it to the pool and get the next row (step 2)
If not add the row to the pool and return.
The sorted running total looks like this (I'll number the rows so that it's easier to understand what happens):
SQL> WITH data AS (
2 SELECT 1 id, 10.25 interest_rate, 50 total_balance FROM DUAL
3 UNION ALL SELECT 2 id, 10.50 interest_rate, 100 total_balance FROM DUAL
4 UNION ALL SELECT 3 id, 10.75 interest_rate, 240 total_balance FROM DUAL
5 UNION ALL SELECT 4 id, 11.00 interest_rate, 20 total_balance FROM DUAL
6 )
7 SELECT id, interest_rate,
8 SUM(total_balance) OVER (ORDER BY total_balance DESC) running_total,
9 SUM(total_balance) OVER (ORDER BY total_balance DESC)
10 /
11 SUM(total_balance) OVER () * 100 pct_running_total
12 FROM data
13 ORDER BY 3;
ID INTEREST_RATE RUNNING_TOTAL PCT_RUNNING_TOTAL
---------- ------------- ------------- -----------------
3 10,75 240 58,5365853658537
2 10,5 340 82,9268292682927
1 10,25 390 95,1219512195122
4 11 410 100
So in this example we must return rows 3 and 2 because row 2 is the first row where its percent running total is above 60%:
SQL> WITH data AS (
2 SELECT 1 id, 10.25 interest_rate, 50 total_balance FROM DUAL
3 UNION ALL SELECT 2 id, 10.50 interest_rate, 100 total_balance FROM DUAL
4 UNION ALL SELECT 3 id, 10.75 interest_rate, 240 total_balance FROM DUAL
5 UNION ALL SELECT 4 id, 11.00 interest_rate, 20 total_balance FROM DUAL
6 )
7 SELECT ID, interest_rate
8 FROM (SELECT ID, interest_rate,
9 SUM(over_limit)
10 OVER(ORDER BY total_balance DESC) over_limit_no
11 FROM (SELECT id,
12 interest_rate,
13 total_balance,
14 CASE
15 WHEN SUM(total_balance)
16 OVER(ORDER BY total_balance DESC)
17 / SUM(total_balance) OVER() * 100 < 60 THEN
18 0
19 ELSE
20 1
21 END over_limit
22 FROM data
23 ORDER BY 3))
24 WHERE over_limit_no <= 1;
ID INTEREST_RATE
---------- -------------
3 10,75
2 10,5

Group by on the basis of conditions

I have a table:
spent totalsent totalused
----------------------------
4 1234 123
6 12 4
7 45 32
I need to group totalused/totalsent in groups of 0 <= spent <= 5, 6 <= spent <= 10 and so on.
How can this be done?
If you are just wanting groups of 5, you can probably just set up a column to group on which divides by 5.
with my_source_data as (
select 4 as spent, 1234 as totalsent, 123 as totalused from dual union all
select 6 as spent, 12 as totalsent, 4 as totalused from dual union all
select 7 as spent, 45 as totalsent, 32 as totalused from dual
)
select
(spent_group -1) * 5 + 1 as lower_bound,
spent_group * 5 as upper_bound, totalsent, totalused
from (
select
greatest(ceil(spent/5),1) as spent_group,
sum(totalsent) as totalsent, sum(totalused) totalused
from my_source_data
group by greatest(ceil(spent/5),1)
)
This code doesn't quite handle 0 or anything below 0 correctly since it puts everything in the lower group and labels it 1-5, but your requirements are a little vague in that respect.
Also, this is a sparse grouping so there will only be a row for 11-15 if there is source data which allows it to be produced.