multiple group by for a table query - sql

I have a table like this:
Month Type Price
============================
1 a 12
2 b 43
1 a 11
4 c 22
1 b 33
2 c 4
3 a 25
2 b 35
4 c 20
I want to get a query that has result some thing like this:
Month Type Total Price
============================
1 a 23
1 b 33
2 b 78
2 c 4
3 a 25
4 c 44
means:
prices are Total Price of special Type in a Month.
for example we have type 'a' in month '1' and '3'
Total Prices of 'a' in month '1' is 23 and in month '3' is 25
I think we should use multiple group by.
I can group it just by Type or Month but not by both of them.
thanks for helping

You can specify a list of expressions in the GROUP BY clause
SELECT Month, Type, SUM(Price) AS [Total Price]
FROM MyTable
GROUP BY Month, Type
ORDER BY Month, Type
In GROUP BY, list all the involved columns, except those that have an aggregate function (SUM, MIN, MAX, AVG etc.) applied to them.

Related

Use aggregation only on rows where count(ID) is greater than one

Hi I have the following table
Cash_table
ID Cash Rates
1 50 3
2 100 4
3 70 10
3 60 10
4 13 7
5 20 8
5 10 10
6 10 5
What I want as a result is to cumulate all the entries that have a Count(id)>1 like this:
ID New_Cash New_Rates
1 50 3
2 100 4
3 (70+60)/(10+10) 10+10
4 13 7
5 (20+10)/(8+10) 8+10
6 10 5
So I only want to change the rows where Count(id)>1 and leave the rest like it was.
For the rows with count(id)>1 I want to sum up the rates and take the sum of the cash and divide it by the sum of the rates. The Rates alone aren't a problem since I can sum them up and group by id and get the desired result.
The problem is with the cash column:
I am trying to do it with a case statement but it isn't working:
select id, sum(rates) as new_rates, case
when count(id)>1 then sum(cash)/nullif(sum(rates),0))
else cash
end as new_cash
from Cash_table
group by id
You only need group by id and aggregate:
select
id,
sum(cash) / (case count(*) when 1 then 1 else sum(rates) end) as new_cash,
sum(rates) as new_rates
from Cash_table
group by id
order by id
See the demo.
You can aggregate rate and cash columns by sum() function with grouping by id
select
id,
sum(cash)/decode( sum( nvl(rates,0) ), 0 ,1, sum( nvl(rates,0) )) as new_cash,
sum(rates) as new_rates
from cash_table
group by id
there's no nullif() function in Oracle, use nvl() instead
switch case part ( where decode() function is used ) against the
possibility of division by zero

SQL: Grouping, Count & Average

Trying an SQL question here. I have a SQLite DB (table):
type_id value
1 26
1 24
2 30
3 5
3 15
I want to achieve the following. For each type_id, I would like to know the number of rows (count) with that type_id and the average value (average) of the group. In the example table I would end up with:
type_id count average
1 2 25
2 1 30
3 2 10
Any ideas? Thanks :)
Just GROUP BY type_id and take the COUNT and AVG:
SELECT type_id, COUNT(*) AS count, AVG(value) AS average
FROM test
GROUP BY type_id
Output:
type_id count average
1 2 25
2 1 30
3 2 10
Demo on dbfiddle

How do I make a query that selects where the SUM equals a fixed value

I've spent that last couple of days searching for a way to make a SQL query that searches the database and returns records where the SUM of the same ID's equal or grater then the value provided.
For this I've been using the W3schools database to test it out in the products table.
More so what I've been trying to do:
SELECT * FROM products
WHERE supplierid=? and SUM(price) > 50
in the "where supplier id" would loop through same suppliers and sum of their price higher than 50 in this case return the records.
In this case it would read supplier ID 1 then add the price of all that supplier 18+19+10=47 now 47 < 50 so it will not print those records at the end. Next supplier ID 2 22+21.35=43.35 and again would not print those records until the sum of price is higher than 50 it will print
I'm working with a DB2 database.
SAMPLE data:
ProductID ProductName SupplierID CategoryID Price
1 Chais 1 1 18
2 Chang 1 1 19
3 Aniseed 1 2 10
4 Chef Anton 2 2 22
5 Chef Anton 2 2 21.35
6 Grandma's 3 2 25
7 Uncle Bob 3 7 30
8 Northwoods 3 2 40
9 Mishi 4 6 97
10 Ikura 4 8 31
11 Queso 5 4 21
12 Queso 5 4 38
13 Konbu 6 8 6
14 Tofu 6 7 23.25
How about:
select * from products where supplierid in (
select supplierid
from products
group by supplierid
having sum(price) > 50
);
The subquery finds out all the supplierid values that match your condition. The main (external) query retrieves all rows that match the list of supplierids.
not tested, but I would expect db2 to have analytic functions and CTEs, so perhaps:
with
basedata as (
select t.*
, sum(t.price) over(partition by t.supplierid) sum_price
from products t
)
select *
from basedata
where supplierid = ?
and sum_price > 50
The analytic function aggregates the price information but does not group the resultset, so you get the rows from your initial result, but restricted to those with an aggregated price value > 50.
The difference to a solution with a subquery is, that the use of the analytic function should be more efficient since it has to read the table only once to produce the result.

