sum for each customers and create a summary table - sql

I have Table A :
Customer_ID Card_number Amount_of_deals
1 221 100
1 222 350
2 223 200
3 334 700
3 344 650
4 544 1500
I want to create a new table with ranges of the amount and count of customers in each range.
the new table should be :
Range Number_of_customers
0-500 2
500-1000 0
1000-1500 2
How can I create this table?
Thanks in advance

You can use case expression with group by :
select (case when amount >= 0 and amount <= 500 then '0-500'
when amount > 500 and amount <= 1000 then '501-1000'
when amount > 1000 and amount <= 1500 then '1001-1500'
else '1500+'
end) as Range, count(distinct custmore_id) as Number_of_customers
from table t
group by (case when amount >= 0 and amount <= 500 then '0-500'
when amount > 500 and amount <= 1000 then '501-1000'
when amount > 1000 and amount <= 1500 then '1001-1500'
else '1500+'
end);
If you are using SQL Server then you can use apply & use SELECT .. INTO statement :
select range, count(distinct custmore_id) as Number_of_customers
into new_table
from table t cross apply
( values (case when amount >= 0 and amount <= 500 then '0-500'
when amount > 500 and amount <= 1000 then '501-1000'
when amount > 1000 and amount <= 1500 then '1001-1500'
else '1500+'
end)
) tt(range)
group by range;

Because you want 0 values, you need a left join somewhere. I would recommend:
select v.range, count(c.customer_id)
from (values ('0-500', 0, 500),
('501-1000', 500, 1000),
('1001-1500', 1000, 1500)
) v(range, lo, hi) left join
(select customer_id, sum(amount_of_deals) as num
from a
group by customer_id
) c
on c.num > v.lo and c.num <= v.hi
group by v.range
order by min( v.lo );
Here is a db<>fiddle.

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

Creating Range Buckets of column

I am having one base table named test_table on which I am doing calculation of the age and it is coming as per expectation.
Here is the query I am using for age calculation.
select acol,
DATEDIFF(hour,CONVERT(DATEADD('SECOND', (epoch_time)/1000, DATE '1970-01-01'), DATE), CURRENT_TIMESTAMP()) AS age
from test_table;
But now I want to create different range of age column which will be displayed as a result by modifying the existing query.
Range would be :
1000-2000
2000-3000
3000-4000
ACOL AGE
MAG 1168
MAG 2168
MAG 3168
MAG 1100
MAG 2168
PNB 1672
MUM 1600
MUM 2696
MUM 3696
MUM 1696
Result after successful query has to look like below table.
ACOL 1000-2000 2000-3000 3000-4000
MAG 2 2 1
PNB 1 0 0
MUM 2 1 1
I know that we can do this using select case but not able to build the working query.
can anyone help me in this.
thanks in advance.
You can use conditional aggregation with a CASE expression:
SELECT t.ACOL,
SUM(CASE WHEN t.AGE >= 1000 AND t.AGE < 2000 THEN t.AGE ELSE 0 END) AS `1000-2000`,
SUM(CASE WHEN t.AGE < 3000 THEN t.AGE ELSE 0 END) AS `2000-3000`,
SUM(CASE WHEN t.AGE < 3000 AND t.AGE < 4000 THEN t.AGE ELSE 0 END) AS `3000-4000`
FROM
(
SELECT ACOL,
DATEDIFF(HOUR, CONVERT(DATEADD(SECOND, (epoch_time)/1000, DATE '1970-01-01'), DATE), CURRENT_TIMESTAMP()) AS AGE
FROM test_table
) t
GROUP BY t.ACOL
SELECT ACOL,
COUNT(CASE WHEN AGE >= 1000 AND AGE < 2000 THEN AGE ELSE NULL END) AS `1000-2000`,
COUNT(CASE WHEN AGE >= 2000 AGE < 3000 THEN AGE ELSE NULL END) AS `2000-3000`,
COUNT(CASE WHEN AGE >= 3000 AND AGE < 4000 THEN AGE ELSE NULL END) AS `3000-4000`
FROM (select ACOL,DATEDIFF(hour,CONVERT(DATEADD('SECOND', (epoch_time)/1000, DATE '1970-01-01'), DATE), CURRENT_TIMESTAMP()) AS AGE FROM test_table )
GROUP BY ACOL
Modified the from clause...
Working ...
:)

Alternative to case statement

