Conditional group by in mysql - php

I had a mysql query where i need to add some condition in Group by statement , if i use single field in Group by it works but i need two field include in the Group by, here is my query any one please help me to find out the issue
SELECT (CASE
WHEN CSR.skill_type = 1 THEN
(SELECT skills_value from cv_skills
WHERE skills_id = CSR.skill_id )
WHEN CSR.skill_type = 2 THEN
(SELECT ostype_name from cv_os_type
WHERE ostype_id = CSR.skill_id )
WHEN CSR.skill_type = 3 THEN
(SELECT dbtype_name from cv_db_type
WHERE dbtype_id = CSR.skill_id)
WHEN CSR.skill_type = 4 THEN
(SELECT title from candidate_competencies
WHERE id = CSR.skill_id)
WHEN CSR.skill_type = 0 THEN
IT.type_name
END) AS skill_name,
(CASE
WHEN IT.type_parent_id > 0 THEN
IT.type_parent_id
WHEN IT.type_parent_id = 0 THEN
CIS.interview_type
END) AS typeId,
(CASE
WHEN CSR.skill_type = 4 THEN
minimum_rating
END) AS minimum_rating,
AVG( CSR.rate ) AS skill_rating,
CSR.skill_type,CIS.created, CC.id
FROM `candidate_interview_skill_rate` `CSR`
LEFT JOIN `candidate_interview_process` `CIP` ON CSR.interview_process_id = CIP.id
LEFT JOIN `candidate_interview_schedule` `CIS` ON CIS.id = CIP.interview_schedules_id AND CIS.archive_date IS NULL
LEFT JOIN `candidate_interview` `CI` ON CI.id = CIS.interview_id AND CI.archived_date IS NULL
LEFT JOIN `interview_type` `IT` ON IT.id = CIS.interview_type
LEFT JOIN `candidate_competencies` `CC` ON CC.id = CSR.skill_id
WHERE CI.candidate_user_id = 39
GROUP BY (CASE
WHEN CSR.skill_type > 0 THEN
CSR.skill_id, CSR.skill_type
ELSE CIS.interview_type
END)
ORDER BY `CSR`.`skill_type`

You can concatenate CSR.skill_id, CSR.skill_type to a single column and use it in select statement. The group by needs to be like this
GROUP BY CASE
WHEN CSR.skill_type > 0 THEN
CONCAT(CSR.skill_id, '-', CSR.skill_type)
ELSE
CIS.interview_type
END;
Hope this works.

You can include multiple expressions in a GROUP BY clause, just separate the expressions with commas, just like in the SELECT list.
Very often, the expressions in the SELECT list are repeated in the GROUP BY clause.
It's often possible to use a CASE expression to support a more complex set of conditions.
It's hard to provide more concrete assistance with your query, absent sample data, and desired output.

Related

sql calculating query doesn't run, #1054 unknown column in field list

I want to create query that would show is the certain section is passed by certain account_id. It means all lessons of the section is checked = 1(true). I tried this solution:
SELECT count(*) as checked,(
SELECT count(*)
FROM lessons
WHERE section_id = 1
GROUP BY section_id
) as cnt, (checked = cnt) as passed
FROM lessons l
LEFT JOIN progress p ON l.id = p.lesson_id
WHERE l.section_id = 1 AND p.account_id = 3 AND checked = 1
GROUP BY l.section_id
But it returns error:
#1054 unknown 'cnt' column in field list.
What do I do wrong?
The unknown 'cnt' is generated from (checked=cnt) as passed. Try this
SELECT checked, cnt, (checked=cnt) as passed FROM (SELECT count(*) as checked,(
SELECT count(*)
FROM lessons
WHERE section_id = 1
GROUP BY section_id
) as cnt
FROM lessons l
LEFT JOIN progress p ON l.id = p.lesson_id
WHERE l.section_id = 1 AND p.account_id = 3 AND checked = 1
GROUP BY l.section_id) tblA
Note that you should move all conditions on the p.* columns to the ON clause. Otherwise you will convert the LEFT JOIN to an INNER JOIN and COUNT(*) will always be the same as in your subquery. However you don't even need that subquery - You can get the same value with COUNT(p.lesson_id) instead. It will ignore all rows with NULL.
SELECT
COUNT(*) as cnt,
COUNT(p.lesson_id) as checked,
COUNT(*) = COUNT(p.lesson_id) as passed
FROM lessons l
LEFT JOIN progress p
ON p.lesson_id = l.id
AND p.account_id = 3
AND p.checked = 1
WHERE l.section_id = 1
SELECT count(*) as checked, (SELECT count(*) FROM lessons
WHERE section_id = 1 GROUP BY section_id) as cnt,
((SELECT count(*) FROM lessons WHERE section_id = 1 GROUP BY section_id) = checked) pass
FROM lessons l LEFT JOIN progress p ON l.id = p.lesson_id
WHERE l.section_id = 1 AND p.account_id = 3 AND checked = 1 GROUP BY l.section_id
Thanks to #aynber. Though I'm not sure if this solution isn't too bulky.

