Sql Union or Join or both together - mysql

I have 2 tables AmountIn and AmountOut.
The first table Amountin looks like :
AmountIn
+--------+--------------+-----------+
| account| date | AmountIn |
+--------+--------------+-----------+
| A | 2017/2/6 | 200 |
| A | 2017/2/5 | 100 |
| A | 2017/2/5 | 500 |
| B | 2017/2/1 | 1000 |
| B | 2017/2/1 | 2000 |
| C | 2017/1/20 | 25 |
+--------+----+---------+-----------+
And the second one looks like:
AmountOut
+--------+--------------+-----------+
| account| date |AmountOut |
+--------+--------------+-----------+
| A | 2017/2/8 | 200 |
| A | 2017/2/7 | 100 |
| A | 2017/2/6 | 500 |
| B | 2017/2/2 | 1000 |
| B | 2017/2/1 | 2000 |
| C | 2017/1/20 | 25 |
+--------+----+---------+-----------+
Now I want a query that will display result as follow:
ForAccountA
+--------+--------------+----------+-----------+------------+
| account| date | AmountIn | AmountOut | Balancy |
+--------+--------------+-------- -+-----------+------------+
| A | 2017/2/5 | 500 | 0 | 500 |
| A | 2017/2/5 | 100 | 0 | 600 |
| A | 2017/2/6 | 0 | 500 | 100 |
| A | 2017/2/6 | 200 | 0 | 300 |
| A | 2017/2/7 | 0 | 100 | 200 |
| A | 2017/2/8 | 0 | 200 | 0 |
+--------+----+---------+----------+-----------+------------+
date field in the query is an union of date in both tables and the balancy is calculated as :
last balance + AmountIn - AmounOut

