how to get records and avoid subquery - mysql

Hi I want to avoid sub query because it working perfect for right now but it will slow in future.
SELECT
u.`id` ,
u.first_name,
u.last_name,
u.username,
( SELECT COUNT(*) FROM `complaint` AS p_c WHERE p_c.landlord_id = u.`id` AND p_c.complaint_id = 1 ) AS party_complaints,
( SELECT COUNT(*) FROM `complaint` AS r_c WHERE r_c.landlord_id = u.`id` AND r_c.complaint_id = 2 ) AS robery_complaints,
( SELECT COUNT(*) FROM `complaint` AS f_c WHERE f_c.landlord_id = u.`id` AND f_c.complaint_id = 3 ) AS fight_complaints,
( SELECT COUNT(*) FROM `complaint` AS o_c WHERE o_c.landlord_id = u.`id` AND o_c.complaint_id = 4 ) AS other_complaints,
COUNT(c.`id`) AS total_complaints_count
FROM
`user` AS u
INNER JOIN complaint AS c
ON c.`landlord_id` = u.`id`
GROUP BY u.`id`
id first_name last_name username party_complaints robery_complaints fight_complaints other_complaints total_complaints_count
------ ---------- --------- --------- ---------------- ----------------- ---------------- ---------------- ------------------------
3591 John Doe thefeature 0 13 3 2 18
4607 John Cena 10Fe416l 2 1 0 1 4

You could use a sum on case
SELECT
u.`id` ,
u.first_name,
u.last_name,
u.username,
sum( case when c.landlord_id = u.`id` and c.complaint_id = 1 then 1 else 0 end) party_complaints,
sum( case when c.landlord_id = u.`id` and c.complaint_id = 2 then 1 else 0 end) robery_complaints,
sum( case when c.landlord_id = u.`id` and c.complaint_id = 3 then 1 else 0 end) fight_complaints,
sum( case when c.landlord_id = u.`id` and c.complaint_id = 4 then 1 else 0 end) other_complaints
COUNT(c.`id`) AS total_complaints_count
FROM
`user` AS u
INNER JOIN complaint AS c
ON c.`landlord_id` = u.`id`
GROUP BY u.`id`

Related

[HY000][1111] Invalid use of group function

I have searched a lot ,but none of other questions with error 1111 solves my problem.
My needs are to count the distinct phone number of some id
The following code works:
SELECT
a.id_borrow_application,
count(DISTINCT c.phone_no) CVG_CALL_OUT_COUNTS_6M
FROM t_snow_borrow_application_id a
JOIN t_snow_call_mobile b
JOIN t_snow_call_record_201612 c ON
(
a.id_borrow_application = b.id_borrow_application
AND b.id = c.id_call_mobile
)
WHERE c.call_type = 0
GROUP BY a.id_borrow_application;
But when I want to write 4 similar queries together,the error in title
happens.
[HY000][1111] Invalid use of group function
SELECT
a.id_borrow_application,
sum(CASE WHEN call_type = 0
THEN count(DISTINCT c.phone_no)
ELSE 0 END) CVG_CALL_OUT_COUNTS_6M,
sum(CASE WHEN call_type = 0 AND c.days <= 30
THEN count(DISTINCT c.phone_no)
ELSE 0 END) CVG_CALL_OUT_COUNTS_1M,
sum(CASE WHEN call_type = 1
THEN count(DISTINCT c.phone_no)
ELSE 0 END) CVG_CALL_IN_COUNTS_6M,
sum(CASE WHEN call_type = 1 AND c.days <= 30
THEN count(DISTINCT c.phone_no)
ELSE 0 END) CVG_CALL_IN_COUNTS_1M
FROM t_snow_borrow_application_id a
JOIN t_snow_call_mobile b
JOIN t_snow_call_record_201612 c ON
(
a.id_borrow_application = b.id_borrow_application
AND b.id = c.id_call_mobile
)
GROUP BY a.id_borrow_application;
Do I have to write 4 queries?
You are nesting aggregate function which is not allowed in MySQL.
You don't actually need the sum function for count distinct phone_nos for different conditions. Take the count (distinct outside the case and remove sum function and else clause of the case.
Try this:
select a.id_borrow_application,
count(distinct case when call_type = 0 then c.phone_no end) CVG_CALL_OUT_COUNTS_6M,
count(distinct case when call_type = 0
and c.days <= 30 then c.phone_no end) CVG_CALL_OUT_COUNTS_1M,
count(distinct case when call_type = 1 then c.phone_no end) CVG_CALL_IN_COUNTS_6M,
count(distinct case when call_type = 1
and c.days <= 30 then c.phone_no end) CVG_CALL_IN_COUNTS_1M
from t_snow_borrow_application_id a
join t_snow_call_mobile b
join t_snow_call_record_201612 c on (
a.id_borrow_application = b.id_borrow_application
and b.id = c.id_call_mobile
)
group by a.id_borrow_application;

