Sql to order results by sum - sql

DB used - Oracle
create table customer_exercise(
customer_id number,
exercise_id number,
cnt number,
exercise_date date)
Table data
1000 10 3 17-DEC-15
1001 20 6 19-DEC-15
1000 20 2 20-DEC-15
1003 20 9 20-DEC-15
1000 20 6 22-DEC-15
1000 30 10 23-DEC-15
1001 10 25 10-DEC-15
Is it possible to get results using sql such that sum(cnt) for exercise_id 20 appears first in the resultset?
select customer_id , exercise_id, sum(cnt) from customer_exercise
where customer_id in (1000, 1001, 1003) and exercise_id in (20)
group by customer_id, exercise_id order by sum(cnt)
1001 20 6
1000 20 8
1003 20 9
select customer_id , exercise_id, sum(cnt) from customer_exercise
where customer_id in (1000, 1001, 1003) and exercise_id not in (20)
group by customer_id, exercise_id order by sum(cnt)
1000 10 3
1000 30 10
1001 10 25
What I am trying to do is merge the results of above two queries with one sql. Is it possible to write a single sql that will fetch the resultset like below?
1001 20 6
1000 20 8
1003 20 9
1000 10 3
1000 30 10
1001 10 25

I think that would be:
select customer_id , exercise_id, sum(cnt)
from customer_exercise
where customer_id in (1000, 1001, 1003)
group by customer_id, exercise_id
order by (case when exercise_id in (20) then 1 else 2 end), sum(cnt)

Related

Select max of nested id from amazon redshift

My database is an amazon redshift.
I have a table that looks like this -
id
nested_id
date
value
1
10
'2021-01-01'
5
1
20
'2021-01-01'
10
1
10
'2021-01-02'
6
1
20
'2021-01-02'
11
1
10
'2021-01-03'
7
1
20
'2021-01-03'
12
2
30
'2021-01-01'
5
2
40
'2021-01-01'
10
2
30
'2021-01-02'
6
2
40
'2021-01-02'
11
2
30
'2021-01-03'
7
2
40
'2021-01-03'
12
So this is basically a table that tracks values by id over time, except for every id there can be a nested_id. And the dates and values are primarily connected to the nested_id.
However, let's say I'm starting with the id field, but for each id I want to only return the points over time for the nested_id that has the greater sum of points.
So right now I'm just grabbing it like this...
select *
from mytable
where id in (1, 2)
except I only want it to return nested_id rows where the maximum value of that nested_id is the greatest.
So here's how I would do this manually.
For id of 1, the maximum value is 12, and the nested_id of that value is 20
For id of 2, the maximum value is 12, and the nested_id of that value is 40
So my return table should be
id
nested_id
date
value
1
20
'2021-01-01'
10
1
20
'2021-01-02'
11
1
20
'2021-01-03'
12
2
40
'2021-01-01'
10
2
40
'2021-01-02'
11
2
40
'2021-01-03'
12
Is there an easy way of performing this query? I'm assuming you have to partition somehow?
You can solve this with row_number window functions
with maxs as (
select id,
nested_id,
value,
row_number() over (partition by id order by value desc) rn
from mytable
)
select mt.*
from mytable mt
left join maxs on mt.id = maxs.id and mt.nested_id = maxs.nested_id
where maxs.rn = 1

Running assignment of values with break T-SQL

