complex MySQL query wrong results - mysql

I am trying to build complex mysql query but its returning wrong results...
SELECT
b.name AS batch_name,
b.id AS batch_id,
COUNT(DISTINCT s.id)
AS total_students,
COALESCE( SUM(s.open_bal), 0 )
AS open_balance,
SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
) AS gross_fee,
SUM( COALESCE(i.discount, 0) )
AS discount,
COALESCE( SUM(s.open_bal), 0 )
+ SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
)
- SUM( COALESCE(i.discount, 0) )
AS net_payable,
SUM( COALESCE(r.reg_fee, 0)
+ COALESCE(r.tut_fee, 0)
+ COALESCE(r.other_fee, 0)
) AS net_recieved,
( COALESCE( SUM(s.open_bal), 0 )
+ SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
)
- SUM( COALESCE(i.discount, 0) )
)
- ( SUM( COALESCE(r.reg_fee, 0)
+ COALESCE(r.tut_fee, 0)
+ COALESCE(r.other_fee, 0)
)
)
AS balance_due
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
GROUP BY b.name, b.id;
Returns following results...
| batch_name | total_students | open_bal | gross_fee | discount | net_payable | net_recieved | due_balance |
+------------+-----------------+----------+-----------+----------+-------------+--------------+-------------+
| MS | 6 | 10000 | 0 | 0 | 10000 | 101000 | -91000 |
+------------+-----------------+----------+-----------+----------+-------------+--------------+-------------+
batches table
| id | name |
+-----+------+
| 9 | Ms |
+-----+------+
Students table
| id | open_bal | batch | inactive |
+-----+----------+-------+----------+
| 44 | -16000 | 9 | 0 |
+-----+----------+-------+----------+
| 182 | 9000 | 9 | 0 |
+-----+----------+-------+----------+
| 184 | -36000 | 9 | 0 |
+-----+----------+-------+----------+
| 185 | 19000 | 9 | 0 |
+-----+----------+-------+----------+
| 186 | 9000 | 9 | 0 |
+-----+----------+-------+----------+
| 187 | 4000 | 9 | 0 |
+-----+----------+-------+----------+
Invoices Table
| id | student_id | reg_fee | tut_fee | other_fee | net_payable | discount |
+------+------------+---------+---------+-----------+-------------+----------+
| | | | | | | |
+------+------------+---------+---------+-----------+-------------+----------+
No invoices are available for above students id.
Recipts table
| id | student_id | reg_fee | tut_fee | other_fee | status |
+------+------------+---------+---------+-----------+------------+
| 8 | 44 | 0 | 0 | 1500 | confirmed |
+------+------------+---------+---------+-----------+------------+
| 277 | 44 | 0 | 50000 | 0 | confirmed |
+------+------------+---------+---------+-----------+------------+
| 26 | 182 | 0 | 0 | 1500 | confirmed |
+------+------------+---------+---------+-----------+------------+
| 424 | 182 | 0 | 15000 | 0 | confirmed |
+------+------------+---------+---------+-----------+------------+
| 468 | 182 | 0 | 15000 | 0 | confirmed |
+------+------------+---------+---------+-----------+------------+
| 36 | 185 | 0 | 0 | 1500 | confirmed |
+------+------------+---------+---------+-----------+------------+
| 697 | 185 | 0 | 15000 | 0 | confirmed |
+------+------------+---------+---------+-----------+------------+
| 66 | 187 | 0 | 0 | 1500 | confirmed |
+------+------------+---------+---------+-----------+------------+
Expected results using above sql query and tables...
| batch_name | total_students | open_bal | gross_fee | discount | net_payable | net_recieved | due_balance |
+------------+-----------------+----------+-----------+----------+-------------+--------------+-------------+
| MS | 6 | -11000 | 0 | 0 | 10000 | 101000 | -112000 |
+------------+-----------------+----------+-----------+----------+-------------+--------------+-------------+