Grouping and chunking by value in MySQL

I have a simple raw SQL query which is returning data like this:
SELECT
a.id, count(ah.id) as count
FROM
`table_name_one` as a
JOIN
`table_name_two` as ah
ON
a.id=ah.answer_id
WHERE
a.user_id = 7178
AND a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
I want to group this result by two groups.
Group One will contain data when revisionCount == 1 and Group Two will contain rest of the data. I can do this by using a loop but if I can do this by SQL query it will be good for me.
Can anybody help me out from here?
If you want to construct a list of the two sets of values, you can do so in one query:
SELECT GROUP_CONCAT(CASE WHEN cnt = 1 THEN id END) as id_1s,
GROUP_CONCAT(CASE WHEN cnt > 1 THEN id END) as id_2plus
FROM (SELECT a.id, count(ah.id) as cnt
FROM `table_name_one` a JOIN
`table_name_two` as ah
ON a.id = ah.answer_id
WHERE a.user_id = 7178 AND
a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
) a;
This puts the values into comma-delimited lists. MySQL by default limits the length to 1,024 characters. If the lists are longer, then you can run the query twice to generate each . . . or just order by cnt and check the value when you are reading the return values.
You can use having clause to achieve this. I would recommend you to read online for more info on having.
Below query will give you all records with count exactly as 1:
SELECT
a.id, count(ah.id) as count
FROM
`table_name_one` as a
JOIN
`table_name_two` as ah
ON
a.id=ah.answer_id
WHERE
a.user_id = 7178
AND a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
HAVING count(ah.id) = 1
You can tweak the having condition in the above query to get the second set.
I assume you need 2 result sets as arrays in php, for which you need to write 2 queries separated by semicolon.
Query 1 where revision count =1;
Query 2 where revision count >1;
Hope this helps

How to count records as per all year?

I need to count record year wise, I did some query but i am not getting correct result. Below is my query. But that is not working for me. Can anyone please look in this and give me right query ? Any help will be appreciated.
SELECT
(SELECT count(DISTINCT id) FROM call_response WHERE disposition=0 AND user_id=pu.id ) AS `trueAlarm`,
(SELECT count(DISTINCT id) FROM call_response WHERE disposition=1 AND user_id=pu.id ) AS `falseAlarm`,
(SELECT count(DISTINCT id) FROM call_response WHERE disposition=2 AND user_id=pu.id ) AS `disregarded`,
YEAR(cr.created_date) AS `callYear`
FROM `call_response` AS `cr`
INNER JOIN `permit_users` AS `pu`
ON cr.user_id=pu.id
WHERE ( pu.is_deleted=0 AND pu.is_trashed=0 AND cr.is_deleted=0)
GROUP BY `callYear`
The query that you want uses either conditional aggregation or subqueries, but not both. In other words, either use the subqueries but do not have an outer join to call_response. Or, have the outer join but not the subqueries.
I would write the query like this:
SELECT count(distinct case when disposition = 0 AND user_id = pu.id then id end) as trueAlarm,
count(distinct case when disposition = 1 AND user_id = pu.id then id end) as falseAlarm,
count(distinct case when disposition = 2 AND user_id = pu.id then id end) as disregarded,
YEAR(cr.created_date) AS `callYear`
FROM `call_response` `cr` INNER JOIN
`permit_users` `pu`
ON cr.user_id = pu.id
WHERE pu.is_deleted = 0 AND pu.is_trashed = 0 AND cr.is_deleted = 0
GROUP BY `callYear`;

Mysql in conditon related query

