sql adding specific columns - sql

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;

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

Can't use case & aggregation correctly

I have the following table
Cash_table
ID Cash Rates Amount
1 50 3 16
2 100 4 25
3 130 10 7
3 130 10 6
4 13 7 1.8
5 30 8 2.5
5 30 10 1
6 10 5 2
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 New_Amount
1 50 3 16
2 100 4 25
3 130 10+10 130/(10+10)
4 13 7 1.8
5 30 8+10 30/(8+10)
6 10 5 2
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 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 New_Amount column:
I am trying to do it with a case statement but it isn't working:
select id,
cash as new_cash,
sum(rates) as new_rates,
(case count(id)
when 1 then amount
else cash/sum(nvl(rates,null))
end) as new_amount
from Cash_table
group by id
As the cash value is always the same for an ID, you can group by that as well:
select id,
cash as new_cash,
sum(rates) as new_rates,
case count(id)
when 1 then max(amount)
else cash/sum(rates)
end as new_amount
from cash_table
group by id, cash
order by id
ID NEW_CASH NEW_RATES NEW_AMOUNT
---------- ---------- ---------- ----------
1 50 3 16
2 100 4 25
3 130 20 6.5
4 13 7 1.8
5 30 18 1.66666667
6 10 5 2
The first branch of the case expression needs an aggregate because you aren't grouping by amount; and the sum(nvl(rates,null)) can just be sum(rates). If you're expecting any null rates then you need to decide how you want the amount to be handled, but nvl(rates,null) isn't doing anything.
You can do the same thing without a case expression if you prefer, manipulating all the values - which might be more expensive:
select id,
cash as new_cash,
sum(rates) as new_rates,
sum(amount * rates)/sum(rates) as new_amount
from cash_table
group by id, cash
order by id

multiple group by for a table query

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.

SQL: Running total by record groups

I'm trying to create a running total based on groups of records. My data looks like the below
Country | Genre | Month | Amt
A X 1 5
A X 2 3
B X 1 8
B Y 1 10
A X 1 12
I'd like to do a running sum on Amt grouped by unique occurences of Genre and Month for each Country. So my desired output is
Country | Genre | Month | Amt | RunAmt
A X 1 5 5
A X 2 3 3
B X 1 8 8
B Y 1 10 10
A X 1 12 17
Since Genre = X and Month = 1 has occurred previously for country A.
My attempt has been sum(amt) over (partition by country, genre, month order by month rows between unbounded preceding and current row) but it seems to do a running sum over every row not for unique occurences by country. Any help would be appreciated.
Try this
SELECT *,
Sum(amt)
OVER(
partition BY country, genre, month
ORDER BY amt) as RunAmt
FROM ( VALUES ('A','X',1,5),
('A','X',2,3),
('B','X',1,8),
('B','Y',1,10),
('A','X',1,12) )tc(Country, Genre, Month, Amt)
Try this..
select *,
sum(amt) over
(
partition by country, Genre, month order by amt
) as RunAmt
from TableName
Here is the SQLFiddle for you that I tried.

complex paratition sum in postgresql

I have tables as follow:
A deliveries
delveryid clientid deliverydate
1 10 2015-01-01
2 10 2015-02-02
3 11 2015-04-08
B items in deliveris
itemid deliveryid qty status
70 1 5 1
70 1 8 2
70 2 10 1
72 1 12 1
70 3 100 1
I need to add a column to my query that gives me the qty of each part in other deliveris of the same client.
meaning that for given data of client 10 and delivery id 1 I need to show:
itemid qty status qtyOther
70 5 1 10 //itemid 70 exists in delivery 2
70 8 2 10 //itemid 70 exists in delivery 2
72 12 1 0 //itemid 72 doesn't exists in other delivery of client 11
Since I need to add qtyOther to my existing qry i'm trying to avoid using Group By as it's a huge query and if I use SUM in select I will have to group by all items in select.
This is what I have so far:
Select ....., coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) ,0) AS qtyOther
FROM B b
LEFT JOIN A a USING
LEFT JOIN (other tables)
WHERE clientid=10 ....
This query gives me the total sum of qty per itemid for specific clientid, regardless of which delivery it is. How do I change it so it will consider the delivryid? I need something like:
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid) FROM B where deliveryid<>b.deliveryid ,0) AS qtyOther
Any suggestions how to do that?
Note: I can NOT change the condition in WHERE.
I think you just want to subtract out the total for the current delivery:
Select .....,
(coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid), 0) -
coalesce( SUM(a.qty) OVER (PARTITION BY a.itemid, a.deliveryid), 0)
) as qtyOther