You still haven't provided full information - no batches table, even the not existing recipts table.. Anyway, I assume we don't care whats in the batches table, lets say it's just the name and id. Your receipts table have multiple rows for the same student. This should result in multiple rows returned for the other tables as well, due to all the JOINs. Therefore you SUM() multiple times values which must be summed just once, i.e. open_balance. This could be a clue as to where the problem is, I'd say you have to move the info that you need from 'the receipts table into subqueries, but I'm not sure you've shown us the entirety of your DB. Try removing the receipts table from the query and check the results again. If that's it, you should see what to do from there on or at least give more info to us.
EDIT:
The query should be:
SELECT
b.name AS batch_name,
b.id AS batch_id,
COUNT(DISTINCT s.id)
AS total_students,
COALESCE( SUM(s.open_bal), 0 )
AS open_balance,
SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
) AS gross_fee,
SUM( COALESCE(i.discount, 0) )
AS discount,
COALESCE( SUM(s.open_bal), 0 )
+ SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
)
- SUM( COALESCE(i.discount, 0) )
AS net_payable,
SUM((SELECT SUM(COALESCE(receipts.reg_fee, 0)
+ COALESCE(receipts.tut_fee, 0)
+ COALESCE(receipts.other_fee, 0)) FROM receipts WHERE receipts.student_id = s.id))
AS net_recieved,
( COALESCE( SUM(s.open_bal), 0 )
+ SUM( COALESCE(i.reg_fee, 0)
+ COALESCE(i.tut_fee, 0)
+ COALESCE(i.other_fee, 0)
)
- SUM( COALESCE(i.discount, 0) )
)
- SUM((SELECT SUM(COALESCE(receipts.reg_fee, 0)
+ COALESCE(receipts.tut_fee, 0)
+ COALESCE(receipts.other_fee, 0)) FROM receipts WHERE receipts.student_id = s.id))
AS balance_due
FROM batches b
LEFT JOIN students s ON s.batch = b.id
LEFT JOIN invoices i ON i.student_id = s.id
WHERE s.inactive = 0
GROUP BY b.name, b.id;
This will sum students data in the receipts table even if it's on more than one row, returning just one row. Removing the join to the receipts table removes duplicate lines from the other tables, so the calculations should now be correct.
One more thing - you've got s.inactive = 0 in the WHERE clause, make sure it's not relevant to this calculations.
P.S. How come you don't know what a sub query is and you end up writing stuff like that?

I have got the solution, i was joining lots of queries together and that's by some results are doubling. thanks.

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).

MySQL - Select Top 5 with Rankings