Join between Tables in SQL

I have 5 tables
1. SCHOOL[id(bigInt, primary), name(varchar)]
2. SELECTED_INDICATOR[id(bigInt, primary), school_id(bigint)]
3. TEACHER[id(bigint, primary), indicator_id(bigInt), attendance_id(int)]
4. STUDENT[id(bigint, primary), indicator_id(bigInt), attendance_id(int)]
5. MIDDAY_MEAL[id(bigint,primary), indicator_id(bigint), served(boolean), consumed_number(int)]
in TEACHER table, attendance_id can have value: 1 or 2 or 3.
Similarly, in STUDENT table, attendance_id can have value: 1 or 2.
I have to generate a report based on the SELECTED_INDICATOR id, in a format as:
School_id | School_Name | Total_Teacher | Teacher_1 | Teacher_2 | Teacher_3 | Total_Student | Student_1 | Student_2 | served | consumed_number
for this I have tried as:
select A.id, A.school_id, SC.name,
SUM(CASE WHEN T.attendance_id IN (1,2,3) THEN 1 ELSE 0 END) as TOTAL_TEACHER,
SUM(CASE WHEN T.attendance_id IN (1) THEN 1 ELSE 0 END) as TEACHERS_1,
SUM(CASE WHEN T.attendance_id IN (2) THEN 1 ELSE 0 END) as TEACHERS_2,
SUM(CASE WHEN T.attendance_id IN (3) THEN 1 ELSE 0 END) as TEACHERS_3,
SUM(CASE WHEN S.attendance_id IN (1,2) THEN 1 ELSE 0 END) as TOTAL_STUDENT,
SUM(CASE WHEN S.attendance_id IN (1) THEN 1 ELSE 0 END) as STUDENTS_1,
SUM(CASE WHEN S.attendance_id IN (2) THEN 1 ELSE 0 END) as STUDENTS_2,
M.served, M.consumed_number
from SELECTED_INDICATOR A
join SCHOOL SC on A.school_id = SC.id
join TEACHER T on A.id = T.indicator_id
join STUDENT S on A.id = S.indicator_id
join MIDDAY_MEAL M on A.id = M.indicator_id
WHERE A.STATUS = 'COMPLETED' group by A.id;
When I join TEACHER or STUDENT with SELECTED_INDICATOR one at a time, it gives me the correct data. But when I join both the TEACHER and STUDENT with SELECTED_INDICATOR as in the above query, I get huge numbers for teacher and student related fields.
what is wrong with my query? Please help to correct it, or give any alternative query.
Try using COUNT() that have the options to use distincted values. The problem is that the tables are multiplying the results.
select A.id, A.school_id, SC.name,
COUNT(DISTINCT CASE WHEN T.attendance_id IN (1,2,3) THEN t.TeacherID END) as TOTAL_TEACHER,
COUNT(DISTINCT CASE WHEN T.attendance_id IN (1) THEN t.TeacherID END) as TEACHERS_1,
....
FROM ....
SELECT A.id, A.school_id, SC.name,
(SELECT COUNT(T.attendance_id) FROM TEACHER T GROUP BY T.indicator_id HAVING A.id = T.indicator_id) AS TOTAL_TEACHER,
(SELECT COUNT(T.attendance_id) FROM TEACHER T WHERE T.attendance_id = 1 GROUP BY T.indicator_id HAVING A.id = T.indicator_id) AS TEACHERS_1,
(SELECT COUNT(T.attendance_id) FROM TEACHER T WHERE T.attendance_id = 2 GROUP BY T.indicator_id HAVING A.id = T.indicator_id) AS TEACHERS_2,
(SELECT COUNT(T.attendance_id) FROM TEACHER T WHERE T.attendance_id = 3 GROUP BY T.indicator_id HAVING A.id = T.indicator_id) AS TEACHERS_3,
(SELECT COUNT(S.attendance_id) FROM STUDENT S GROUP BY S.indicator_id HAVING A.id = S.indicator_id) AS TOTAL_STUDENT,
(SELECT COUNT(S.attendance_id) FROM STUDENT S WHERE S.attendance_id = 1 GROUP BY S.indicator_id HAVING A.id = S.indicator_id) AS STUDENTS_1,
(SELECT COUNT(S.attendance_id) FROM STUDENT S WHERE S.attendance_id = 2 GROUP BY S.indicator_id HAVING A.id = S.indicator_id) AS STUDENTS_2,
M.served, M.consumed_number
FROM SELECTED_INDICATOR A
JOIN SCHOOL SC on A.school_id = SC.id
JOIN MIDDAY_MEAL M on A.id = M.indicator_id