SELECT ac1.id, ac1.course_name, case when a.yettoapprove is not null
then 'true'
else 'false'
end OrderedAll , a.* from (SELECT ac.id, sum(case when ad.status = '2' then 1 else 0 end) as yettoapprove , sum(case when ad.status = '1' then 1 else 0 end) as approved,case when ad.status = '2' is not null
then 'true'
else 'false'
end OrderedAll FROM aw_ou_student_data ad , jos_users ju , aw_ou_lookup_courses ac,aw_ou_lookup_colleges ac1 where ac1.id=ju.college_code and ac.id in (27,28,29,30,133,32,33,34,35,36,37,38,39,40,41,42,43,44,134,135) and ac.school_id=2 and ju.id=ad.user_id and ac.id=ju.course_code GROUP BY ac.id ORDER BY ac.id ) a left join aw_ou_lookup_courses ac1 on ac1.id = a.id
In the above inner query in above one i have given 20 ac.id but i am getting only 14 out records as a result. How do i get the total 20 records as a result. i.e The id present in the in condition if it doesnt satisfy then i should a record with 0 as values for it beside that id.
How do i do it..?
A simple left join with condition should do it:
SELECT
table1.id,
table2.*
FROM
table table1
LEFT JOIN table2 ON table1.id = table2.id AND (your conditions here)

Limiting a left join to returning one result?

I currently have this left join as part of a query:
LEFT JOIN movies t3 ON t1.movie_id = t3.movie_id AND t3.popularity = 0
The trouble is that if there are several movies with the same name and same popularity (don't ask, it just is that way :-) ) then duplicate results are returned.
All that to say, I would like to limit the result of the left join to one.
I tried this:
LEFT JOIN
(SELECT t3.movie_name FROM movies t3 WHERE t3.popularity = 0 LIMIT 1)
ON t1.movie_id = t3.movie_id AND t3.popularity = 0
The second query dies with the error:
Every derived table must have its own alias
I know what I'm asking is slightly vague since I'm not providing the full query, but is what I'm asking generally possible?
The error is clear -- you just need to create an alias for the subquery following its closing ) and use it in your ON clause since every table, derived or real, must have its own identifier. Then, you'll need to include movie_id in the subquery's select list to be able to join on it. Since the subquery already includes WHERE popularity = 0, you don't need to include it in the join's ON clause.
LEFT JOIN (
SELECT
movie_id,
movie_name
FROM movies
WHERE popularity = 0
ORDER BY movie_name
LIMIT 1
) the_alias ON t1.movie_id = the_alias.movie_id
If you are using one of these columns in the outer SELECT, reference it via the_alias.movie_name for example.
Update after understanding the requirement better:
To get one per group to join against, you can use an aggregate MAX() or MIN() on the movie_id and group it in the subquery. No subquery LIMIT is then necessary -- you'll receive the first movie_id per name withMIN() or the last with MAX().
LEFT JOIN (
SELECT
movie_name,
MIN(movie_id) AS movie_id
FROM movies
WHERE popularity = 0
GROUP BY movie_name
) the_alias ON t1.movie_id = the_alias.movie_id
LEFT JOIN movies as m ON m.id = (
SELECT id FROM movies mm WHERE mm.movie_id = t1.movie_id
ORDER BY mm.id DESC
LIMIT 1
)
you could try to add GROUP BY t3.movie_id to the first query
Try this:
LEFT JOIN
(
SELECT t3.movie_name, t3.popularity
FROM movies t3 WHERE t3.popularity = 0 LIMIT 1
) XX
ON t1.movie_id = XX.movie_id AND XX.popularity = 0
On MySQL 5.7+ use ANY_VALUE & GROUP_BY:
SELECT t1.id,t1.movie_name, ANY_VALUE(t3.popularity) popularity
FROM t1
LEFT JOIN t3 ON (t3.movie_id=t1.movie_id AND t3.popularity=0)
GROUP BY t1.id
more info
LEFT JOIN only first row
https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
Easy solution to left join the 1 most/least recent row is using select over ON phrase
SELECT A.ID, A.Name, B.Content
FROM A
LEFT JOIN B
ON A.id = (SELECT MAX(id) FROM B WHERE id = A.id)
Where A.id is the auto-incremental primary key.
LEFT JOIN (
SELECT id,movie_name FROM movies GROUP BY id
) as m ON (
m.id = x.id
)
// Mysql
SELECT SUM(db.item_sales_nsv) as total FROM app_product_hqsales_otc as db
LEFT JOIN app_item_target_otc as it ON
db.id = (SELECT MAX(id) FROM app_item_target_otc as ot WHERE id = db.id)
and db.head_quarter = it.hqcode
AND db.aaina_item_code = it.aaina_item_code AND db.month = it.month
AND db.year = it.year
WHERE db.head_quarter = 'WIN001' AND db.month = '5' AND db.year = '2022' AND db.status = '1'

Categories