SQL query to return rows in multiple groups

I have a SQL table with data in the following format:
REF FIRSTMONTH NoMONTHS VALUE
--------------------------------
1 2 1 100
2 4 2 240
3 5 4 200
This shows a quoted value which should be delivered starting on the FIRSTMONTH and split over NoMONTHS
I want to calculate the SUM for each month of the potential deliveries from the quoted values.
As such I need to return the following result from a SQL server query:
MONTH TOTAL
------------
2 100 <- should be all of REF=1
4 120 <- should be half of REF=2
5 170 <- should be half of REF=2 and quarter of REF=3
6 50 <- should be quarter of REF=3
7 50 <- should be quarter of REF=3
8 50 <- should be quarter of REF=3
How can I do this?
You are trying extract data from what should be a many to many relationship.
You need 3 tables. You should be able to write a JOIN or GROUP BY select statement from there. The tables below don't use the same data values as yours, and are merely intended for a structural example.
**Month**
REF Month Value
---------------------
1 2 100
2 3 120
etc.
**MonthGroup**
REF
---
1
2
**MonthsToMonthGroups**
MonthREF MonthGroupREF
------------------
1 1
2 2
2 3
The first part of this query gets a set of numbers between the start and the end of the valid values
The second part takes each month value, and divides it into the monthly amount
Then it is simply a case of grouping each month, and adding up all of the monthly amounts.
select
number as month, sum(amount)
from
(
select number
from master..spt_values
where type='p'
and number between (select min(firstmonth) from yourtable)
and (select max(firstmonth+nomonths-1) from yourtable)
) numbers
inner join
(select
firstmonth,
firstmonth+nomonths-1 as lastmonth,
value / nomonths as amount
from yourtable) monthly
on numbers.number between firstmonth and lastmonth
group by number

sql adding specific columns

I have a table in oracle containing fields
id,location,stock,rate
select decode(grouping(id),1,'Total',id) id,loction,
sum(stock) stock,avg(rate) rate from product
group by rollup(id),location
I have got
ID Location stock rate
------------------------------------------
A xx 2 10
A xy 5 20
Total 7 10
B SD 3 4
B RT 6 10
Total 9 7
C FG 12 12
C GH 20 18
Total 32 15
**Now I want a row of Total rows where sum of stock and
sum of rate is shown.**
My desired output is
ID Location stock rate
------------------------------------------
A xx 2 10
A xy 5 20
Total 7 10
B SD 3 4
B RT 6 10
Total 9 7
C FG 12 12
C GH 20 18
Total 32 15
Grand Total 48 32
Note : Rate is not average but sum of average rate of total rows.
You could use UNION to append the total:
select decode(grouping(id),1,'Total',id) id,loction,
sum(stock) stock,avg(rate) rate from product
group by rollup(id),location
UNION
select 'Grand Total','',sum(stock) stock,sum(rate) rate
from product
If you weren't mixing different aggregates you could get a grand total with ROLLUP or with grouping sets, but I don't think you can get a SUM() grand total with AVG() subtotals.
Doing a rollup on all columns will produce the grand total:
select (case when grouping(id) = 1 then 'Total' else id end) as id, loction,
sum(stock) stock,avg(rate) rate
from product
group by rollup(id, location)
However, you do not want the rollup on location. So you can filter that out with a having clause:
select (case when grouping(id) = 1 then 'Total' else id end) as id, loction,
sum(stock) stock, avg(rate) rate
from product
group by rollup(id, location)
having not (grouping(product.location) = 1 and grouping(product.id) = 0);
May be like this:
select decode(grouping(id),1,'Total',id) id,loction,
sum(stock) stock,avg(rate) rate from product
group by rollup(id),location
union
select sum(stock), sum(rate) from
(
select decode(grouping(id),1,'Total',id) id,loction,
sum(stock) stock,avg(rate) rate from product
group by rollup(id),location
) where id='Total' group by id;