Counting the number of Male & Females from tables - mysql

SELECT
student_class.acad_yr,
(case when((students.gender = 'Male') then count(students.gender) end)) AS Male,
(case when((students.gender = 'Female') then count(students.gender) end)) AS Female
FROM
students
INNER JOIN student_class ON (students.st_id = student_class.st_id)
WHERE
student_class.acad_yr = '2013/2014' AND
left(student_class.class_id, 1) = '1'
GROUP BY
student_class.acad_yr
ORDER BY
students.surname,
students.othername
I am getting this error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'then count(students.gender) end)) AS Male,
(case when((students.gender = 'Fem' at line 3

Try this :-
SELECT
student_class.acad_yr,
case when students.gender='Male' then count(students.gender) end AS Male,
case when students.gender='Female' then count(students.gender) end AS Female
FROM
students
INNER JOIN student_class ON (students.st_id = student_class.st_id)
WHERE
student_class.acad_yr = '2013/2014' AND
left(student_class.class_id, 1) = '1'
GROUP BY
student_class.acad_yr
ORDER BY
students.surname,
students.othername
Hope it will help you.

SELECT
student_class.acad_yr,
(SELECT COUNT(*) FROM STUDENTS WHERE GENDER = 'Male' AND acad_yr = student_class.acad_yr) AS Male,
(SELECT COUNT(*) FROM STUDENTS WHERE GENDER = 'Female' AND acad_yr = student_class.acad_yr) AS Female,
FROM
students AS S
INNER JOIN student_class ON (S.st_id = student_class.st_id)
WHERE
student_class.acad_yr = '2013/2014' AND
left(student_class.class_id, 1) = '1'
GROUP BY
student_class.acad_yr
ORDER BY
S.surname,
S.othername

Try this:
SELECT
student_class.acad_yr,
sum(case when students.gender = 'Male' then 1 else 0 end) AS Male,
sum(case when students.gender = 'Female' then 1 else 0 end) AS Female,
FROM
students
INNER JOIN student_class ON (students.st_id = student_class.st_id)
WHERE
student_class.acad_yr = '2013/2014' AND
left(student_class.class_id, 1) = '1'
GROUP BY
student_class.acad_yr
ORDER BY
student_class.acad_yr
I removed the columns you were using in the ORDER BY, since it didn't really make much sense.

Related

shows mysql records twice because of inner joining

In below query (Mentors) are 13 which shows me 26, while (SchoolSupervisor) are 5 which shows me 10 which is wrong. it is because of the Evidence which having 2 evidance, because of 2 evidence the Mentors & SchoolSupervisor values shows me double.
please help me out.
Query:
select t.c_id,t.province,t.district,t.cohort,t.duration,t.venue,t.v_date,t.review_level, t.activity,
SUM(CASE WHEN pr.p_association = "Mentor" THEN 1 ELSE 0 END) as Mentor,
SUM(CASE WHEN pr.p_association = "School Supervisor" THEN 1 ELSE 0 END) as SchoolSupervisor,
(CASE WHEN count(file_id) > 0 THEN "Yes" ELSE "No" END) as evidence
FROM review_m t , review_attndnce ra
LEFT JOIN participant_registration AS pr ON pr.p_id = ra.p_id
LEFT JOIN review_files AS rf ON rf.training_id = ra.c_id
WHERE 1=1 AND t.c_id = ra.c_id
group by t.c_id, ra.c_id order by t.c_id desc
enter image description here
You may perform the aggregations in a separate subquery, and then join to it:
SELECT
t.c_id,
t.province,
t.district,
t.cohort,
t.duration,
t.venue,
t.v_date,
t.review_level,
t.activity,
pr.Mentor,
pr.SchoolSupervisor,
rf.evidence
FROM review_m t
INNER JOIN review_attndnce ra
ON t.c_id = ra.c_id
LEFT JOIN
(
SELECT
p_id,
COUNT(CASE WHEN p_association = 'Mentor' THEN 1 END) AS Mentor,
COUNT(CASE WHEN p_association = 'School Supervisor' THEN 1 END) AS SchoolSupervisor,
FROM participant_registration
GROUP BY p_id
) pr
ON pr.p_id = ra.p_id
LEFT JOIN
(
SELECT
training_id,
CASE WHEN COUNT(file_id) > 0 THEN 'Yes' ELSE 'No' END AS evidence
FROM review_files
GROUP BY training_id
) rf
ON rf.training_id = ra.c_id
ORDER BY
t.c_id DESC;
Note that this also fixes another problem your query had, which was that you were selecting many columns which did not appear in the GROUP BY clause. Under this refactor, there is nothing wrong with your current select, because the aggregation take place in a separate subquery.
try adding this to the WHERE part of your query
AND pr.p_id IS NOT NULL AND rf.training_id IS NOT NULL
You can add a group by pr.p_id to remove the duplicate records there. Since, the group by on pr is not present as of now, there might be multiple records of same p_id for same ra
group by t.c_id, ra.c_id, pr.p_id order by t.c_id desc

Translate MYSQL query to HQL using multiple JOINS

everyone.
I am using grails 3.3.0.M2 framework with mysql as data-source the following sql query is working as expected
SELECT
c.name,
SUM(CASE
WHEN t.status = 'open' THEN 1
ELSE 0
END) 'open',
SUM(CASE
WHEN t.status = 'pending' THEN 1
ELSE 0
END) 'in progress',
SUM(CASE
WHEN t.status = 'closed' THEN 1
ELSE 0
END) 'closed'
FROM
tickets t
INNER JOIN
users u ON t.user_id = u.id
INNER JOIN
user_coordinations uc ON uc.user_id = u.id
INNER JOIN
coordinations c ON c.id = uc.coordination_id
GROUP BY 1
I translated to HQL using implicit JOIN but I am getting the wrong results, here is the hql query:
SELECT
c.name,
SUM(CASE
WHEN t.status = 'open' THEN 1
ELSE 0
END),
SUM(CASE
WHEN t.status = 'pending' THEN 1
ELSE 0
END),
SUM(CASE
WHEN t.status = 'closed' THEN 1
ELSE 0
END)
FROM
Ticket t, User u, UserCoordination uc, Coordination c
WHERE
MONTH(t.dateCreated) = :month
GROUP BY 1
In order to get the right results stack overflow users help me to understand that the query needs to use explicit JOINS, here the question: Group by a field that does not belongs to the consulted table
Right now I am trying with the following query:
SELECT
c.name,
SUM(CASE
WHEN t.status = 'open' THEN 1
ELSE 0
END),
SUM(CASE
WHEN t.status = 'pending' THEN 1
ELSE 0
END),
SUM(CASE
WHEN t.status = 'closed' THEN 1
ELSE 0
END)
FROM
Ticket t
INNER JOIN
User u
INNER JOIN
UserCoordination uc
INNER JOIN
Coordination c
WHERE
MONTH(t.dateCreated) = :month
GROUP BY 1
But i am getting a com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException with the caused message You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'inner join user_coordinations usercoordi2_ on inner join coordinations coordinat' at line 1
Thanks for your help and time
SELECT new map(
c.name as name,
(CASE
WHEN t.status = 'open' THEN 1
ELSE 0
END) as open,
(CASE
WHEN t.status = 'pending' THEN 1
ELSE 0
END) as pending,
(CASE
WHEN t.status = 'closed' THEN 1
ELSE 0
END) as closed,
SUM(open) as openSum,
SUM(pending) as pendingSum,
SUM(closed) as closedSum
)
FROM
Ticket t
left join t.user u left join u.userCoordination uc left join uc.coordination c
WHERE
MONTH(t.dateCreated) = :month
//GROUP BY 1
What you had had lots missing above is more like what you need, you need
select new map(i.item as item... if you compare the basics of this with what you had and what i tried to do you can see why you had errors.
unsure about your group by it should be group by something. Wasn't sure by inner join if you just meant a join if that was the case leave out all the left join since left join attempts to connect and get any null hasMany relations etc.

Left join in MySQL doesn't give me expected result

I have the following SQL:
SELECT t.teilnehmer_id, t.familienname, t.vorname, t.ort, t.ortsteil, t.kontrolle_ertrag, t.kontrolle_1j, t.kontrolle_brache,
SUM(fe.nutzflaeche) AS nutzflaeche_ertrag, GROUP_CONCAT(fe.nutzflaeche) AS einzelfl_ertrag,
SUM(fp.nutzflaeche) AS nutzflaeche_pflanzj, GROUP_CONCAT(fp.nutzflaeche) AS einzelfl_pflanzj,
SUM(fb.nutzflaeche) AS nutzflaeche_brache, GROUP_CONCAT(fb.nutzflaeche) AS einzelfl_brache,
SUM(fn.nutzflaeche) AS nutzflaeche_nicht_aush, GROUP_CONCAT(fn.nutzflaeche) AS einzelfl_nicht_aush
FROM teilnehmer t
LEFT JOIN anrede a ON (t.anrede_id = a.anrede_id)
LEFT JOIN antragsform af ON (t.antragsform_id = af.antragsform_id)
LEFT JOIN bank b ON (t.bank_id = b.bank_id)
LEFT JOIN flurverzeichnis fe ON (t.teilnehmer_id = fe.teilnehmer_id AND fe.kulturbez = 'E')
LEFT JOIN flurverzeichnis fp ON (t.teilnehmer_id = fp.teilnehmer_id AND fp.kulturbez = 'P')
LEFT JOIN flurverzeichnis fb ON (t.teilnehmer_id = fb.teilnehmer_id AND fb.kulturbez = 'B')
LEFT JOIN flurverzeichnis fn ON (t.teilnehmer_id = fn.teilnehmer_id AND fn.kulturbez = 'N')
WHERE 1 = 1
GROUP BY t.teilnehmer_id
ORDER BY familienname, vorname
The sum doesn't reflect the correct areas if there is a match in more than one kulturbez. E.g. if I have 5 rows with kulturbez 'E' and 2 rows with kulturbez 'N', each 'E' row shows up twice and each 'N' row shows up 5 times. Any suggestions on how to redo the SQL to only sum each row with the matching kulturbez once?
Thanks,
Gunter
As indicated in my comment, unavoidable 1:N joins usually need subqueries to calculate aggregate values appropriately; but it looks like your need can be solved with conditional aggregation, like so:
SELECT t.teilnehmer_id, t.familienname, t.vorname, t.ort, t.ortsteil, t.kontrolle_ertrag, t.kontrolle_1j, t.kontrolle_brache
, SUM(CASE WHEN f.kulturbez = 'E' THEN f.nutzflaeche ELSE NULL END) AS nutzflaeche_ertrag
, GROUP_CONCAT(CASE WHEN f.kulturbez = 'E' THEN f.nutzflaeche ELSE NULL END) AS einzelfl_ertrag
, SUM(CASE WHEN f.kulturbez = 'P' THEN f.nutzflaeche ELSE NULL END) AS nutzflaeche_pflanzj
, GROUP_CONCAT(CASE WHEN f.kulturbez = 'P' THEN f.nutzflaeche ELSE NULL END) AS einzelfl_pflanzj
, SUM(CASE WHEN f.kulturbez = 'B' THEN f.nutzflaeche ELSE NULL END) AS nutzflaeche_brache
, GROUP_CONCAT(CASE WHEN f.kulturbez = 'B' THEN f.nutzflaeche ELSE NULL END) AS einzelfl_brache
, SUM(CASE WHEN f.kulturbez = 'N' THEN f.nutzflaeche ELSE NULL END) AS nutzflaeche_nicht_aush
, GROUP_CONCAT(CASE WHEN f.kulturbez = 'N' THEN f.nutzflaeche ELSE NULL END) AS einzelfl_nicht_aush
FROM teilnehmer t
LEFT JOIN anrede a ON (t.anrede_id = a.anrede_id)
LEFT JOIN antragsform af ON (t.antragsform_id = af.antragsform_id)
LEFT JOIN bank b ON (t.bank_id = b.bank_id)
LEFT JOIN flurverzeichnis f ON (t.teilnehmer_id = fe.teilnehmer_id)
WHERE 1 = 1
GROUP BY t.teilnehmer_id
ORDER BY familienname, vorname
Aggregate functions ignore NULL values for the most part. (Also, technically ELSE NULL is optional, as it is the assumed value if ELSE is not specified; but is good practice to make your intent clear.)

Complex query taking too long to run

I have a query (original question that has table structure too) that is perfect, but it creates a temporary table which unfortunately takes like 12 seconds due to amount of data. (1 table has 95k records, another 155k, and another 21k).
Is there any way to go around the temporary table solution or make it run faster? Maybe suggest which fields should be indexed? I have ID fields, date fields, etc... indexed, but that's not helping at all.
SELECT
(
CASE
WHEN a.winner = a.f_a THEN "Win"
WHEN a.winner = a.f_b THEN "Loss"
WHEN a.winner IS NULL THEN a.method
END
) AS result,
SUM(a.f_a IN (d.fighter_a, d.fighter_b) AND d.winner <=> a.f_a) AS fighter_wincount,
SUM(a.f_a IN (d.fighter_a, d.fighter_b) AND d.winner IS NOT NULL AND d.winner <> a.f_a) AS fighter_losscount,
SUM(a.f_a IN (d.fighter_a, d.fighter_b) AND d.method = "Draw") AS fighter_drawcount,
SUM(a.f_a IN (d.fighter_a, d.fighter_b) AND d.method = "No Contest") AS fighter_nocontestcount,
b.name AS opponent,
SUM(a.f_b IN (d.fighter_a, d.fighter_b) AND d.winner <=> a.f_b) AS opponent_wincount,
SUM(a.f_b IN (d.fighter_a, d.fighter_b) AND d.winner IS NOT NULL AND d.winner <> a.f_b) AS opponent_losscount,
SUM(a.f_b IN (d.fighter_a, d.fighter_b) AND d.method = "Draw") AS opponent_drawcount,
SUM(a.f_b IN (d.fighter_a, d.fighter_b) AND d.method = "No Contest") AS opponent_nocontestcount,
b.fighter_id AS opponent_id,
b.fighting_out_of_country AS opponent_country,
a.method AS method,
a.method_type AS method_type,
a.round AS round,
a.time AS time,
c.event_id AS event_id,
c.event_name AS event,
c.event_date AS date,
c.event_city AS event_city,
c.event_state AS event_state,
c.event_country AS event_country
FROM
(
SELECT
fight_id,
IF(fighter_b = :fighter_id_3, fighter_b, fighter_a) AS f_a,
IF(fighter_b = :fighter_id_4, fighter_a, fighter_b) AS f_b,
winner,
method,
method_type,
round,
time,
event
FROM
fights
WHERE
:fighter_id_5 IN (fighter_a, fighter_b)
) a
INNER JOIN
fighters b ON a.f_b = b.fighter_id
INNER JOIN
events c ON a.event = c.event_id
LEFT JOIN
(
SELECT
a.fighter_a,
a.fighter_b,
a.winner,
a.method,
b.event_date
FROM
fights a
INNER JOIN
events b ON a.event = b.event_id
) d ON
(a.f_a IN (d.fighter_a, d.fighter_b) OR a.f_b IN (d.fighter_a, d.fighter_b)) AND
d.event_date <= c.event_date
GROUP BY
a.fight_id
ORDER BY
date DESC
Using a.id IN (b.a_id, b.b_id) is never going to be performant, you won't be able to create an index to fit the query. You should normalise that aspect of your structure.
I propose the following changes to your schema:
Add fight_date_time to your fights table
Move fighter_a and fighter_b out to a participicants table
fight_id, fighter_id - Normalised - Two rows per fight
The following will create two records for every fight (One for each fighter), but will also list their opponent, and both their records going in to the fight.
SELECT
event.event_name,
event.event_date,
MAX(CASE WHEN participant.fighter_id = fighter.id THEN fighter.name END) AS fighter,
MAX(CASE WHEN participant.fighter_id = fighter.id THEN record.wins END) AS wins,
MAX(CASE WHEN participant.fighter_id = fighter.id THEN record.draws END) AS draws,
MAX(CASE WHEN participant.fighter_id = fighter.id THEN record.losses END) AS losses,
CASE WHEN fight.winner = participant.fighter_id THEN 'Win'
WHEN fight.winner IS NULL THEN 'Draw'
ELSE 'Loss' END AS result,
fight.method,
MAX(CASE WHEN participant.fighter_id <> fighter.id THEN fighter.name END) AS Opp,
MAX(CASE WHEN participant.fighter_id <> fighter.id THEN record.wins END) AS Opp_wins,
MAX(CASE WHEN participant.fighter_id <> fighter.id THEN record.draws END) AS Opp_draws,
MAX(CASE WHEN participant.fighter_id <> fighter.id THEN record.losses END) AS Opp_losses
FROM
event
INNER JOIN
fight
ON event.id = fight.event_id
INNER JOIN
participant
ON participant.fight_id = fight.id
INNER JOIN
(
SELECT
participant.fighter_id,
participant.fight_id,
SUM(CASE WHEN prevFight.winner = participant.fighter_id THEN 1 ELSE 0 END) AS wins,
SUM(CASE WHEN prevFight.winner IS NULL) AS draws,
SUM(CASE WHEN prevFight.winner <> participant.fighter_id THEN 1 ELSE 0 END) AS losses
FROM
participant
INNER JOIN
fight
ON participant.fight_id = fight.id
INNER JOIN
participant AS prevParticipant
ON prevParticipant.fighter_id = participant.fighter_id
INNER JOIN
fight AS prevFight
ON prevFight.id = prevParticipant.fight_id
AND prevFight.fight_date_time < fight.fight_date_time
GROUP BY
participant.fighter_id,
participant.fight_id
)
AS record
ON record.fight_id = participant.fight_id
INNER JOIN
fighter
ON fighter.id = record.fighter_id
GROUP BY
event.id,
fight.id,
participant.fighter_id
If you want to see the results for just one fighter, add:
- WHERE participant.fighter_id = :fighter_id
Even better, keep this kind of data up to date with triggers. That way you don't need to calculate it again and again and again.

MYSQL How to count elements grouped by type

I have a problem with a query:
I have a list of stores, each of these stores has members and there are various categories of membership (Bronze, silver, gold ...)
The tables are: 'shops', 'members', 'membership_cards'.
shops: id, name
members: id, shops_id, membership_id, first_name, last_name
membership_cards: id, description
I need to extract the count of members, grouped by membership of each stores. Can I do this without using a server side language?
The final result should be something like:
Store's name, n°bronze members, n°silver_members, n°gold_members ....
Based on what you provided, you want a query like:
select shopid,
sum(case when c.cardtype = 'Bronze' then 1 else 0 end) as Bronze,
sum(case when c.cardtype = 'Silver' then 1 else 0 end) as Silver,
sum(case when c.cardtype = 'Gold' then 1 else 0 end) as Gold
from shops s left outer join
members m
on s.shopid = m.shopid left outer join
cards c
on c.memberid = m.memberid
group by shopid
If you want to know the number of members, rather than of cards in each group (if members can have more than one card), then replace the sum() expression with:
count(case when c.cardtype = 'Bronze' then m.memberid end)
Without knowing your database schema, it's a bit hard to answer that question, but something like the following should do the job:
SELECT shop.name,
SUM(CASE WHEN membership_cards.category = 'Bronze' THEN 1 ELSE 0 END) AS Bronze,
SUM(CASE WHEN membership_cards.category = 'Silver'THEN 1 ELSE 0 END) AS Silver,
SUM(CASE WHEN membership_cards.category = 'Gold' THEN 1 ELSE 0 END) AS Gold
FROM shops
INNER JOIN members
ON shop.id = members.shopid
INNER JOIN membership_cards
ON members.id = membership_cards.memberid
GROUP BY shop.name
Just change the column names to the names you are using.
SELECT B.name,A.Bronze,A.Silver,A.Gold
FROM
(
SELECT S.id,
SUM(IF(IFNULL(C.cardtype,'')='Bronze',1,0)) Bronze,
SUM(IF(IFNULL(C.cardtype,'')='Silver',1,0)) Silver,
SUM(IF(IFNULL(C.cardtype,'')='Gold' ,1,0)) Gold
FROM shops S
LEFT JOIN members M ON S.id = M.shops_id
LEFT JOIN membership_cards C ON M.membership_id = C.id
GROUP BY S.id
) A
INNER JOIN shops B USING (id);
I used the IFNULL function in case any member has no cards