There is an error in my query and I would like some help. I have three tables
Rooms{id,number,name,type(ECO/LUX),active(0/1)}
Men{passport,roomid,status(YOUTH/ADULT)}
Women{passport,roomid,status(YOUTH/ADULT)}
**In each room there can be more than one woman or man.
I want to count how many women and men have the same room with roomid in (1,2,3), status='ADULT', type='LUX' and active=1. Therefore I need a result like this:
+----+--------+-----------+----------+------------+
| id | number | name | CountMen | CountWomen |
+----+--------+-----------+----------+------------+
| 1 | 23 | 1st suite | 2 | 4 |
| 3 | 4 | 2nd suite | 1 | 2 |
+----+--------+-----------+----------+------------+
SELECT id,number,name,
sum(case when Men.status='ADULT' then 1 else 0 end) as CountMen,
sum(case when Women.status='ADULT' then 1 else 0 end) as CountWomen
FROM Rooms left join Men
on Rooms.id=Men.roomid
left join Women on Room.id=Women.roomid where
(type='LUX') and (active=true) and (id in (1,2,3))
group by id;
The problem is that I get sometimes wrong results in the counters.
In a left join, conditions on the second table need to be in the on clause. It would help if you qualified all column names in the query.
However your problem is because you are getting a Cartesian product between the gender tables. This is definitely a case where gender segregation is not a good thing. You should have just one table for people (and this doesn't even bring up other issues with defining binary genders).
SELECT r.id, r.number, r.name,
(SELECT COUNT(*)
FROM men m
WHERE m.status = 'ADULT' AND r.id = m.roomid
) as CountMen,
(SELECT COUNT(*)
FROM women w
WHERE w.status = 'ADULT' AND r.id = w.roomid
) as CountWomen
FROM Rooms r
WHERE r.type = 'LUX' AND r.active = true AND r.id IN (1, 2, 3);
However, you should fix your data model so you have people rather than segregated gender tables.
Related
I am creating a database to store music.
There are different categories that each have sub categories. Example:
id name parentID
1 instrumentation null
2 solo_instrument null
3 Concert Band 1
4 Brass Band 1
5 Fanfare Band 1
6 Clarinet 2
7 Saxophone 2
8 Trumpet 2
On the other hand I have a table that stores the musicID that is linked to a categoryID
id categoryID musicID
1 4 1
2 8 1
3 3 2
4 6 2
I need the following result from a query:
musicID instrumentation solo_instrument
1 Brass Band Trumpet
2 Concert Band Clarinet
I have been told to use a tree structure as in the future it is likely that other categories are added and this should be able to support that. However, I am not able to figure out how to write a query to get the result above.
I kind of get the result I want when selecting first the instrumentation, second the solo_instrument, but this is all hardcoded and does not allow for music tracks to only have one parentID as I select them individually.
Is this even possible or should I overhaul my database structure? I'd like to see your recommendations.
You should be able to tackle this using conditional aggregation.
Query :
SELECT
mu.musicID,
MAX(CASE WHEN cp.name = 'instrumentation' THEN ca.name END) instrumentation,
MAX(CASE WHEN cp.name = 'solo_instrument' THEN ca.name END) solo_instrument
FROM
musics mu
INNER JOIN categories ca ON ca.id = mu.categoryID
INNER JOIN categories cp ON cp.id = ca.parentID
GROUP by mu.musicID
The INNER JOINs pull up the corresponding category, and then goes up one level to find the parent category. If new root categories are created, you would just need to add more MAX() columns to the query.
In this DB Fiddle demo with your sample data, the query returns :
| musicID | instrumentation | solo_instrument |
| ------- | --------------- | --------------- |
| 1 | Brass Band | Trumpet |
| 2 | Concert Band | Clarinet |
First you group by musicid in table_music and the join twice to table_categories:
select t.musicid, c1.name instrumentation, c2.name solo_instrument
from (
select musicid, min(categoryid) instrumentationid, max(categoryid) solo_instrumentid
from table_music
group by musicid
) t inner join table_categories c1
on c1.id = t.instrumentationid
inner join table_categories c2
on c2.id = t.solo_instrumentid
order by t.musicid
I have a simple configuration :
2 tables linked in a many-to-many relation, so it gave me 3 tables.
Table author:
idAuthor INT
name VARCHAR
Table publication:
idPublication INT,
title VARCHAR,
date YEAR,
type VARCHAR,
conference VARCHAR,
journal VARCHAR
Table author_has_publication:
Author_idAuthor,
Publication_idPublication
I am trying to get all the authors name that have published at least 2 papers in conference SIGMOD and conference PVLDB.
Right now I achieved this but I still have a double result. My query :
SELECT author.name, publication.journal, COUNT(*)
FROM author
INNER JOIN author_has_publication
ON author.idAuthor = author_has_publication.Author_idAuthor
INNER JOIN publication
ON author_has_publication.Publication_idPublication = publication.idPublication
GROUP BY publication.journal, author.name
HAVING COUNT(*) >= 2
AND (publication.journal = 'PVLDB' OR publication.journal = 'SIGMOD');
returns
+-------+---------+----------+
| name | journal | COUNT(*) |
+-------+---------+----------+
| Renee | PVLDB | 2 |
| Renee | SIGMOD | 2 |
+-------+---------+----------+
As you can see the result is correct but doubled, as I just want 1 time the name.
Other question, how to modify the number parameter for only one conference, for example get all the author that published at least 3 SIGMOD and at least 1 PVLDB ?
If you don't care about the journal , don't select it, it is splitting your results. Also, normal filters need to be placed in the WHERE clause, not the HAVING clause :
SELECT author.name, COUNT(*)
FROM author
INNER JOIN author_has_publication
ON author.idAuthor = author_has_publication.Author_idAuthor
INNER JOIN publication
ON author_has_publication.Publication_idPublication =
publication.idPublication
WHERE publication.journal IN('PVLDB','SIGMOD')
GROUP BY author.name
HAVING COUNT(CASE WHEN publication.journal = 'SIGMOD' THEN 1 END) >= 2
AND COUNT(CASE WHEN publication.journal = 'PVLDB' THEN 1 END) >= 2;
For the second question, use this HAVING() clause :
HAVING COUNT(CASE WHEN publication.journal = 'SIGMOD' THEN 1 END) >= 3
AND COUNT(CASE WHEN publication.journal = 'PVLDB' THEN 1 END) >= 1;
So basically, I require a query that will return display name, amount of kills and amount of deaths.
I have two tables that I need to pull from.
The two tables are
player
id | name
2334324 | user
4353454 | user2
where id is their unique identifier and name is their display name.
The second table is:
player_kill
id | killer | victim |
1 | 2334324 | 4353454 |
2 | 2334324 | 4353454 |
3 | 4353454 | 2334324 |
where killer / victim columns contain the unique identifier of the player table.
I'd like to be able to count the occurrences of player id in the killer and victim so that the query returns:
name | kills | deaths
user | 2 | 1
user2| 1 | 2
where the number under kills would be the amount of occurrences the playerid has in the killer column and same for deaths
Hope I provided enough information.
What I have so far:
SELECT `player`.`name`, COUNT(DISTINCT `player_kill`.`id`) as `kills`, COUNT(DISTINCT `player_kill`.`id`) as `deaths`
FROM `player`
LEFT JOIN `player_kill` ON `player`.`id`=`player_kill`.`killer`
LEFT JOIN `player_kill` ON `player`.`id`=`player_kill`.`victim`
WHERE `player`.`id` = `player_kill`.`killer` AND `player`.`id` = `player_kill`.`victim`
GROUP BY `player`.`id`;
Try
SELECT
p.name,
count(distinct pk1.id) as kills,
count(distinct pk2.id) as deaths
FROM player p
LEFT JOIN player_kill pk1 ON pk1.killer = p.id
LEFT JOIN player_kill pk2 ON pk2.victim = p.id
group by p.name
http://sqlfiddle.com/#!9/649504/15/0
See if this works:
SELECT `player`.`name`,
COUNT(DISTINCT k.`id`) as `kills`,
COUNT(DISTINCT v.`id`) as `deaths`
FROM `player`
LEFT JOIN `player_kill` AS k ON `player`.`id` = k.`killer`
LEFT JOIN `player_kill` AS v ON `player`.`id` = v.`victim`
GROUP BY `player`.`id`;
If not, then we may need to make the COUNTs into subqueries.
I have a database with two tables, matches and teams. The teams table is simple, it contains a field with a list of 20 teams. The matches table records the matches played between teams. The fields are as follows (with an example of data):
+------+----------+-------------+-----------+-----------+--------+---------+
| Week | HomeTeam | AwayTeam | HomeScore | AwayScore | Result | MATCHID |
+------+----------+-------------+-----------+-----------+--------+---------+
| 1 | Stoke | Aston Villa | 0 | 1 | A | 151 |
+------+----------+-------------+-----------+-----------+--------+---------+
My question is, how can I return useful data from this in the form of points earned, etc, for each team? I can calculate points earned for a given team but not for all teams in a table. For instance, I can calculate the points for Liverpool with
SELECT
(SELECT COUNT(matches.Result)*3
FROM matches
WHERE (HomeTeam='Liverpool' AND Result='H') OR (AwayTeam='Liverpool' AND Result='A'))+
(SELECT COUNT(matches.Result)
FROM matches
WHERE (HomeTeam='Liverpool' OR AwayTeam='Liverpool') AND Result='D');
which will spit out an output of '21'. But what I actually want is a list of teams (as defined by the 'teams' table) and the points earned by each. How do I go about getting that?
You need to use GROUP BY to get each individual's teams statistics. Then you can use conditional aggregation to get your desired results.
Here's an example with SUM and CASE:
SELECT
t.teamname,
3*SUM(
CASE WHEN t.teamname = m.hometeam AND m.result = 'H' THEN 1
WHEN t.teamname = m.awayteam AND m.result = 'A' THEN 1
ELSE 0
END) +
SUM(
CASE WHEN m.result = 'D' THEN 1
ELSE 0
END)
FROM teams t
JOIN matches m ON t.teamname in (hometeam,awayteam)
GROUP BY
t.teamname
SQL Fiddle Demo
I have two tables
prj
id | ptitle
1 | prj111
2 | prj222
prjflow
id | pid | paction | pactiontxt
1 | 1 | 1 | man1
2 | 1 | 1 | man2
3 | 1 | 2 | woman1
4 | 1 | 1 | man3
i want this output:
output
ptitle | men | women
prj111 | man1,men3 | woman1
i write this query:
SELECT prj.ptitle
, GROUP_CONCAT(pflow1.pactiontxt) men
, GROUP_CONCAT(pflow2.pactiontxt) women
FROM prj
JOIN prjflow pflow1
ON prj.id = pflow1.pid
AND pflow1.paction = 1
JOIN prjflow pflow2
ON prj.id = pflow2.pid
AND pflow2.paction = 2;
but output is:
ptitle | men | women
prj111 | man1,men3 | woman1,woman1
My Query when the number of rows of men and women have been equal, working properly
but
i want that works at any case.
thanks a lot
and excuse me for poor english writing
Just use conditional aggregation:
SELECT prj.ptitle,
GROUP_CONCAT(CASE WHEN prjflow.paction = 1 THEN prjflow.pactiontext END ORDER BY prjflow.id) as men,
GROUP_CONCAT(CASE WHEN prjflow.paction = 2 THEN prjflow.pactiontext END ORDER BY prjflow.id) as women
FROM prj JOIN
prjflow
ON prj.id = prjflow.pid
GROUP BY prj.ptitle;
This will also fix two potential problems with your query. The first is performance. If some of the titles have large numbers of men and women, then the query has to process a cartesian product. The second is semantic. If some titles have men but not women or women without men, then the two joins will filter them out.
Here is a SQL Fiddle demonstrating it.
Do note that the suggested output seems inconsistent with the input data. This produces the output:
ptitle | men | women
prj111 | man1,man2,men3 | woman1
I see no reasonable way to exclude man2 from the list, so I assume that is a typo.
The 'quick fix' would be to use distinct in the group_concat aggregation:
SELECT
prj.ptitle,
group_concat(distinct pflow1.pactiontxt) AS men,
group_concat(distinct pflow2.pactiontxt) AS women
FROM prj
JOIN prjflow AS pflow1 ON (prj.id = pflow1.pid AND pflow1.paction = 1)
JOIN prjflow AS pflow2 ON (prj.id = pflow2.pid AND pflow2.paction = 2)
GROUP BY
prj.ptitle -- Officially you'll need this as well, although MySQL is quite forgiving.
But the query still uses two joins, and will even fail if there a no men or no women for a particular project.
So to solve that, you can write the query with subqueries. This way, you won't need joins distinct or group by, and the query will work too when an item has no men or no women at all.
SELECT
prj.ptitle,
( SELECT group_concat(pflow1.pactiontxt) FROM prjflo pflow1
WHERE prj.id = pflow1.pid AND pflow1.paction = 1) AS men,
( SELECT group_concat(pflow2.pactiontxt) FROM prjflo pflow2
WHERE prj.id = pflow2.pid AND pflow2.paction = 2) AS women
FROM prj