doing a group by within a group - sql

i have two columns business_line(with values X,Y) and business_segment(values X,Y,Z same X and Y as business_line) in a table with name "Sometable". I have another column with name type_of_payment(with values A,B,C,D,E) and a final column with name transaction_value. This is what i want to do:
Sum the transactions grouped by business_line and business_segment and also find out what proportion of these payments were from A,C,E. So my output table would be something like this
(last three columns can be named anything
but they specify proportions of A,C,E)
Business_line SUM(transaction_value) A C E
and business seg.
X 100 20% 30% 50%
Y 200 11% 12% 77%
X 300 and so on
Y 170
Z 230
How do i do this??
PS : the sums of A C E need not be 100% as B and D are also present

This is standard SQL, should work on Oracle (but untested):
SELECT
business_line,
business_segment,
grand_total,
A_total * 100.0 / grand_total as A,
C_total * 100.0 / grand_total as C,
E_total * 100.0 / grand_total as E
FROM
(
SELECT
business_line,
business_segment,
SUM(transaction_value) as grand_total,
SUM(CASE WHEN payment_type = 'A' THEN transaction_value END) as A_total,
SUM(CASE WHEN payment_type = 'C' THEN transaction_value END) as C_total,
SUM(CASE WHEN payment_type = 'E' THEN transaction_value END) as E_total
FROM
SomeTable
GROUP BY
business_line,
business_segment
) as t

For Oracle 11g and above, you can use PIVOT
select *
from
(
select sometable.line, paymenttype,total, 100.0*transaction_value/total as percentage
from sometable
inner join
(select line, sum(transaction_value) as total
from sometable
group by line) total
on sometable.line = total.line
)
pivot
(
sum(percentage) for paymenttype in (a,c,e)
)

Related

Average Multiple Columns in Postgres

I have a table with multiple rows, each of which contains these four columns:
product
price_instore
price_pickup
price_delivery
price_ship
1
$13.00
$13.50
$14.50
$18.00.
2
$4.00
$4.00
NULL
NULL
3
$10.00
$10.00
$12.00
NULL
I'd like to have a fifth column average_price that gives the average price for a product, but does not count NULLS towards the sum price, or towards the count used to divide the average.
So average price of product 1: ($13+$13.50+$14.50+$18)/4=$14.75
Average price of product 2: ($4+$4)/2 = $4.00
Average price of product 3: ($10+$10+$12)/3 = $10.67
Is there any way to do this is SQL? I've tried subqueries such as the one below without success:
(select coalesce((PRICE_INSTORE + PRICE_PICKUP + PRICE_DELIVERY + PRICE_SHIP) / 4,
PRICE_INSTORE, PRICE_PICKUP, PRICE_DELIVERY, PRICE_SHIP)
but then I only get one price if any of them are null
select
*,
(select avg(x) from unnest(array[price_instore,price_pickup,price_delivery,price_ship]) as x) as avg_price
from
your_table;
I'm drunk, so there's probably a much better way to do this than pivoting, but I cannot see it now with the room spinning around me like it is.
with j as (
select product, to_jsonb(mytable) - 'product' as jdata
from mytable
)
select j.product, avg(e.value::numeric) as avg_price
from j
cross join lateral jsonb_each(j.jdata) as e(key, value)
where e.value is not null
group by j.product.
Try this
select coalesce (PRICE_INSTORE, 0 ) + coalesce (PRICE_PICKUP, 0) + coalesce (PRICE_DELIVERY , 0) + coalesce (PRICE_SHIP , 0) /
((case when PRICE_INSTORE is null then 0 else 1 end) + (case when PRICE_PICKUP is null then 0 else 1 end) +
(case when PRICE_DELIVERY is null then 0 else 1 end) + (case when PRICE_SHIP is null then 0 else 1 end) )
from product
One method is a lateral join:
select t.*, avg_price
from t cross join lateral
(select avg(price) as avg_price
from (values (price_instore), (price_pickup), (price_delivery), (price_ship)
) v(price)
) x
try this:
select coalesce(PRICE_INSTORE,0) + coalesce(PRICE_PICKUP,0) + coalesce(PRICE_DELIVERY,0) + coalesce(PRICE_SHIP,0)) / 4
This one is probably the easiest to digest. Performance shouldn't be too bad unless you have a very large table that is also, not indexed
with cte (product, prices) as
(select product, price_instore from t union all
select product, price_pickup from t union all
select product, price_delivery from t union all
select product, price_ship from t)
select product, avg(prices)
from cte
group by product;
You an either use the output as is, or wrap it around another CTE and use that to join on your original table to get the averages