With the below table of data
Customer
Amount Billed
Amount Paid
Date
1
100
60
01/01/2000
1
100
40
01/02/2000
2
200
150
01/01/2000
2
200
30
01/02/2000
2
200
10
01/03/2000
2
200
15
01/04/2000
I would like to create the next two columns
Customer
Amount Billed
Amount Paid
Assigned
Remainder
Date
1
100
60
60
40
01/01/2000
1
100
40
40
0
01/02/2000
2
200
150
150
50
01/01/2000
2
200
30
30
20
01/02/2000
2
200
10
10
10
01/03/2000
2
200
15
10
-5
01/04/2000
The amount paid on each line should be removed from the amount billed and pushed onto the next line for the same customer. The process should continue until there are no more records or the remainder is < 0.
Is there a way of doing this without a cursor? Maybe a recursive CTE?
Thanks
As I mentioned in the comments, this is just a cumulative SUM:
WITH YourTable AS(
SELECT *
FROM (VALUES(1,100,60 ,CONVERT(date,'01/01/2000')),
(1,100,40 ,CONVERT(date,'01/02/2000')),
(2,200,150,CONVERT(date,' 01/01/2000')),
(2,200,30 ,CONVERT(date,'01/02/2000')),
(2,200,10 ,CONVERT(date,'01/03/2000')),
(2,200,15 ,CONVERT(date,'01/04/2000')))V(Customer,AmountBilled,AmountPaid,[Date]))
SELECT Customer,
AmountBilled,
AmountPaid,
AmountBilled - SUM(AmountPaid) OVER (PARTITION BY Customer ORDER BY [Date] ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Remainder,
[Date]
FROM YourTable
ORDER BY Customer,
[Date];
Note this returns -5 for the last row, not 5, as 200 - 205 = -5. If you want 5 wrap the whole expression in an absolute function.
You can achieve this using recursive CTE as well.
DECLARE #customer table (Customer int, AmountBilled int, AmountPaid int, PaidDate date)
insert into #customer
values
(1 ,100, 60 ,'01/01/2000')
,(1 ,100, 40 ,'01/02/2000')
,(2 ,200, 150 ,'01/01/2000')
,(2 ,200, 30 ,'01/02/2000')
,(2 ,200, 10 ,'01/03/2000')
,(2 ,200, 15 ,'01/04/2000');
;WITH CTE_CustomerRNK as
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY customer order by paiddate) AS RNK
from #customer),
CTE_Customer as
(
SELECT customer, AmountBilled, AmountPaid, (amountbilled-amountpaid) as remainder, paiddate ,RNK FROM CTE_CustomerRNK where rnk = 1
union all
SELECT r.customer, r.AmountBilled, r.AmountPaid, (c.remainder - r.AmountPaid) as remainder, r.PaidDate, r.rnk
FROM CTE_CustomerRNK as r
inner join CTE_Customer as c
on c.Customer = r.Customer
and r.rnk = c.rnk + 1
)
SELECT customer, AmountBilled, AmountPaid, remainder, paiddate
FROM CTE_Customer order by Customer
customer
AmountBilled
AmountPaid
remainder
paiddate
1
100
60
40
2000-01-01
1
100
40
0
2000-01-02
2
200
150
50
2000-01-01
2
200
30
20
2000-01-02
2
200
10
10
2000-01-03
2
200
15
-5
2000-01-04

3 or more consecutive entries in the last 15 days

I have the following data:
ID EMP_ID SALE_DATE
---------------------------------
1 777 5/28/2016
2 777 5/29/2016
3 777 5/30/2016
4 777 5/31/2016
5 888 5/26/2016
6 888 5/28/2016
7 888 5/29/2016
8 999 5/29/2016
9 999 5/30/2016
10 999 5/31/2016
i need to fetch data for emp_id having 3 or more days of consecutive sales in the last 15 days.
Output should be:
777
999
Following is the query:
SELECT TRUNC (sale_date), emp_id
FROM table1
WHERE sale_date >= SYSDATE - 14
GROUP BY TRUNC (sale_date), emp_id
HAVING COUNT (*) >= 3
But this returns consecutive transactions in the last three days only.
Note: This is oracle.
Assuming you have one row per day, you can use lead():
select distinct emp_id
from (select t1.*,
lead(sale_date, 1) over (partition by emp_id order by sale_date) as sd_1,
lead(sale_date, 2) over (partition by emp_id order by sale_date) as sd_2
from table1 t1
where sale_date >= trunc(sysdate) - 14
) t
where sd_1 = sale_date + 1 and
sd_2 = sale_date + 2;

Find out average loan taken by age group in specific ranges and display them in a table