I'm trying to get a users ranking getting his highest performances in every beatmap.
I get the user highest performance in every beatmap (only taking the top 5 performances) and adding them together, but it fails when the highest performance in one beatmap is repeated... because it counts twice
I'm based in this solution, but it doesn't works well for me...
Using MySQL 5.7
What i'm doing wrong?
Fiddle
Using this code:
SET group_concat_max_len := 1000000;
SELECT #i:=#i+1 rank, x.userID, x.totalperformance FROM (SELECT r.userID, SUM(r.performance) as totalperformance
FROM
(SELECT Rankings.*
FROM Rankings INNER JOIN (
SELECT userID, GROUP_CONCAT(performance ORDER BY performance DESC) grouped_performance
FROM Rankings
GROUP BY userID) group_max
ON Rankings.userID = group_max.userID
AND FIND_IN_SET(performance, grouped_performance) <= 5
ORDER BY
Rankings.userID, Rankings.performance DESC) as r
GROUP BY userID) x
JOIN
(SELECT #i:=0) vars
ORDER BY x.totalperformance DESC
Expected result:
+------+--------+------------------+
| rank | userID | totalperformance |
+------+--------+------------------+
| 1 | 1 | 450 |
+------+--------+------------------+
| 2 | 2 | 250 |
+------+--------+------------------+
| 3 | 5 | 140 |
+------+--------+------------------+
| 4 | 3 | 50 |
+------+--------+------------------+
| 5 | 75 | 10 |
+------+--------+------------------+
| 6 | 45 | 0 | --
+------+--------+------------------+
| 7 | 70 | 0 | ----> This order is not relevant
+------+--------+------------------+
| 8 | 76 | 0 | --
+------+--------+------------------+
Actual Result:
+------+--------+------------------+
| rank | userID | totalperformance |
+------+--------+------------------+
| 1 | 1 | 520 |
+------+--------+------------------+
| 2 | 2 | 350 |
+------+--------+------------------+
| 3 | 5 | 220 |
+------+--------+------------------+
| 4 | 3 | 100 |
+------+--------+------------------+
| 5 | 75 | 10 |
+------+--------+------------------+
| 6 | 45 | 0 | --
+------+--------+------------------+
| 7 | 70 | 0 | ----> This order is not relevant
+------+--------+------------------+
| 8 | 76 | 0 | --
+------+--------+------------------+
As you have mentioned that you are picking only top 5 performances per user across beatmaps then you can try this way:
select #i:=#i+1, userid,performance from (
select userid,sum(performance) as performance from (
select
#row_number := CASE WHEN #last_category <> t1.userID THEN 1 ELSE #row_number + 1 END AS row_number,
#last_category :=t1.userid,
t1.userid,
t1.beatmapid,
t1.performance
from (
select
userid, beatmapid,
max(performance) as performance
from Rankings
group by userid, beatmapid
) t1
CROSS JOIN (SELECT #row_number := 0, #last_category := null) t2
ORDER BY t1.userID , t1.performance desc
) t3
where row_number<=5
group by userid
)
t4 join (SELECT #i := 0 ) t5
order by performance desc
Above query will not consider duplicate Performance Score and pick only top 5 performance values.
DEMO

Sql Union or Join or both together

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

Consolidate SQL query with 2 subselects with same where clause

My app uses a scores table with a locationId, scoreDateTime, score, and comment columns. Users can score a location and optionally submit comments. A small data set might look like the following:
mysql> select locationId, scoreDateTime, score, comments from scores;
+-----------------------------+-------------------------+-------+--------------------------------+
| locationId | scoreDateTime | score | comments |
+-----------------------------+-------------------------+-------+--------------------------------+
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 2016-04-17 17:30:32.899 | 3 | asdfasf |
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 2016-04-17 18:28:46.221 | 3 | |
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 2016-04-17 18:29:56.395 | 3 | safasf |
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 2016-04-17 18:32:10.358 | 3 | |
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 2016-04-17 18:49:32.262 | 3 | |
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 2016-04-17 18:50:33.693 | 3 | |
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 2016-04-17 19:13:58.456 | 3 | |
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 2016-04-17 19:28:10.435 | 3 | asdfasf |
| ChIJqZyf8O8F44kRhatfHL4GYe0 | 2016-04-17 23:20:28.857 | 3 | aasdfasfsfsd |
| ChIJqZyf8O8F44kRhatfHL4GYe0 | 2016-04-17 23:22:55.254 | 3 | asdfasfasfsafasfsfasf asdfasfd |
| ChIJqZyf8O8F44kRhatfHL4GYe0 | 2016-04-17 23:40:37.106 | 3 | |
| ChIJpbSR1a4I44kRemEzTpniis8 | 2016-04-19 11:17:41.836 | 5 | adfgadf |
| ChIJF1LAoqgI44kR5EWvRqJPUN4 | 2016-04-19 11:17:52.536 | 4 | |
+-----------------------------+-------------------------+-------+--------------------------------+
I'd like to build a single query that will get the following for each location:
a score count from the last X hours
a comment count from the last Y days
the latest scoreDateTime (or NULL) for any comments in the last Y days
My motivation is to show locations, their recent score counts, their historical comment counts, and their latest comment datetime (or null). This will give me the recent running score counts and the hotness of the comment trail.
The following query works. However, the duplicate locationId list is actually going to be much higher in production. QUESTION: I'd like to know if there is a performant way to consolidate the 2 locationId lists, a.k.a 'locationId in (...)'.
select
x.locationId, count1, count2, count3, count4, count5, IFNULL(commentCount,0) as commentCount, lastCommentDateTime
from
( select
locationId,
sum(if (score = 1, 1, 0)) count1,
sum(if (score = 2, 1, 0)) count2,
sum(if (score = 3, 1, 0)) count3,
sum(if (score = 4, 1, 0)) count4,
sum(if (score = 5, 1, 0)) count5
from
scores
where
scoreDateTime > '2016-04-16 21:38:51.843' and
locationId in (
'ChIJqZyf8O8F44kRbNWHQkDkpGQ',
'ChIJqZyf8O8F44kRhatfHL4GYe0',
'ChIJCes00a4I44kRKG8zB4KvYTM',
'ChIJP-eRLq8I44kRKU6VOpTXqTM',
'ChIJpbSR1a4I44kRemEzTpniis8',
'ChIJF1LAoqgI44kRip2l7rjO2g4',
'ChIJF1LAoqgI44kR5EWvRqJPUN4',
'ChIJF1LAoqgI44kRRD_ZvPUmrGA',
'ChIJjweq4h0G44kRWoCPQKPdrPM',
'ChIJf2tVDB4G44kRTYjhl3sjm8M',
'ChIJ_Vg4giEG44kRq2nvtjEn8yA',
'ChIJP00qFSMG44kRyKcy2f_S12o'
)
group by locationId
) as x
left join
( select
locationId,
count(comments) as commentCount,
max(scoreDateTime) as lastCommentDateTime
from
scores
where
comments != "" and
scoreDateTime > '2016-01-16 00:00:00.000' and
locationId in (
'ChIJqZyf8O8F44kRbNWHQkDkpGQ',
'ChIJqZyf8O8F44kRhatfHL4GYe0',
'ChIJCes00a4I44kRKG8zB4KvYTM',
'ChIJP-eRLq8I44kRKU6VOpTXqTM',
'ChIJpbSR1a4I44kRemEzTpniis8',
'ChIJF1LAoqgI44kRip2l7rjO2g4',
'ChIJF1LAoqgI44kR5EWvRqJPUN4',
'ChIJF1LAoqgI44kRRD_ZvPUmrGA',
'ChIJjweq4h0G44kRWoCPQKPdrPM',
'ChIJf2tVDB4G44kRTYjhl3sjm8M',
'ChIJ_Vg4giEG44kRq2nvtjEn8yA',
'ChIJP00qFSMG44kRyKcy2f_S12o'
)
group by locationId
) as y
on x.locationId = y.locationId;
The results look like the following:
mysql> source ../../query3.sql
+-----------------------------+--------+--------+--------+--------+--------+--------------+-------------------------+
| locationId | count1 | count2 | count3 | count4 | count5 | commentCount | lastCommentDateTime |
+-----------------------------+--------+--------+--------+--------+--------+--------------+-------------------------+
| ChIJF1LAoqgI44kR5EWvRqJPUN4 | 0 | 0 | 0 | 1 | 0 | 0 | NULL |
| ChIJpbSR1a4I44kRemEzTpniis8 | 0 | 0 | 0 | 0 | 1 | 1 | 2016-04-19 11:17:41.836 |
| ChIJqZyf8O8F44kRbNWHQkDkpGQ | 0 | 0 | 8 | 0 | 0 | 3 | 2016-04-17 19:28:10.435 |
| ChIJqZyf8O8F44kRhatfHL4GYe0 | 0 | 0 | 3 | 0 | 0 | 2 | 2016-04-17 23:22:55.254 |
+-----------------------------+--------+--------+--------+--------+--------+--------------+-------------------------+
It looks like the difference between your 2 queries are the scoreDateTime and comments criteria. One way to combine your queries is by moving these conditions to your select using conditional aggregation.
Also, mysql evaluates booleans to 1 or 0, so you can simplify your sum calls by removing your if statements.
select
locationId,
sum(score = 1 and scoreDateTime > '2016-04-16 21:38:51.843') count1,
sum(score = 2 and scoreDateTime > '2016-04-16 21:38:51.843') count2,
sum(score = 3 and scoreDateTime > '2016-04-16 21:38:51.843') count3,
sum(score = 4 and scoreDateTime > '2016-04-16 21:38:51.843') count4,
sum(score = 5 and scoreDateTime > '2016-04-16 21:38:51.843') count5,
sum(comments != "") commentCount,
max(case when comments != "" then scoreDateTime end) as lastCommentDateTime
from
scores
where
scoreDateTime > '2016-01-16 00:00:00.000' and
locationId in (
'ChIJqZyf8O8F44kRbNWHQkDkpGQ',
'ChIJqZyf8O8F44kRhatfHL4GYe0',
'ChIJCes00a4I44kRKG8zB4KvYTM',
'ChIJP-eRLq8I44kRKU6VOpTXqTM',
'ChIJpbSR1a4I44kRemEzTpniis8',
'ChIJF1LAoqgI44kRip2l7rjO2g4',
'ChIJF1LAoqgI44kR5EWvRqJPUN4',
'ChIJF1LAoqgI44kRRD_ZvPUmrGA',
'ChIJjweq4h0G44kRWoCPQKPdrPM',
'ChIJf2tVDB4G44kRTYjhl3sjm8M',
'ChIJ_Vg4giEG44kRq2nvtjEn8yA',
'ChIJP00qFSMG44kRyKcy2f_S12o'
)
group by locationId
This query can take advantage of a composite index on (locationId, scoreDateTime)

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