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.
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 two tables with the fields as below
______________
|Game_Details:|
|_____________|______
|Game_Id | Game_Name|
__________
|WC_Info:|
|________|_____________________________
|S/N | Game_Id | Country_Name| WC_Won|
I am trying to fetch the list of game_name which are being played in atleast the 4 countries (India, England,France, Italy) and the field WC_Won is 0 for all of them.
The below query is fetching incorrect details.can someone please suggest the way to get it.
Select G.Game_Name
From Game_Details G
Inner Join WC_Info W
On G.Game_Id=W.Game_Id
Where W.WC_Won=0
Group By Game_Name
Having Count(Country_Name in (India, England,France, Italy))>3
You may use this select statement :
Select distinct G.Game_Name
From Game_Details G
Where G.Game_Id in ( Select Game_Id
From WC_Info W
Where W.WC_Won=0
And Country_Name in ('India', 'England','France', 'Italy')
Group By Game_Id
Having Count( distinct Country_Name ) = 4 );
You are close:
Select G.Game_Name
From Game_Details G Inner Join
WC_Info W
On G.Game_Id = W.Game_Id
Where W.WC_Won = 0
Group By Game_Name
Having Count(distinct case when Country_Name in ('India', 'England', 'France', 'Italy')
then country_name
end) = 4;
I find the use of an inequality to be misleading. You want all four countries, so = 4 makes more sense than > 3 or >= 4.
Alternatively, you can filter on the country before the aggregation:
Select G.Game_Name
From Game_Details G Inner Join
WC_Info W
On G.Game_Id = W.Game_Id
Where W.WC_Won = 0 and
Country_Name in ('India', 'England', 'France', 'Italy')
Group By Game_Name
Having Count(distinct country_name) = 4;
Given the following schema:
employees
id | name
employee_attributes
id | employee_id | key | value
I would like to select all employees that have the provided attributes.
The following statement works:
SELECT employees.* FROM employees
INNER JOIN employee_attributes ON employee_attributes.employee_id = employees.id
WHERE employee_attributes.key = 'foo' AND employee_attributes.value = 'bar'
but only allows me to find an employee by one attribute. How can I adapt this to retrieve employees by more than one attribute?
To be clear, if I supply two sets of attributes to match against, the query should only return employees that have at least those two attributes.
For example, if Bob has just one attribute:
key | value
===========
foo | bar
But I supply two attributes to the query (foo and bar, bin and baz), Bob should not be returned.
Following should work:
SELECT employees.id, employees.name, count(employee_attributes.id) as attribute_count FROM employees
INNER JOIN employee_attributes ON employee_attributes.employee_id = employees.id
WHERE (employee_attributes.key = 'foo' AND employee_attributes.value = 'bar') OR (employee_attributes.key = 'bin' AND employee_attributes.value = 'baz')
group by employees.id, employees.name
having attribute_count >= 2;
You can get the employee ids using aggregation:
SELECT ea.employee_id
FROM employee_attributes.employee_id
WHERE (ea.key = 'foo' AND ea.value = 'bar') OR
(ea.key = 'bin' AND ea.value = 'baz')
GROUP BY ea.employee_id
HAVING COUNT(DISTINCT ea.key) = 2;
For the full information, you can use a JOIN:
SELECT e.*
FROM employee e JOIN
(SELECT ea.employee_id
FROM employee_attributes.employee_id
WHERE (ea.key = 'foo' AND ea.value = 'bar') OR
(ea.key = 'bin' AND ea.value = 'baz')
GROUP BY ea.employee_id
HAVING COUNT(DISTINCT ea.key) = 2
) ea
ON ea.employee_id = e.id;
Use conditional aggregation:
SELECT employees.*
FROM employees
INNER JOIN employee_attributes
ON employee_attributes.employee_id = employees.id
GROUP BY employee_attributes.employee_id
HAVING SUM(CASE WHEN employee_attributes.key = 'foo' AND
employee_attributes.value = 'bar' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN employee_attributes.key = 'bin' AND
employee_attributes.value = 'baz' THEN 1 ELSE 0 END) > 0
I have a table like this, in which I need to set the male and female counts for the primary key id:
summaryTable:
id femaleCount maleCount
-----------------------------
s1 ? ?
s2 ? ?
... and so on
There is a detail table as below, that has the users corresponding to each id of summaryTable:
id parentId userId
--------------------------
1 s1 u1
2 s1 u2
3 s2 u2
4 s2 u2
...and so on
The third is the user table like this:
userTable:
userId gender
-------------
u1 M
u2 F
u3 F
..and so on
I have to update the summary table with the counts of male and female. So as per the above, for id=s1, femaleCount should be set to 1 , maleCOunt=1
For id=s2, femaleCOunt should get set to 2 and maleCount=0
Is this possible to do using an UPDATE query in MySQL?
I tried the following, but this returns the sum of occurences of a user i.e. if u1 occurs 2 times for p1(say), then it will return count as 2 and not 1:
SELECT
d.parentId,
SUM(gender = 'F') AS 'F#',
sum(gender = 'M') as 'M#'
FROM detailsTable as d
JOIN userTable as c on c.userId = d.userId
GROUP BY d.parentId;
Also tried as below, but it gave an error:
select d.parentId,
count(case when c.gender='M' then 1 end) as male_cnt,
count(case when c.gender='F' then 1 end) as female_cnt,
from detailsTable d, userTable c where d.userId=c.userId group by d.parentId, d.userId ;
Further, my problem doesnt just end at the select, I need to get the values and then update these in the summary table too.
I might be rusty on the syntax for MySql but I believe this does what you need. The CASE/SUM is effectively a pivot to get the counts, then you can update the table as normal.
UPDATE summaryTable AS st
INNER JOIN ( SELECT parentId
,SUM(CASE WHEN gender = 'f' THEN 1
ELSE 0
END) femaleCount
,SUM(CASE WHEN gender = 'm' THEN 1
ELSE 0
END) maleCount
FROM userTable d
INNER JOIN (SELECT DISTINCT parentId, userId FROM detail) ut ON d.userId = ut.userId
GROUP BY parentId
) AS c ON c.parentId = st.parentId
SET femaleCount = c.femaleCount
,maleCount = c.maleCount
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