Solved! Thanks for all the help!
How do I fix this query
So the Pet's table's fields are
Pet_Code,Name,Employee_code,Type,Sex
And the Employees table's fields are
Employee_code,Last_Name,First_Name,Department
This is what I tried but didn't work:
SELECT First_Name,Last_Name
FROM employees E,pets P
WHERE P.Employee_code=E.Employee_code AND COUNT(Type='C') > Count(Type='D');
Ok you can try this:
SELECT DISTINCT
E.Employee_code, E.Last_Name, E.Department,
(SELECT COUNT(*) FROM pet WHERE Employee_code = E.Employee_code AND Type = 'C') AS cats,
(SELECT COUNT(*) FROM pet WHERE Employee_code = E.Employee_code AND Type = 'D') AS dogs
FROM employee E
LEFT JOIN pet P ON E.Employee_code = P.Employee_code
GROUP BY E.Employee_code
HAVING cats > dogs
it is like previous one, But more simply
I think you could try this:
SELECT
Employee_code,
Last_Name,
First_Name,
SUM(CASE WHEN Type = 'C' THEN 1 ELSE 0 END) AS CAT,
SUM(CASE WHEN Type = 'D' THEN 1 ELSE 0 END) AS DOG
FROM Employees_TABLE E
JOIN PET_TABLE P ON E.Employee_code = P.Employee_code
GROUP BY Employee_code
HAVING SUM(CASE WHEN Type = 'C' THEN 1 ELSE 0 END) > SUM(CASE WHEN Type = 'D' THEN 1 ELSE 0 END)
Maybe you can try with this:
SELECT e.First_Name, e.Last_Name
FROM employees e INNER JOIN pets p ON e.Employee_code = p.Employee_code
WHERE
(SELECT COUNT(*)
FROM pets p1
WHERE p1.Type = 'C' AND p.Employee_code = p1.Employee_code) >
(SELECT COUNT(*)
FROM pets p1
WHERE p1.Type = 'D' AND p.Employee_code = p1.Employee_code)
GROUP BY e.Employee_code;
It might not be an optimal solution, but should do the job.
select min(e.First_Name), min(e.Last_Name)
from emp e left outer join pets p
on p.Employee_Code = e.Employee_Code
group by e.Employee_Code
having
count(case when Type = 'C' then 1 end) >
count(case when Type = 'D' then 1 end)
Related
Show the provinces that has more patients identified as 'M' than 'F'. Must only show full province_name
patient table :
patient_id INT
first_name TEXT
last_name TEXT
gender CHAR(1)
birth_date DATE
city TEXT
province_id CHAR(2)
allergies TEXT
height INT
weight INT
Province_names table:
province_id CHAR(2)
province_name TEXT
I tried this but it's not working.
`select province_name
from patients p,province_names pn
where p.province_id = pn.province_id
and (count(p.province_id) group by p.patient_id having p.gender = 'M' ) >
(count(p.province_id) group by p.patient_id having p.gender = 'F') `
Aggregation provides one straightforward approach:
SELECT pn.province_name
FROM province_names pn
INNER JOIN patients p
ON p.province_id = pn.province_id
GROUP BY pn.province_name
HAVING COUNT(CASE WHEN p.gender = 'M' THEN 1 END) >
COUNT(CASE WHEN p.gender = 'F' THEN 1 END);
I think your problem is solved by this query
select res.province_name
from (SELECT pn.province_name,
case when p.gender = 'M' then 1 else 0 end as gender_no,
count(*) as cnt
FROM province_names pn
INNER JOIN patients p
ON p.province_id = pn.province_id
group by pn.province_names, p.GENDER) res
where res.gender_no = 1
and cnt > (select count(*)
from province_names pnn
INNER JOIN patients pp
ON pp.province_id = pnn.province_id
where res.province_name = pnn.province_name
and pp.GENDER = 'F');
You can USE CTE to achieve your goal if your MySQL version is 8.0.X.X and more!!
(Run this command to check it:)
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 8.0.28 |
+-----------+
1 row in set (0.00 sec)
Full Code:
WITH CTE AS (SELECT province_name, COUNT(*) AS MaleCount FROM patients p JOIN province_names pn ON p.province_id = pn.province_id WHERE gender = 'M' GROUP BY province_name)
, CTE2 AS (SELECT province_name, COUNT(*) AS FemaleCount FROM patients p JOIN province_names pn ON p.province_id = pn.province_id WHERE gender = 'F' GROUP BY province_name)
SELECT CTE.province_name FROM CTE JOIN CTE2 ON CTE.province_name = CTE2.province_name WHERE MaleCount > FemaleCount;
I have this schema:
PERSON(Name, Sex)
FREQUENTS(Name, Shop)
My question is, how do I find the shops whose clients are exclusively men?
You can use the following using a GROUP BY with HAVING:
SELECT frequents.shop
FROM frequents LEFT JOIN person ON frequents.name = person.name
GROUP BY frequents.shop
HAVING SUM(person.sex = 'female') = 0 AND SUM(person.sex = 'male') > 0
demo at dbfiddle.uk
select shop
from
(select shop , (case when sex = 'Male' then 1 else 2 end)s_cnt
from frequents a11
join person a12
on a11.name = a12.name
group by shop , (case when sex = 'Male' then 1 else 2 end)
) a11
group by shop
having sum(s_cnt) = 1
For example NOT EXISTS
select distinct shop
from frequents
where not exists (
select 1
from person
where person.name = frequents.name and person.sex = 'female'
)
However, according to this test is may be better to use IS NULL approach:
select distinct shop
from frequents
left join person on person.name = frequents.name and person.sex = 'female'
where person.name is null
If you want exclusively men, then I would think:
SELECT f.shop
FROM frequents f JOIN
person p
ON f.name = p.name
GROUP BY f.shop
HAVING MIN(p.sex) = MAX(p.sex) AND -- all sex values are the same or NULL
COUNT(p.sex) = COUNT(*) AND -- no NULL values
MIN(p.sex) = 'male' -- the value is male
This version does not assume that there are only two genders.
Is there a way out to achieve something as below?
The below query is only to convey the intention. I do understand that it is syntactically incorrect.
SELECT
T_DATE,
T_NAME,
(SELECT MAX(DUR) WHERE STATUS = 'Y') AS UPD_MAX,
(SELECT AVG(DUR) WHERE STATUS = 'Y') AS UPD_AVG,
(SELECT MAX(DUR) WHERE STATUS = 'N') AS READ_MAX,
(SELECT AVG(DUR) WHERE STATUS = 'N') AS READ_AVG
FROM
TABLE_1
WHERE
T_NAME LIKE 'ab%cd%'
GROUP BY
T_DATE,
T_NAME
Use conditional aggregation:
SELECT T_DATE, T_NAME,
MAX(CASE WHEN STATUS = 'Y' THEN DUR END) AS UPD_MAX,
AVG(CASE WHEN STATUS = 'Y' THEN DUR END) AS UPD_AVG,
MAX(CASE WHEN STATUS = 'N' THEN DUR END) AS READ_MAX,
AVG(CASE WHEN STATUS = 'N' THEN DUR END) AS READ_AVG
FROM TABLE_1
WHERE T_NAME LIKE 'ab%cd%'
GROUP BY T_DATE, T_NAME;
try something like that:
SELECT
T_DATE,
T_NAME,
a.UPD_MAX,
c.UPD_AVG,
b.READ_MAX,
d.READ_AVG
FROM TABLE_1
INNER JOIN (SELECT T_NAME, MAX(DUR) AS UPD_MAX FROM TABLE_1 WHERE STATUS = 'Y' GROUP BY T_NAME) as a ON T_NAME=a.T_NAME
INNER JOIN (SELECT T_NAME, MAX(DUR) AS READ_MAX FROM TABLE_1 WHERE STATUS = 'N' GROUP BY T_NAME) as b ON T_NAME=b.T_NAME
INNER JOIN (SELECT T_NAME, AVG(DUR) AS UPD_AVG FROM TABLE_1 WHERE STATUS = 'Y' GROUP BY T_NAME) as c ON T_NAME=c.T_NAME
INNER JOIN (SELECT T_NAME, AVG(DUR) AS READ_AVG FROM TABLE_1 WHERE STATUS = 'N' GROUP BY T_NAME) as d ON T_NAME=d.T_NAME
WHERE T_NAME LIKE 'ab%cd%'
GROUP BY
T_DATE,
T_NAME
I have
users
------------------------
id | name | other_stuff.....
.
engagement
------------------------
user_id | type_code |
type_code is a varchar, but either A, B, C or NULL
[ EDIT for clarity: Users can have many engagements of each type code. SO I want to count how many they have of each. ]
I want to return ALL user rows, but with a count of A, B and C type engagements. E.g.
users_result
------------------------
user_id | user_name | other_stuff..... | count_A | count_B | count_C |
I've done quite a bit of searching, but found the following issues with other solutions:
The "other_stuff..." is actually grouped / concatenated results from a dozen other joins, so it's a bit of a monster already. So I need to be able to just add the additional fields to the pre-existing "SELECT ...... FROM users..." query.
The three additional required bits of data all come from the same engagement table, each with their own condition. I havent found anything to allow me to use the three conditions on the same related table.
Thanks
[edit]
I tried to simplify the question so people didn't have to look through loads of unnecessary stuff, but seems I might not have given enough info. Here is 'most' of the original query. I've taken out a lot of the selected fields as there are loads, but I've left most of the joins in so you can see basically what is actually going on.
SELECT
user.id,
user.first_name,
user.second_name,
GROUP_CONCAT(DISTINCT illness.id ORDER BY illness.id SEPARATOR ',' ) AS reason_for_treatment,
IF(ww_id=1000003, 1,'') as user_refused_program,
Group_CONCAT(DISTINCT physical_activity.name SEPARATOR ', ') AS programme_options,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM `user`
LEFT JOIN session AS session_induction ON (user.id = session_induction.user_id AND session_induction.session_type_id = 3)
LEFT JOIN stats AS stats_induction ON session_induction.id = stats_induction.session_id
LEFT JOIN session AS session_interim ON (user.id = session_interim.user_id AND session_interim.session_type_id = 4)
LEFT JOIN stats AS stats_interim ON session_interim.id = stats_interim.session_id
LEFT JOIN session AS session_final ON (user.id = session_final.user_id AND session_final.session_type_id = 5)
LEFT JOIN stats AS stats_final ON session_final.id = stats_final.session_id
LEFT JOIN user_has_illness ON user.ID = user_has_illness.user_id
LEFT JOIN illness ON user_has_illness.illness_id = illness.id
LEFT JOIN user_has_physical_activity ON user.ID = user_has_physical_activity.user_id
LEFT JOIN physical_activity ON user_has_physical_activity.physical_activity_id = physical_activity.id
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
WHERE (user.INDUCTION_DATE>='2010-06-09' AND user.INDUCTION_DATE<='2011-06-09' AND user.archive!='1' )
GROUP BY user.id, engagement_item.user_id
It's worth mentioning that it works fine - returns all users with all details required. Except for the count_A B and C cols.
[edit added slightly more simplified query below]
Stripped out the unrelated joins and selects.
SELECT
user.id,
user.first_name,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM `user`
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
GROUP BY user.id, engagement_item.user_id
SELECT e.user_id, u.name,
COUNT(CASE type_code WHEN 'A' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE type_code WHEN 'B' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE type_code WHEN 'C' THEN 1 ELSE NULL END) as count_C
FROM engagement e join users u on (e.user_id = u.id)
GROUP BY e.user_id, u.name
I would use COUNT instead of SUM just because that is what it is made for, counting things when not NULL.
SELECT
user.id,
user.first_name,
user.second_name,
GROUP_CONCAT(DISTINCT illness.id ORDER BY illness.id SEPARATOR ',' ) AS reason_for_treatment,
IF(ww_id=1000003, 1,'') as user_refused_program,
Group_CONCAT(DISTINCT physical_activity.name SEPARATOR ', ') AS programme_options,
ei.count_A, ei.count_B, ei.count_C
FROM `user`
LEFT JOIN ( SELECT user_id
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM engagement_item
GROUP BY userid ) ei
LEFT JOIN session AS session_induction ON (user.id = session_induction.user_id AND session_induction.session_type_id = 3)
LEFT JOIN stats AS stats_induction ON session_induction.id = stats_induction.session_id
LEFT JOIN session AS session_interim ON (user.id = session_interim.user_id AND session_interim.session_type_id = 4)
LEFT JOIN stats AS stats_interim ON session_interim.id = stats_interim.session_id
LEFT JOIN session AS session_final ON (user.id = session_final.user_id AND session_final.session_type_id = 5)
LEFT JOIN stats AS stats_final ON session_final.id = stats_final.session_id
LEFT JOIN user_has_illness ON user.ID = user_has_illness.user_id
LEFT JOIN illness ON user_has_illness.illness_id = illness.id
LEFT JOIN user_has_physical_activity ON user.ID = user_has_physical_activity.user_id
LEFT JOIN physical_activity ON user_has_physical_activity.physical_activity_id = physical_activity.id
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
WHERE (user.INDUCTION_DATE>='2010-06-09' AND user.INDUCTION_DATE<='2011-06-09' AND user.archive!='1' )
GROUP BY user.id, engagement_item.user_id, ei.count_A, ei.count_B, ei.count_C
Something like this perhaps?
select e.user_id, u.name,
sum(case e.type_code when 'A' then 1 else 0 end) as count_A,
sum(case e.type_code when 'B' then 1 else 0 end) as count_B,
sum(case e.type_code when 'C' then 1 else 0 end) as count_C
from engagement e join users u on (e.user_id = u.id)
group by e.user_id, u.name
The interesting part is the use of CASE inside the SUM to split the counting into three chunks.
SELECT p.value AS __color__,
milestone AS __group__,
milestone,
priority,
time AS created,
COUNT(t.id) as 'total open tickets',
SUM(c.value) as 'Total Dev LOE',
SUM(d.value) as 'Total QALOE'
FROM ticket t
LEFT JOIN ticket_custom c ON (t.id = c.ticket AND c.name = 'devloe')
LEFT JOIN ticket_custom d ON (t.id = d.ticket AND d.name = 'qaloe')
LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
WHERE t.milestone = '$MILESTONE'
AND status <> 'closed'
GROUP BY milestone, priority, DATE(FROM_UNIXTIME(time/1000000)) DESC
Then add subquery to return the sum total devloe and qaloe for each priority.
Select p.value AS __color__
, milestone AS __group__
, milestone
, priority
, time AS created
, Count(t.id) as 'total open tickets'
, Sum( Case When c.name = 'devloe' Then c.value End ) As 'Total Dev LOE'
, Sum( Case When c.name = 'qaloe' Then c.value End ) As 'Total QALOE'
, PriorityCounts.Total_Dev_LOE As Priority_Total_QALOE
, PriorityCounts.Total_QALOE As Priority_Total_QALOE
From ticket As t
Left Join ticket_custom As c
On t.id = c.ticket
And c.name In('devloe', 'qaloe')
Left Join enum p
On p.name = t.priority
AND p.type = 'priority'
Join (
Select t1.priority,
, Sum( Case When c1.name = 'devloe' Then c1.value End ) As Total_Dev_LOE
, Sum( Case When c1.name = 'qaloe' Then c1.value End ) As Total_QALOE
From ticket As t1
Left Join ticket_custom As c1
On t1.id = c1.ticket
And c1.name In('devloe', 'qaloe')
Group By t1.priority
) As PriorityCounts
On PriorityCounts.priority = t.priority
Where t.milestone = '$MILESTONE'
And status <> 'closed'
Group By milestone, priority, DATE(FROM_UNIXTIME(time/1000000)) DESC
Doesn't answer your question, but you could simplify the query:
SELECT p.value AS __color__,
milestone AS __group__,
milestone,
priority,
time AS created,
COUNT(t.id) as 'total open tickets',
SUM(CASE WHEN c.name = 'devloe' THEN c.value ELSE 0 END) as 'Total Dev LOE',
SUM(CASE WHEN c.name = 'qaloe' THEN c.value ELSE 0 END) as 'Total QALOE'
FROM TICKET t
LEFT JOIN TICKET_CUSTOM c ON c.ticket = t.id
AND c.name IN ('devloe', 'qaloe')
LEFT JOIN ENUM p ON p.name = t.priority
AND p.type = 'priority'
WHERE t.milestone = '$MILESTONE'
AND status <> 'closed'
GROUP BY milestone, priority, DATE(FROM_UNIXTIME(time/1000000)) DESC