Consider the following 2 tables:
customer( **c_id**, c_name, c_dob)
customer_loan_taken( **loan_no**, c_id, taken_date, loan_amount)
How to find out average loan taken by age group 20-25, 30-35, 40-45, and display them in a single table ?
The table contents are as follows:
customer table
C_ID C_NAME C_DOB
1 Jainam Jhaveri 17-FEB-93
2 Harsh Mehra 10-DEC-91
3 Mohit Desai 15-OCT-75
4 Raj Gupta 31-AUG-80
5 Yash Shah 24-NOV-85
6 Dishank Parikh 02-OCT-78
7 Chandni Jain 06-MAR-83
8 Bhavesh Prajapati 13-MAY-71
9 Priyank Khandelwal 18-JUN-86
10 Mihir Vora 11-NOV-95
customer_loan_taken table
LOAN_NO C_ID TAKEN_DAT LOAN_AMOUNT
1011 1 12-SEP-11 100000
1012 3 20-APR-10 200010
1013 4 15-OCT-12 150000
1014 5 04-JAN-13 2500005
1015 7 15-AUG-16 2600001
1016 8 21-DEC-16 3500000
1017 9 13-NOV-17 4000000
1018 10 05-MAR-18 1010100
This is working in Oracle 12c. The trick to figure out the age can differ on database as datediff is not working in oracle. So modify it accordingly
with customer( c_id, c_name, c_dob) as
(select 1,'A','31/01/1990' from dual union
select 2,'A','31/01/1980' from dual union
select 3,'C','31/01/1970' from dual union
select 4,'D','31/08/1990' from dual),
ag as
(select c.* ,
FLOOR(TRUNC(MONTHS_BETWEEN(SYSDATE, to_date(c_dob,'DD/MM/YYYY'))) /12) as age,
case when FLOOR(TRUNC(MONTHS_BETWEEN(SYSDATE, to_date(c_dob,'DD/MM/YYYY'))) /12) between 20 and 25 then '20-25' when
FLOOR(TRUNC(MONTHS_BETWEEN(SYSDATE, to_date(c_dob,'DD/MM/YYYY'))) /12) between 30 and 35 then '30-35' when
FLOOR(TRUNC(MONTHS_BETWEEN(SYSDATE, to_date(c_dob,'DD/MM/YYYY'))) /12) between 40 and 45 then '40-45'
end as agegroup from customer c
),
customer_loan_taken( loan_no, c_id, taken_date, loan_amount)
as
(
select 101,1,'01/01/1990',1000 from dual union
select 102,2,'01/01/1990',2000 from dual union
select 103,3,'01/01/1990',3000 from dual union
select 104,4,'01/01/1990',4000 from dual
)
select distinct(ag.agegroup),avg(loan_amount) from customer_loan_taken cl,ag
where ag.c_id=cl.c_id
group by ag.agegroup
;WITH cte AS (
SELECT CASE
WHEN DATEDIFF ("YY", c_dob, GETDATE()) > 20
AND DATEDIFF ("YY", c_dob, GETDATE()) <= 25 THEN '20-25'
WHEN DATEDIFF ("YY", c_dob, GETDATE()) > 25
AND DATEDIFF ("YY", c_dob, GETDATE()) <= 30 THEN '25-30'
WHEN DATEDIFF ("YY", c_dob, GETDATE()) > 30
AND DATEDIFF ("YY", c_dob, GETDATE()) <= 35 THEN '30-35'
END AS rangedate,
l.loan_amount
FROM customer
INNER JOIN customer_loan_taken l ON customer.c_id = l.c_id
)
SELECT rangedate,
AVG(loan_amount) average
FROM cte
GROUP BY rangedate
select avg(loan_amt) "LOAN_AVG",age_range from(
select l.loan_amount as loan_amt,
(case
when FLOOR((months_between(sysdate,to_CHAR(c.c_dob,'DD-MON-YYYY')))/12)
between 20 and 25 then '20-25'
when FLOOR((months_between(sysdate,to_CHAR(c.c_dob,'DD-MON-YYYY')))/12)
between 30 and 35 then '30-35'
when FLOOR((months_between(sysdate,to_CHAR(c.c_dob,'DD-MON-YYYY')))/12)
between 40 and 45 then '40-45'
else '46-90'
end) as age_range
from customer c,cust_loan_taken l
where c.C_ID=l.C_ID) a
group by age_range;

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