Left JOIN returning ALL NULL values when no corresponding row in right table

I have the following query and the sub query is producing a strange result when left joined. Here is what is happening (row with StoreID 1 is returning all null in joined table) and what I really want :
TABLE A
StoreID unitsales1 unitsales2 unitsales3
1 NULL NULL 6
2 12 12 12
3 12 NULL 12
TABLE B
StoreID prevunitsales1 prevunitsales2 prevunitsales3
2 NULL NULL 6
3 12 12 12
What I am getting
LEFT JOIN ON A.StoreId = B.StoreId
StoreID unitsales1 unitsales2 unitsales3 prevunitsales1 prevunitsales2 prevunitsales3
1 NULL NULL NULL NULL NULL NULL
2 12 12 12 NULL NULL 6
3 12 NULL 12 12 12 12
What I want
LEFT JOIN ON A.StoreId = B.StoreId
StoreID unitsales1 unitsales2 unitsales3 prevunitsales1 prevunitsales2 prevunitsales3
1 NULL NULL 6 NULL NULL NULL
2 12 12 12 NULL NULL 6
3 12 NULL 12 12 12 12
Here is the query:
SELECT SQL_CALC_FOUND_ROWS #storeid:=z.id,z.biz_name, z.wf_store_name, z.e_address, z.e_city, z.e_state, z.e_postal, IFNULL(total_sales - prev_total_sales,'CV') as diff_total_sales, IFNULL(d_source,'N/A') as d_source, IFNULL(unit_sales1 - prev_unit_sales1,(SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords WHERE store_id=#storeid AND upc=855555000019)) as diff_unit_sales1, IFNULL(unit_sales2 - prev_unit_sales2,(SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords WHERE store_id=#storeid AND upc=855555000022)) as diff_unit_sales2, IFNULL(unit_sales3 - prev_unit_sales3,(SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords WHERE store_id=#storeid AND upc=855555000025)) as diff_unit_sales3, IFNULL(unit_sales4 - prev_unit_sales4,(SELECT IFNULL(max(datetimesql),'NS') FROM storeCheckRecords WHERE store_id=#storeid AND upc=855555000028)) as diff_unit_sales4 FROM
(SELECT s1.id,s1.biz_name as biz_name, s1.wf_store_name as wf_store_name, s1.e_address as e_address,s1.e_city as e_city,s1.e_state as e_state,s1.e_postal as e_postal,sum(s2.unit_sales) as total_sales, sum(s2.unit_sales/4.28571428571) as week_avg,group_concat(DISTINCT s2.d_source separator ',') as d_source,
SUM(CASE u.id WHEN 1 THEN s2.unit_sales ELSE NULL END) AS unit_sales1,SUM(CASE u.id WHEN 2 THEN s2.unit_sales ELSE NULL END) AS unit_sales2,SUM(CASE u.id WHEN 3 THEN s2.unit_sales ELSE NULL END) AS unit_sales3,SUM(CASE u.id WHEN 4 THEN s2.unit_sales ELSE NULL END) AS unit_sales4
FROM allStores as s1
INNER JOIN storeCheckRecords AS s2
ON s1.id = s2.store_id
AND s2.datetimesql BETWEEN '2015-07-01' AND '2015-07-31'
AND s1.key_retailer LIKE 'FRESH THYME'
INNER JOIN ( SELECT 1 AS id, '855555000019' AS upc UNION SELECT 2, '855555000022' UNION SELECT 3, '855555000025' UNION SELECT 4, '855555000028' ) u ON u.upc = s2.upc
GROUP BY
s1.id) x
LEFT OUTER JOIN
(SELECT s1.id,s1.biz_name as prev_biz_name, s1.wf_store_name as prev_wf_store_name, s1.e_address as prev_e_address,s1.e_city as prev_e_city,s1.e_state as prev_e_state,s1.e_postal as prev_e_postal,sum(s2.unit_sales) as prev_total_sales, sum(s2.unit_sales/4.28571428571) as prev_week_avg,group_concat(DISTINCT s2.d_source separator ',') as prev_d_source,
SUM(CASE u.id WHEN 1 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales1,SUM(CASE u.id WHEN 2 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales2,SUM(CASE u.id WHEN 3 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales3,SUM(CASE u.id WHEN 4 THEN s2.unit_sales ELSE 0 END) AS prev_unit_sales4
FROM allStores as s1
INNER JOIN storeCheckRecords AS s2
ON s1.id = s2.store_id
AND s2.datetimesql BETWEEN '2015-06-01' AND '2015-06-30'
AND s1.key_retailer LIKE 'FRESH THYME'
INNER JOIN ( SELECT 1 AS id, '855555000019' AS upc UNION SELECT 2, '855555000022' UNION SELECT 3, '855555000025' UNION SELECT 4, '855555000028' ) u ON u.upc = s2.upc
GROUP BY
s1.id) y
ON x.id = y.id
RIGHT JOIN
(SELECT s1.id,s1.biz_name,s1.wf_store_name,s1.e_address,s1.e_city,s1.e_state,s1.e_postal
FROM allStores as s1
WHERE 1
AND s1.key_retailer LIKE 'FRESH THYME') z
ON y.id = z.id
ORDER BY biz_name ASC
LIMIT 0, 1000
What am I missing?
Comparing NULL with NULL using == never matches, which is why there is no table2 row that matches the first row of table1.
You can use <==> instead if you really want to do this.
mysql> SELECT 1 <=> 1, NULL <=> NULL, 1 <=> NULL;
-> 1, 1, 0
mysql> SELECT 1 = 1, NULL = NULL, 1 = NULL;
-> 1, NULL, NULL

MySQL query slow in Where MONTH(datetime)

I am trying to add index in datetime, but the result still same.
SELECT s.id, s.player,
COUNT(case when dg.winner = 1 AND dp.colour <= 5 then 1 when dg.winner = 2 AND dp.colour > 5 then 1 else null end) as totalwin,
COUNT(case when dg.winner = 2 AND dp.colour <= 5 then 1 when dg.winner = 1 AND dp.colour > 5 then 1 else null end) as totallose,
COUNT(dg.winner) as totalgames
FROM dotaplayers AS dp
LEFT JOIN gameplayers AS gp ON gp.gameid = dp.gameid and dp.colour = gp.colour
LEFT JOIN stats AS s ON s.player_lower = gp.name
LEFT JOIN dotagames AS dg ON dg.gameid = dp.gameid
LEFT JOIN games AS g ON g.id = dp.gameid
LEFT JOIN bans as b ON b.name=gp.name
WHERE MONTH(g.datetime) = 4
GROUP by gp.name
ORDER BY totalwin DESC LIMIT 0,10
Showing rows 0 - 9 (10 total, Query took 7.7552 seconds.)
I want order the most winner in 4th month (April). Then it shows id, username, totalwins, totallose, totaldraw, totalgames. The case in my query is the how to get that. The result is correct, but slow.
Assuming g.datetime is indexed, try this instead:
WHERE g.`datetime` BETWEEN 20150401 AND 20150430`
Using the MONTH function, or any other function, on the field data in the WHERE eliminates the benefits of any indexes you might have on those fields; this results in the query requiring a full scan of the values in the table.
Rearranging the order of JOINs will probably help as well:
SELECT s.id, s.player
, SUM(case
when dg.winner = 1 AND dp.colour <= 5 then 1
when dg.winner = 2 AND dp.colour > 5 then 1
else 0
end
) as totalwin
, SUM(case
when dg.winner = 2 AND dp.colour <= 5 then 1
when dg.winner = 1 AND dp.colour > 5 then 1
else 0
end
) as totallose
, COUNT(dg.winner) as totalgames -- Not, sure of the nature of dg.`winner`, a SUM might be more appropriate here as well.
FROM games AS g
INNER JOIN dotaplayers AS dp ON g.id = dp.gameid
LEFT JOIN gameplayers AS gp ON gp.gameid = dp.gameid and dp.colour = gp.colour
LEFT JOIN stats AS s ON s.player_lower = gp.name
LEFT JOIN dotagames AS dg ON dg.gameid = dp.gameid
LEFT JOIN bans as b ON b.name=gp.name
WHERE g.`datetime` BETWEEN 20150401000000 AND 20150430235959
GROUP by gp.name
ORDER BY totalwin DESC
LIMIT 0,10
;
Another thing to note: Depending on the relationship between tables, some of the intermediate joins may result in effectively multiplying the resulting totals; this can be resolved by doing the sums in subqueries and joining those instead.

Complex querying on table with multiple userids

I have a table like this:
score
id week status
1 1 0
2 1 1
3 1 0
4 1 0
1 2 0
2 2 1
3 2 0
4 2 0
1 3 1
2 3 1
3 3 1
4 3 0
I want to get all the id's of people who have a status of zero for all weeks except for week 3. something like this:
Result:
result:
id w1.status w2.status w3.status
1 0 0 1
3 0 0 1
I have this query, but it is terribly inefficient on larger datasets.
SELECT w1.id, w1.status, w2.status, w3.status
FROM
(SELECT s.id, s.status
FROM score s
WHERE s.week = 1) w1
LEFT JOIN
(SELECT s.id, s.status
FROM score s
WHERE s.week = 2) w2 ON w1.id=w2.id
LEFT JOIN
(SELECT s.id, s.status
FROM score s
WHERE s.week = 3) w3 ON w1.id=w3.id
WHERE w1.status=0 AND w2.status=0 AND w3.status=1
I am looking for a more efficient way to calculate the above.
select id
from score
where week in (1, 2, 3)
group by id
having sum(
case
when week in (1, 2) and status = 0 then 1
when week = 3 and status = 1 then 1
else 0
end
) = 3
Or more generically...
select id
from score
group by id
having
sum(case when status = 0 then 1 else 0 end) = count(*) - 1
and min(case when status = 1 then week else null end) = max(week)
You can do using not exists as
select
t1.id,
'0' as `w1_status` ,
'0' as `w2_status`,
'1' as `w3_status`
from score t1
where
t1.week = 3
and t1.status = 1
and not exists(
select 1 from score t2
where t1.id = t2.id and t1.week <> t2.week and t2.status = 1
);
For better performance you can add index in the table as
alter table score add index week_status_idx (week,status);
In case of static number of weeks (1-3), group_concat may be used as a hack..
Concept:
SELECT
id,
group_concat(status) as totalStatus
/*(w1,w2=0,w3=1 always!)*/
FROM
tableName
WHERE
totalStatus = '(0,0,1)' /* w1=0,w2=1,w3=1 */
GROUP BY
id
ORDER BY
week ASC
(Written on the go. Not tested)
SELECT p1.id, p1.status, p2.status, p3.status
FROM score p1
JOIN score p2 ON p1.id = p2.id
JOIN score p3 ON p2.id = p3.id
WHERE p1.week = 1
AND p1.status = 0
AND p2.week = 2
AND p2.status = 0
AND p3.week = 3
AND p3.status = 1
Try this, should work