Try this:
select
t.*,
#sum := if(#account = account,
#sum + AmountIn - AmountOut,
if((#account := account) is not null,
AmountIn - AmountOut, 0)
) balance
from (
select
*
from (
select
1 x,
account,
date,
AmountIn,
0 AmountOut
from AmountIn
union all
select
0 x,
account,
date,
0 AmountIn,
AmountOut
from AmountOut
) t order by account, date, x
) t cross join (select #account := null, #sum := 0) t2
EDIT:
For three tables:
select
t.*,
#sum := if(#account = account,
#sum + amountOne + amountTwo - amountThree,
if((#account := account) is not null,
amountOne + amountTwo - amountThree, 0)
) balance
from (
select
*
from (
select
2 x, account, date, amount amountOne,
0 amountTwo, 0 amountThree
from table1
union all
select
1 x, account, date, 0 amountOne,
amount amountTwo, 0 amountThree
from table2
union all
select
0 x, account, date, 0 amountOne,
0 amountTwo, amount amountThree
from table3
) t order by account, date, x
) t cross join (select #account := null, #sum := 0) t2

MSSQL:
select ai.account,
ai.date,ai.AmountIn,
case when AmountIn is NULL then 0
else 0
end as AmontOut
from AmountIn as ai
where ai.account = 'A'
union
select ao.account,
ao.date,
case
when AmountOut is NULL then 0
else 0
end as AmountOut,
ao.AmountOut
from AmountOut as ao
where ao.account = 'A'
this returns your desired table without column balancy. Maybe someone help you out with this column

Select
case when a.account is not null
then a.account else b.account
end as Account,
case when a.account is not null then a.date
else b.date as date,
a.AmountIn, b.AmountIn, (a.AmountIn - b.AmountOut) as balance
from AmountIn
a left join ForAccontA on a.account = b.account where a.account = 'A' and
b.account = 'A'
Hi I would like to solve this query like this rather redundant code using union

Related

Optimize a query for calcilating datetime difference

I have a SQL table:
+---------+----------+---------------------+---------------------+---------+
| id | party_id | begintime | endtime | to_meas |
+---------+----------+---------------------+---------------------+---------+
| 1395035 | 9255 | 2010-09-26 00:34:02 | 2010-09-26 03:56:20 | 0 |
| 1395036 | 8974 | 2009-07-10 11:00:00 | 2009-07-10 21:30:00 | 0 |
| 1395037 | 8974 | 2009-07-10 23:14:00 | 2009-07-11 08:48:00 | 0 |
| 1395038 | 8975 | 2009-07-10 11:00:00 | 2009-07-10 21:30:00 | 0 |
| 1395039 | 8975 | 2009-07-10 23:14:00 | 2009-07-11 08:48:00 | 0 |
| 1395040 | 8974 | 2009-07-11 10:08:31 | 2009-07-12 18:49:51 | 0 |
| 1395041 | 8975 | 2009-07-11 10:08:31 | 2009-07-12 18:49:51 | 0 |
| 1395042 | 8974 | 2009-07-12 20:38:27 | 2009-07-13 20:33:21 | 0 |
| 1395043 | 8975 | 2009-07-12 20:38:27 | 2009-07-13 20:33:21 | 0 |
| 1395044 | 8974 | 2009-07-13 21:57:37 | 2009-07-15 08:25:45 | 0 |
| 1395045 | 8975 | 2009-07-13 21:57:37 | 2009-07-15 08:25:45 | 0 |
| 1395046 | 8974 | 2009-07-15 08:51:25 | 2009-07-16 10:29:13 | 0 |
| 1395047 | 8975 | 2009-07-15 08:51:25 | 2009-07-16 10:29:13 | 0 |
| 1395048 | 8974 | 2009-07-16 12:22:22 | 2009-07-17 14:39:10 | 0 |
| 1395049 | 8975 | 2009-07-16 12:22:22 | 2009-07-17 14:39:10 | 0 |
| 1395050 | 8976 | 2009-07-24 16:53:48 | 2009-07-25 08:47:29 | 0 |
| 1395051 | 8977 | 2009-07-24 16:53:48 | 2009-07-25 08:47:29 | 0 |
| 1395052 | 8978 | 2009-07-24 16:53:48 | 2009-07-25 08:47:29 | 0 |
| 1395053 | 8979 | 2009-07-24 16:53:48 | 2009-07-25 08:47:29 | 0 |
| 1395054 | 8976 | 2009-07-25 10:47:14 | 2009-07-26 09:41:44 | 0 |
+---------+----------+---------------------+---------------------+---------+
...
I need to calculate time between begintime and previous endtime and set to_meas to 1 if this difference is > 30 minutes. Here is my attempt to do it in MySQL:
update doses d set to_meas=1 where d.id in
(select a.id from party join (select * from doses) a
on party_id=a.party_id
left join (select * from doses) b
on party.id=b.party_id
and b.begintime=(select min(begintime)
from (select * from doses) c
where c.begintime > a.endtime)
and timestampdiff(minute, a.endtime, b.begintime) > 30
group by party.id);
This command runs (quasi-) forever. I've tried to do it in python's pandas:
conn = engine.connect()
sql =
'''
select doses.id, party_id, party.ml, begintime, endtime
from doses join party on party.id=doses.party_id
'''
df = pd.read_sql(con=conn, sql=sql,
measure = df.groupby('party_id', as_index=False).apply(
lambda x: x[pd.to_datetime(x['begintime']) -
pd.to_datetime(x.shift()['endtime']) > pd.to_timedelta('30 minutes')])
measure_ids = measure['id'].to_list()
measure_list = ','.join([str(x) for x in measure_ids])
conn.execute(
'update doses set to_meas=true where id in(%s)' % measure_list)
The last statement runs about 10 seconds. Is there a way to optimize SQL code for running as fast as the pandas` one?
In MySQL 8.0, you can get select the result you want with window functions, like so:
select d.*,
(begintime > lag(endtime) over(partition by pary_id order by endtime) + interval 30 minute) as to_meas
from doses d
In earlier versions:
select d.*,
(
begintime > (
select max(endtime) + interval 30 minute
from doses d1
where d1.party_id = d.party_id and d1.endtime < d.endtime
)
) as to_meas
from doses d
I would not recommend storing such derived information. You can use the query, or create a view. But if you really insist on an update:
update doses d
inner join (
select id,
(
begintime > (
select max(endtime) + interval 30 minute
from doses d1
where d1.party_id = d.party_id and d1.endtime < d.endtime
)
) as to_meas
from doses d
) d1 on d1.id = d.id
set d.to_meas = d1.to_meas
You can update your data using exists as follows:
Update doses d
Set meas = 1
Where begintime > (select max(dd.endtime) + interval '30' minute
From doses dd where dd.begintime < d.begintime
And dd.party_id = d.party_id)
If you want to update the data, you can use window functions in the update:
update doses d join
(select d.*,
lag(d.endtime) over (partition by d.party_id order by d.endtime) as prev_endtime
from doses d
) dd
on d.id = dd.id and
d.starttime > dd.prev_endtime + interval 30 minute
set to_meas = 1;
Then, for this query, you want an index on doses(party_id, endtime). I assume that id is already declared as a primary key.
Note: With this index, you might find it faster simply to calculate the value on the fly rather than storing it in the table.
EDIT:
In older versions of MySQL, you can phrase this as:
update doses d join
(select d.*,
(select d2.endtime
from doses d2
where d2.party_id = d.party_id and
d2.endtime < d.endtime
) as prev_endtime
from doses d
) dd
on d.id = dd.id and
d.starttime > dd.prev_endtime + interval 30 minute
set to_meas = 1;
You have relatively few rows per party_id so a correlated query seems reasonable. This also needs an index on (party_id, endtime).

Is there a way to UPDATE column values based another column's value?

Part 1 of my SQL task involves restructuring data. The jist of my task is as follows: Based on the event_type, if it is "begin" I am trying to use that "time" to find it's stopping time (in another row) and add it to a column (event_end) on the same row as the start time so that all the data for an event sits nicely in one row.
pID customerID locationID event_type time event_end (new row)
1 1 a begin 12.45
2 2 a begin 11.10
3 1 a stop 1.30
4 2 b begin 9.45
5 3 b stop 8.78
I would like to add another column (event_end), and have event_end = the minimum value of event_start IF event_start = 'stop', IF locationID = locationID, and IF customerID = customerID. The final step would be to delete all event_start 'begin' rows.
I have tried UPDATE SET WHERE sequences, and a little bit of CASE, but my issue is that I cannot wrap my head around how to perform this without a loop like VBA. The following is my best stab at it:
UPDATE table
SET event_end = MIN(time)
WHERE event_type = 'stop'
WHERE customerid = customerid
WHERE locationid = locationid
WHERE time > time
SELECT *
FROM table
I'm hoping to have a table with all event data in one row, not spread out over multiple rows. If this is a handful, I appologize but am thankful in advance.
Thanks
Problem Statement:
Add event_end as an extra attribute to the existing row, data will be populated based on customer_id, location_id.
We will populate data in event_end to all events which have event type as begin
Data would be picked from rows which have the same customer_id, location_id but event type as stop.
Finally, we will remove all events with type stop.
Solution: Consider your table name is customer_events and will use self join concept for the same.
First, identify which records needs to be updated. We can use a SELECT query to identify such records.
c1 table will represent rows with begin event type.
c2 table will represent rows with stop event type.
SELECT *
FROM customer_events c1
LEFT JOIN customer_events c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.event_type = 'begin' AND c2.event_type = 'stop'
WHERE c1.event_type = 'begin'; -- As we want to populate data in events with value as `begin`
Write a query to update the records.
UPDATE customer_events c1
LEFT JOIN customer_events c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.event_type = 'begin' AND c2.event_type = 'stop'
SET c1.event_end = c2.`time`
WHERE c1.event_type = 'begin';
Now every record with event type as begin has either value in event_end column or it would be null if no records match as stop event.
For rows with event type as stop, either they are mapped with some row with event type as begin or some are not mapped. In both cases, we don't want to keep them. To remove all records with event type as stop.
DELETE FROM customer_events
WHERE event_type = 'stop';
Note: Don't run DELETE statement unless you are sure that this solution will work for you.
Updated: We can have multiple records of begin & stop events for single customer & location.
Sample Input:
| pID | customerID* | *locationID* | *event_type* | *time* | *event_end* |
| 1 | 1 | a | begin | 02:45:00 | |
| 2 | 2 | a | begin | 03:10:00 | |
| 3 | 1 | b | begin | 04:30:00 | |
| 4 | 2 | b | begin | 05:45:00 | |
| 5 | 2 | a | stop | 06:49:59 | |
| 6 | 1 | a | begin | 07:38:00 | |
| 7 | 3 | b | begin | 08:57:19 | |
| 8 | 2 | b | stop | 09:57:43 | |
| 9 | 3 | b | stop | 10:58:03 | |
| 10 | 4 | a | begin | 11:58:34 | |
| 11 | 1 | a | stop | 12:09:36 | |
| 12 | 1 | b | stop | 13:09:50 | |
| 13 | 1 | a | stop | 14:10:02 | |
Query:
SELECT *
FROM (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'begin'
ORDER BY customerId, locationID, `time`) AS c1
LEFT JOIN (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'stop'
ORDER BY customerId, locationID, `time`
) AS c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.rank = c2.rank;
Output:
| pId | customerID| locationId| event_type| Start_Time|End_Id| End_Time |
| 1 | 1 | a | begin | 02:45:00 | 11 | 12:09:36 |
| 6 | 1 | a | begin | 07:38:00 | 13 | 14:10:02 |
| 3 | 1 | b | begin | 04:30:00 | 12 | 13:09:50 |
| 2 | 2 | a | begin | 03:10:00 | 5 | 06:49:59 |
| 4 | 2 | b | begin | 05:45:00 | 8 | 09:57:43 |
| 7 | 3 | b | begin | 08:57:19 | 9 | 10:58:03 |
| 10 | 4 | a | begin | 11:58:34 | | |
Update Statement: Create two columns end_pID and event_end for migration.
UPDATE customer_events
INNER JOIN (
SELECT c1.pId, c2.pID End_Id, c2.time AS End_Time
FROM (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'begin'
ORDER BY customerId, locationID, `time`) AS c1
LEFT JOIN (
SELECT
ce.*,
IF(#c_id <> ce.customerId OR #l_id <> ce.locationID, #rank:= 1, #rank:= #rank + 1 ) as rank,
#c_id:= ce.customerId,
#l_id:= ce.locationID
FROM customer_events ce,
(SELECT #c_id:= 0 c, #l_id:= '' l, #rank:= 0 r) AS t
WHERE event_type = 'stop'
ORDER BY customerId, locationID, `time`
) AS c2 ON c1.customerID = c2.customerID AND c1.locationID = c2.locationID AND c1.rank = c2.rank) AS tt ON customer_events.pID = tt.pId
SET customer_events.end_pID = t.End_Id, customer_events.event_end = t.End_Time;
Finally, remove all events with event_type = 'stop'

How to subtract column's old value from the new one in mysql

My question is same as this one on SO, but difference is what should we do if random incremented id not continues.
QUERY
create table sales_table (no int, date varchar(10), sales int);
insert into sales_table values (1, '1-Jan', 10000),
(3, '3-Jan', 12500),
(5, '4-Jan', 8000);
EXPECTED RESULT
+-------+------------+--------------+-------------+
| no | date | sales | diff |
+-------+------------+--------------+-------------+
| 1 | 1-Jan | 10,000 | 0 |
| 3 | 3-Jan | 12,500 | 2500 |
| 5 | 4-Jan | 8,000 | -4500 |
+-------+------------+--------------+-------------+
Why a subquery?
SELECT
no,
date,
IF(#sales = 0, 0, sales - #sales) diff,
#sales := sales AS sales
FROM
sales_table, (SELECT #sales := 0) x
ORDER BY no;
Output:
+------+-------+-------+-------+
| no | date | diff | sales |
+------+-------+-------+-------+
| 1 | 1-Jan | 0 | 10000 |
| 3 | 3-Jan | 2500 | 12500 |
| 5 | 4-Jan | -4500 | 8000 |
+------+-------+-------+-------+
you can use user defined variable to store the previous row value of sales
SELECT
t1.no,
t1.date,
t1.sales,
t1.diff
FROM (
SELECT
*,
CASE WHEN #rownum = 0 THEN 0 ELSE s.sales - #prev END AS diff,
#prev:=s.sales,
#rownum:= #rownum +1 rownum
FROM sales_table s
CROSS JOIN (SELECT #prev:=0,#rownum:=0) t
ORDER BY s.no
) t1
Demo
QUERY
SELECT
curr.no,
curr.date,
curr.sales,
COALESCE(IFNULL(curr.sales, 0) - next.sales,0) AS Diff
FROM sales_table AS curr
LEFT JOIN sales_table AS next
ON next.no =
(SELECT MAX(no)
FROM sales_table
WHERE no < curr.no)
FIND FIDDLE HERE

MySQL: time between rows

I'm new to MySQL and I'm trying to figure out how to calculate the time passed between the rows in a log table.
The table is a basic table with ID , Hostname , Info , Timestamp, with data like:
+---+----------+-------------------+---------------------+
|ID | Hostname | Info | Timestamp |
+---+----------+-------------------+---------------------+
|445| switch1 | "port 1 inserted" | 2013-01-19 19:51:40 |
|446| switch1 | "port 2 inserted" | 2013-01-19 19:59:41 |
|447| Router2 | "alarm fan speed" | 2013-01-19 20:00:40 |
|448| switch1 | "alarm fan speed" | 2013-01-19 20:12:20 |
|449| Router2 | "alarm fan speed" | 2013-01-19 21:42:41 |
+---+----------+-------------------+---------------------+
So basically I want to get the time difference between the rows with the same HOSTNAME, in this case between row 445 and 446 it would result in 8 minutes 1 second. And between 446 and 448 it would result in 12 minutes and 39 seconds . And so on...
Any tips on this would be greatly appreciated.
This will give you the time difference in seconds between rows:
SELECT c.info,
CASE
WHEN f.`timestamp` IS NOT NULL THEN
Timestampdiff(second, f.`timestamp`,
c.`timestamp`)
ELSE NULL
end AS time_diff
FROM (SELECT #rowa := #rowa + 1 AS id,
a.hostname,
a.info,
a.`timestamp`
FROM sparkles a
JOIN (SELECT #rowa := 0) b
WHERE a.hostname = 'switch1') c
LEFT JOIN (SELECT #rowb := #rowb + 1 AS id,
d.hostname,
d.info,
d.`timestamp`
FROM sparkles d
JOIN (SELECT #rowb := 0) e
WHERE d.hostname = 'switch1') f
ON f.id = c.id - 1
Result (for switch1 as the hostname)
| INFO | TIME_DIFF |
-------------------------------
| port 1 inserted | (null) |
| port 2 inserted | 481 |
| alarm fan speed | 759 |
See the demo
Result (for Router2 as the hostname)
| INFO | TIME_DIFF |
-------------------------------
| alarm fan speed | (null) |
| alarm fan speed | 6121 |
See the demo
Try this:
SELECT id, IF(#lastdate = '', NULL, TIMESTAMPDIFF(SECOND, TIMESTAMP, #lastdate)),
#lastdate:=TIMESTAMP
FROM tablename, (SELECT #lastdate:='') a;

mysql wrong results on big query

Please help me to fix mysql query and get correct results...
Please see dataset for tables as following...
students
| id | name | batch | discount | open_bal | inactive |
+----+-------+-------+----------+----------+----------+
| 1 | Ash | 19 | 0 | -5000 | 0 |
+----+-------+-------+----------+----------+----------+
| 2 | Tuh | 15 | 0 | 0 | 0 |
+----+-------+-------+----------+----------+----------+
invoices
| id | invoice_num | student_id | reg_fee | tut_fee | other_fee | discount |
+------+-------------+------------+---------+---------+-----------+----------+
| 1 | 2011/1 | 1 | 5000 | 0 | 0 | 0 |
+------+-------------+------------+---------+---------+-----------+----------+
| 137 | 2011/137 | 1 | 15000 | 0 | 0 | 0 |
+------+-------------+------------+---------+---------+-----------+----------+
| 169 | 2011/169 | 2 | 15000 | 0 | 0 | 0 |
+------+-------------+------------+---------+---------+-----------+----------+
recipts
| id | recipt_num | student_id | reg_fee | tut_fee | other_fee | status |
+------+-------------+------------+---------+---------+-----------+------------+
| 264 | 2011/264 | 1 | 0 | 15000 | 0 | confirmed |
+------+-------------+------------+---------+---------+-----------+------------+
| 18 | 2011/18 | 2 | 0 | 5250 | 0 | confirmed |
+------+-------------+------------+---------+---------+-----------+------------+
| 251 | 2011/251 | 2 | 4650 | 0 | 0 | pending |
+------+-------------+------------+---------+---------+-----------+------------+
batches
| id | name |
+-----+----------+
| 19 | S.T-11 |
+-----+----------+
| 15 | Mc/11-13 |
+-----+----------+
I want to achieve report according to batches....
Batch id - batch id from batches table
Batch Name - batch name from batches table
Total Students - count(s.id) from students table group by batch
Opening Bal - sum(s.openbal) from students table
Gross Fee - sum(reg_fee+tut_fee+other_fee) from invoices table
Discount - sum(i.discount) from invoices table
Net Payable - (openbal + grossfee) - discount
Net Received - sum(reg_fee+tut_fee+other_fee) from recipts table where r.status = 'confirmed'
Due Balance - Net Payable - Net Received
expected report
| batch_id | batch_name | total_students | opening_bal | gross_fee | discount | net_payable | net_recieved | due_balance |
+----------+------------+----------------+-------------+-----------+----------+-------------+--------------+-------------+
| 15 | 2011/264 | 1 | 0 | 15000 | 0 | 15000 | 5250 | 9750 |
+----------+------------+----------------+-------------+-----------+----------+-------------+--------------+-------------+
| 19 | S.T-11 | 1 | -5000 | 20000 | 0 | 15000 | 15000 | 0 |
+----------+------------+----------------+-------------+-----------+----------+-------------+--------------+-------------+
I have tried using following query but its giving wrong results.
SELECT b.name AS batch_name,
b.id AS batch_id,
COUNT( s.id ) AS total_students,
COALESCE( s.open_bal, 0 ) AS open_balance,
COALESCE( sum( i.reg_fee + i.tut_fee + i.other_fee ) , 0 ) AS gross_fee,
COALESCE( s.discount, 0 ) ,
COALESCE( sum( i.reg_fee + i.tut_fee + i.other_fee ) , 0 ) -
COALESCE( s.discount, 0 ) AS net_payable,
COALESCE( sum( r.reg_fee + r.tut_fee + r.other_fee ) , 0 ) AS net_recieved,
COALESCE( s.discount, 0 ) ,
COALESCE( sum( i.reg_fee + i.tut_fee + i.other_fee ) , 0 ) -
COALESCE( s.discount, 0 ) -
COALESCE( sum( r.reg_fee + r.tut_fee + r.other_fee ) , 0 )
AS due_balance
FROM batches b
LEFT JOIN students s ON s.batch = b.id
LEFT JOIN invoices i ON i.student_id = s.id
LEFT JOIN recipts r ON r.student_id = s.id
WHERE s.inactive =0 and r.status = 'confirmed'
GROUP BY b.name;
please help me to rewrite this query...
Talking about SQL this line is quite certainly wrong:
GROUP BY b.name;
The GROUP BY should contain every element of the select which is not an aggregate expression.
Try the query using:
GROUP BY b.name,b.id,COALESCE(s.open_bal,0), COALESCE(s.discount,0);
When you do not make the right GROUP BY expression MySQL makes his own improved and simplified group by, which avoids a query rejection but produce higly unexpectable results, especially if your query is complex.
If you do not need a distinct result row for each s.open_bal and s.discount, then maybe you do not need theses (duplicates) data in the select.
Then I did not took the time to analyze the complete query. But your needs seems quite complex. I would say Divide and conquer, KISS (Keep It Stupid Simple), make several queries you fully understand instead of one huge query. Especially if requirements from some of the results differs (some working on details, some working on aggregates, and some working on different aggregates, etc), as you would maybe need some window functions ("partition by" keyword) that you do not have on MySQL.
maybe you should try to fix your sum like this example:
COALESCE( sum( i.reg_fee + i.tut_fee + i.other_fee ) , 0 ) //bad
sum( COALESCE(i.reg_fee,0) + COALESCE(i.tut_fee,0) + COALESCE(i.other_fee,0) ) //good