I need your suggestions on writing an SQL query other than using CASE statement in Oracle.I have a table named SYSTEM_SPECS with following data
Customer_Id Disk_space_allocated
C001 44G
C002 1300G
C003 1503G
C004 1780G
I wrote following SQL query using Oracle case statement to get count of customer_id based on ranges for disk_space_allocated
select
case
when to_number(substr(disk_space_allocated,0,length(disk_space_allocated) -1 )) <= 300
then '1-300'
when to_number(substr(disk_space_allocated,0,length(disk_space_allocated) -1 )) <= 500
then '300-500'
when to_number(substr(disk_space_allocated,0,length(disk_space_allocated) -1 )) <= 700
then '500-700'
when to_number(substr(disk_space_allocated,0,length(disk_space_allocated) -1 )) <= 900
then '700-900'
else '900+'
end as diskallocated,
count(*) as number_of_customers
from SYSTEM_SPECS
group by
case
when to_number(substr(disk_space_allocated,0,length(disk_space_allocated) -1 )) <= 300
then '1-300'
when to_number(substr(disk_space_allocated,0,length(disk_space_allocated) -1 )) <= 500
then '300-500'
when to_number(substr(disk_space_allocated,0,length(disk_space_allocated) -1 )) <= 700
then '500-700'
when to_number(substr(disk_space_allocated,0,length(disk_space_allocated) -1 )) <= 900
then '700-900'
else '900+'
end;
Can this query be written is some other form?
Firstly your logic is wrong you are calculating <=700 and saying it between 500-700, if it is <=700 then by default it is <=500 and <=300. then we can not call it between 500-700. Try below query
select
count(*) as number_of_customers,b.var as "Less Than below value"
from SYSTEM_SPECS a,
(select 300 + (level-1)*200 var
from dual
connect by level <= 4) b
where to_number(substr(a.disk_space_allocated,0,length(a.disk_space_allocated) -1 )) <= b.var
group by b.var
order by b.var
Final answer for your question with the help of #Alex
with ranges as (
select case when level = 1 then 0 else 100 end + (level-1) * 200 low_value,
case when level = 5 then 99999999 else 100 + (level) * 200 end high_value
from dual
connect by level <= 5
)
select r.low_value ||'-'|| r.high_value as diskallocated,
count(customer_id) as number_of_customers
from ranges r
left join system_specs ss
on to_number(substr(ss.disk_space_allocated, 1, length(ss.disk_space_allocated) -1 )) > r.low_value
and to_number(substr(ss.disk_space_allocated, 1, length(ss.disk_space_allocated) -1 )) <= r.high_value
group by r.low_value, r.high_value
order by r.low_value, r.high_value;

count row with total

From tbl Department, I am trying to write a stored procedure to display output as shown below where I can find the count from each row based on following conditions:
Total = 100
Total >100 and <=200
Total >200 and <=300
Total >300
set #select = 'select count(*) as Orders, sum (tbl.Expenses) as Total from tbl group by tbl.Department'
So, how can I dynamically get the output for 4 conditions as shown above based on my #select statement.
I think you just want conditional aggregation:
select sum(case when total = 100 then 1 else 0 end) as condition1,
sum(case when total > 100 and total <= 200 then 1 else 0 end) as condition2,
sum(case when total > 200 and total <= 300 then 1 else 0 end) as condition3,
sum(case when total > 300 then 1 else 0 end) as condition4
from department d;
Hope that it would be helpful to you,
Select total,
Sum (Case when total = 100 then NumberOfOrders_Created end ) As "Condition1",
Sum (Case when total > 100 and total <= 200 then NumberOfOrders_Created end ) As "Condition2",
Sum (Case when total > 200 and total <= 300 then NumberOfOrders_Created end ) As "Condition3",
Sum (Case when total > 200 and total <= 300 then NumberOfOrders_Created end ) As "Condition4",
From Department
where
Orders_date between '2013-01-01 00:00:00' and '2013-12-31 00:00:00'
group by 2
order by 1 Desc

return enddate where cumulative sum of fee column less than 1100 using sql

end date fee
-----------------
05-Sep-14 700
12-Sep-14 200
19-Sep-14 100
26-Sep-14 300
03-Oct-14 400
In the table shown here, I need to return enddate where cumulative sum of fee column is less than 1100 using SQL.
Example:
19-Sep-14 (700 + 200 + 100 < 1100)
SELECT TOP 1
t1.enddate,
t1.fee,
SUM(t2.fee) as cumulative_sum
FROM test t1
INNER JOIN tableName t2 on t1.enddate >= t2.enddate
GROUP BY t1.enddate, t1.fee
HAVING SUM(t2.fee) < 1100
ORDER BY t1.enddate DESC
Data sample
create view fees
as
select cast('05-Sep-14' as date) as end_date, 700 as fee
union all select '12-Sep-14', 200
union all select '19-Sep-14', 100
union all select '26-Sep-14', 300
union all select '03-Oct-14', 400
Solution
SELECT TOP 1
a.end_date,
SUM(b.fee) as cumulative
FROM fees a CROSS JOIN fees b
WHERE a.end_date >= b.end_date
GROUP BY a.end_date
HAVING SUM(b.fee) < 1100
ORDER BY end_date desc