How to calculate in SQL

How to calculate in SQL
SELECT NO
, SUM(COST)*1.05
FROM TR
WHERE NO in (SELECT NO FROM USERS)
AND TR.TYPE = 'A'
OR TR.TYPE = 'B'
GROUP
BY NO
I would like to calculate the COST:
WHEN TR.TYPE IS A I want to add to the total cost.
WHEN TR.TYPE IS B I want to subtract the cost value from the total.
TR_TABLE
NO,COST,TYPE
1,1000,A
1,500, B
1,200, A
2,100, A
Ideal
No1 COST 700
No2 COST 100
I think that you want a conditional expression within the aggregation function:
select
no,
sum(case when type = 'A' then cost else -cost end) * 0.25 as fuyogaku
from tr
where
tr.type in ('A', 'B')
and exists (select 1 from users u where u.no = c.card_no)
group by no
Note that I rewrote the in condition as an exists condition - both are equivalent here, but exists usually scales better when there is a large number of rows in the subquery.

dvide 2 column ina table on each other

I have a factor table and it has two columns: totalprice and paidprice.
I want divide the count of total price on paidprice and *100 and i want return result as percentage.
select TotalPrice/PaidPrice as h
from Factors
where TotalPrice>=PaidPrice
You can try the following:
SELECT (COUNT(PAID_PRICE) / CAST(Total_Price AS FLOAT)) * 100 AS Result
from #Table
WHERE TOTAL_PRICE >= PAID_PRICE
GROUP by TOTAL_PRICE
http://rextester.com/PKXSYV97387
TABLE:
**PAID_PRICE** **TOTAL_PRICE**
35 50
40 45
QUERY:
SELECT (SUM(PAID_PRICE)/SUM(TOTAL_PRICE))*100
FROM TABLE_NAME WITH (NOLOCK)
WHERE TOTAL_PRICE>=TOTAL_PRICE
Try this?
select
cast(
SUM(CASE WHEN PaidPrice>=TotalPrice THEN 1 ELSE 0 END) *
100 /
COUNT(FactorID)as varchar(5)
) + '%' as percentageOFcomepletfactor
from
Factors

Optimize SQL query for given Conditions

I have a query of the form:
select SUM(some_column) from (table)
where
IF x then a
ELSE y then b
ELSE z then c
...
Now in my JAVA code,i call this query for every different value(x,y,z,...),which returns me required sum.My objective is to calculate the Total sum for all those values,i.e,
Total = SUM_for_x + SUM_for_y + SUM_for_z + ....
Now,off course,I am hitting the DB for every such value,which is costly.Can i optimize this in 1 single query which does the job for me,hitting the DB just once ?
Assuming that you are only interested in the total sum and not in the partial sums and the conditions are mutually exclusive, you can do this:
SELECT SUM(some_column)
FROM (table)
WHERE (a)
OR (b)
OR (c)
OR ...
An other way (if your conditions are not mutually exclusive) would be:
SELECT SUM(some_column)
FROM(SELECT SUM(some_column) AS some_column FROM (table) WHERE (a) UNION ALL
SELECT SUM(some_column) AS some_column FROM (table) WHERE (b) UNION ALL
SELECT SUM(some_column) AS some_column FROM (table) WHERE (c) -- UNION ALL a.s.o.
)
SELECT SUM(case when x then a end) sum_a,
SUM(case when y then b end) sum_b,
SUM(case when z then c end) sum_c
FROM <table>
and sum up in Java
or get total in SQL:
SELECT SUM(case when x then a end) sum_a +
SUM(case when y then b end) sum_b +
SUM(case when z then c end) sum_c
FROM <table>

Union of two tables but show which table the data came from

I have two tables:
TABLE_A TABLE_B
Fields: Trans Amend Trans Amend
data: 100 0 100 0
100 1
110 0
120 0
120 1
130 0 130 0
130 1
140 0 140 0
150 0 150 0
150 1 150 1
150 2
What I want is a table (view) that will combine (union) these to tables but will only show the highest Amend for each Trans
Looking for this as the answer:
Fields: Trans Amend
data: 100 1
110 0
120 1
130 1
140 0
150 2
Then to make it harder, I would like to know if there is a way I can tell from which table the data is coming from. Table A always wins when Record A and Record B are equal
Looking for this as the answer:
Fields: Trans Amend WhichTBL
data: 100 1 Table_A
110 0 Table_A
120 1 Table_B
130 1 Table_B
140 0 Table_A
150 2 Table_A
I know a UNION can't be done to get this result.
What if you added a string in your select and aliased it as a column?
SELECT Trans, Amend, 'Table_A' as WhichTBL
FROM (your 1st select query)
UNION
SELECT Trans, Amend, 'Table_B' as WhichTBL
FROM (your 2nd select query)
ORDER BY Trans
In Teradata SQL you would do the following, not sure about SQL Server:
select trans,amend,WhichTBL from
(
select trans,amend,'Table_A' WhichTBL from Table_A
union
select trans,amend,'Table_B' WhichTBL from Table_B
) X
qualify row_number() over(partition by trans order by amend desc, WhichTBL) = 1
order by trans;
A version using Lucero's suggestion if your SQL doesn't have a QUALIFY clause:
select trans,amend,WhichTBL from
(
select x.*,row_number() over(partition by trans order by amend desc, WhichTBL) as rn
(
select trans,amend,'Table_A' as WhichTBL from Table_A
union
select trans,amend,'Table_B' as WhichTBL from Table_B
) Derived1 as X
) Derived2
where rn = 1
order by trans;
would this work?
SELECT
trans, MAX(max_amend) as max_max_amend
FROM
(SELECT
'a' AS src, trans, MAX(amend) AS max_amend
FROM
table_a
GROUP BY
trans
UNION ALL
SELECT
'b' AS src, trans, MAX(amend) AS max_amend
FROM
table_b
GROUP BY
trans) m
GROUP BY
trans
Lucero's point below is correct, the min(src) would be on the global set, not the related max()
I think you'd have to combine the source and table values into one column you can max. In your example, adding 1 to the value is all you need to distinguish the sources, like:
SELECT trans, Max(amend) AS MaxOfamend, 1+[amend] AS isa, 0 AS isb
FROM TableA
GROUP BY trans
but you could add 100, or multiply by a big value, or whatever works with your data. The idea is to combine the two pieces of information, the amend value and the source, into one column.
Then, after the information is combined, you get the max of that value, then strip off the source flag by uncombining them (subtracting 1, dividing by 100, whatever)
OK, here's what I got:
CREATE VIEW [dbo].[viewA] AS
SELECT trans, MAX(amend + .20) AS srcIsA, 0 AS srcIsb
FROM dbo.tableA
GROUP BY trans
CREATE VIEW [dbo].[viewB] AS
SELECT trans, 0 AS srcIsA, MAX(amend + .10) AS srcIsB
FROM dbo.tableB
GROUP BY trans
CREATE VIEW [dbo].[viewU] AS
SELECT * from viewA
union all
select *
FROM viewb
CREATE VIEW [dbo].[viewv] AS
SELECT trans, srcIsA, srcIsb, srcIsA + srcIsb AS total
FROM dbo.viewU
CREATE VIEW [dbo].[vieww] AS
SELECT trans, MAX(total) AS max_total
FROM dbo.viewv
GROUP BY trans
CREATE VIEW [dbo].[viewx] AS
SELECT trans,
max_total,
CAST(max_total AS int) AS maxval,
CASE WHEN (max_total - CAST(max_total AS int)) = .1 THEN 'a' ELSE 'b' END AS src
FROM dbo.vieww
I want to offer that you can do this with a join and aggregation, using standard SQL:
select coalesce(a.trans, b.trans) as trans,
(case when coalesce(max(b.amend), -1) > coalesce(max(a.amend), -1)
then max(b.amend)
else max(a.amend)
end) as amend,
(case when coalesce(max(b.amend), -1) > coalesce(max(a.amend) , -1)
then 'B' else 'A'
end) as whichTable
from Table_A a full outer join
Table_B b
on a.trans = b.trans
group by coalesce(a.trans, b.trans)
If Amend only has value 1 and 0
then the first question can be done
select Trans,sum(Amend) AmendMax from (select Trans,Amend from TABLE_A
union select Trans,Amend from TABLE_B) C group by Trans
the secound question would be
select Trans,max(Amend) Amend,case when sum(s)=1 or sum(s)=2 or sum(s)=21
then 'Table-A' when sum(s)=10 or sum(s)=12 or sum(s)=20 then 'Table-B'
when sum(s)=11 or sum(s)=22 then 'Table-A and B' end s from
(select case when max(Amend)=1 then 1 else 2 end s,Trans,max(Amend) Amend from TABLE_A
group by Trans union select case when max(Amend)=1
then 10 else 20 end s,Trans,max(Amend) Amend from TABLE_B group by Trans) C group by Trans