Trying to sum and group two sets of results from three SQL tables - mysql

I am having some major difficulties with grouping and summing results of a query in the way I want. I am having trouble explaining what I want to do in words, so I'll just show you. I have three tables: A, H, and W which look like (simplified):
Table A:
Aid name
1 adam
2 bob
Table H:
Hid Wid date atk Aid
1 1 - 10 2
2 2 - 1 1
3 2 - 5 1
4 1 - 2 2
5 1 - 22 1
6 2 - 7 2
Table W:
Wid name user pass
1 charlie - -
2 donald - -
I am trying to get the SUM of atk grouped by Aid and Wid. Basically, assume this is a fight club tally. I want to display the sum of how many times person W attacked person A (it will always be a one directional fight, ie: charlie can only attack adam, but adam can't attack charlie). (not really a fight club - being used for an online game :))
I am trying to get my result to look like:
name1 atk name2
charlie 22 adam
charlie 12 adam
donald 6 bob
donald 7 bob
My current query looks like...
SELECT w.name AS name1, h.atk, a.name AS name2
FROM H
JOIN W ON w.Wid=h.Wid
JOIN A ON a.Aid=h.Aid
...which gives me every instance that name1 attacked name2. When I try to GROUP BY and/or SUM(h.atk) it is grouping or summing in a way I can't figure out. I'm just not understanding how to accomplish this. Any help would be greatly appreciated.
Thanks in advance!

SELECT w.name AS name1, sum(h.atk) as atk, a.name AS name2
FROM H
JOIN W ON w.Wid=h.Wid
JOIN A ON a.Aid=h.Aid
GROUP BY w.name, a.name

Related

Find count of value A that does not have value B

ROW - NAME - BRAND
____________________
1 - John - Ford
2 - John - BMW
3 - John - Merc
4 - Mary - Ford
5 - Mary - VW
6 - James - Ford
7 - James - Jeep
8 - James - Lexus
9 - Susan - Jeep
10 - Susan - Lexus
How do I find the values in Column A that does not have a particular value in Column B.
Column A, let's say Name, is not unique, there are multiple rows with the same value in A (as the same person can have multiple cars)
Column B, let's say Car Brand, shows the brand of car that the person has in A. There are only a total of 5 brands possible and ideally everyone should have one of the brands - Ford.
How do I find out all the unique Names for people who have other car types, but are missing the Ford? In the above example I'm looking to find Susan.
You can use aggregation:
select name
from t
group by name
having sum(brand = 'Ford') = 0;
The having clause counts the number of rows that match Ford. The = 0 says there are no such rows.
I hope you are talking about this.
SELECT DISTINCT name
FROM tablename
WHERE brand <> 'FORD';
If you need names with all 4 other cars but with no Ford:
SELECT DISTINCT name FROM t
WHERE (SELECT COUNT(DISTINCT car) FROM t AS t2 WHERE t2.name=t.name)=4
AND (SELECT COUNT(*) FROM t AS t2 WHERE t2.name=t.name AND t2.car="Ford")=0
There are a lot of ways, but one answer would be:
SELECT DISTINCT A
FROM T
WHERE A NOT IN (
SELECT A
FROM T
WHERE B = 'Ford'
);

Querying table with dates in SQL

I need to query a MySQL database table that looks like this:
Bike Owner Date
-----------------------
1 Oscar 2014-02-02
2 Oscar 2014-02-02
3 John 2014-04-28
4 Jane 2014-05-29
2 John 2015-04-16
3 Mike 2015-06-16
1 Bob 2015-07-16
4 John 2015-08-16
2 Mike 2016-04-16
3 John 2016-04-16
It contains 4 bikes and as soon as the bike switches from owner, a new record is placed within this table. Since this table doesn't have an 'until' attribute, I am stuck creating a query that gets all the owners of all bikes at a specific date, say 2015-07-06.
Does anyone now how to do this in an easy way? I can think of ways of doing this by creating functions or temporary tables, but this seems way too far fetched for such an easy question.
Thanks!
One method uses an explicit join and aggregation:
select b.*
from (select bike, max(date) as maxdate
from bikes b
where date <= '2015-07-06
group by bike
) bd join
bikes b
on b.bike = bd.bike and b.date = bd.maxdate;
A somewhat shorter way to write this:
select b.*
from bikes b
where b.date = (select max(b2.date) from bikes b2 where b2.bike = b.bike);
If I understood your requirement correctly then it is very straight foreword query, getting all owner of bike on specific date
select b.*
from bikes b
where CAST(b.date AS DATE) = '2015-07-06'

SQL Query to match unlinked data

Say I have three tables:
TABLE A
idA variable
1 Number of hats
2 Number of scarves
3 Number of mittens
TABLE B
idB name
1 Andy
2 Betty
3 Cedric
4 Daphne
TABLE C
idA idB value
1 1 15
1 2 2
1 3 89
2 1 10
2 3 3
2 4 1504
3 2 12
3 3 4
3 4 1
Looking at the table, it's relatively simple to work out - we know how many hats (2) and mittens (12) that she owns, but not how many scarves. Likewise, for Daphne we know how many scarves (1504) and mittens (1) she owns, but not the amount of hats.
However, I'd like a list of fields that there ISN'T information for - I would have a returned result looking something like this (if I asked for Andy)
idA variable
3 Number of mittens
Any idea how I do that? :)
The following query works:
SELECT B.name, A.variable
FROM B
CROSS JOIN A
LEFT JOIN C ON C.idA = A.idA AND C.idB = B.idB
WHERE C.value IS NULL
Its the CROSS JOIN that is key, it says JOIN every record in B to every record in A. Once you've done that you can easily check which combinations of idA and idB don't have a corresponding record in C.
Tested on SQLFiddle
Result:
NAME UNKNOWN VARIABLE
-------------------------------
Andy Number of mittens
Betty Number of scarves
Daphne Number of hats
You can use joins to associate 2 tables.
In your case, if you ask for Andy and you wanna know the number of mittens, you'll have:
SELECT name, value
FROM B
INNER JOIN C on B.idB = C.idB
WHERE id.A = 3
Responding to your comment, you try something like that:
SELECT name, variable
FROM B
RIGHT JOIN C on B.idB = C.idB
RIGHT JOIN A on C.idA = A.idA
WHERE C.idA IS NULL
select idA, variable
from a
where idA not in (select idA from c where idB = 1)

MySQL query table filtering issue

I've been struggling on the following.
I have 3 tables: players, players_clothes, and teams_clothes.
Table players:
id user team_id
1 tom 4
2 robo 5
3 bob 4
So tom and bob are both on the same team
Table players_clothes:
id clothes_id p_id
1 13 1
2 35 3
3 45 3
Bob has clothing article 35 and 45, robo has none.
Table teams_clothes:
id clothes_id team_id
1 35 4
2 45 4
3 55 4
4 65 5
This shows which teams have rights to which articles of clothing. The problem: tom is wearing an article of clothing that does no belong to his team... Let's assume this is illegal.
I'm having trouble figuring out how to capture all those who are wearing illegal clothes for a particular team.
SELECT pc.clothes_id FROM players AS p
JOIN players_clothes AS pc
ON p.id = pc.p_id
AND p.team_id = 4 GROUP BY pc.clothes_id
(I group by players_clothes.clothes_id because believe it or not, two players can be assigned the same piece of clothing)
I think this results the following set (13, 35, 45)
Now I would like to check against the actual set of clothes that team 4 owns.
SELECT clothes_id FROM teams_clothes WHERE team_id = 4 and this return (35, 45, 55)
How can I create a query so that it returns (13)? I've tried things like NOT EXISTS IN but I think the GROUP BY players_clothes.clothes_id part gets in the way
I suggest
select * from A where team_id = $team_id join B on B.a_id = A.id
where not exists
(
select 1 from C where C.clothes_id = B.clothes_id and team_id = $team_id
)
Basically, we find all As who are on their team and for each A join to all clothing they wear, and then only return the row IF we can't find indication in table C that the clothing is on our team (this covers not existent in C and exists but in the wrong team on C)
This should do the trick:
SELECT b.a_id, b.clothes_id
FROM
b INNER JOIN a
ON b.a_id = a.id
LEFT OUTER JOIN c
ON a.team_id = c.team_id
WHERE
c.clothes_id = NULL
The thought is to do an outer join on the combination of tables A/B against table C. And then only look for the cases where c.clothes_id is NULL, which would represent those cases where there is no relational match on the outer join (i.e. the clothes item is not approved for that user's team).
Not sure if this is too late for you, but I'd change the database model itself to make this situation impossible in the first place:
("Unimportant" fields omitted for brevity, including surrogate keys such as PLAYER_ID.)
Note how TEAM_ID migrates through the identifying relationship from TEAM to PLAYER, and then to the PLAYER_ARTICLE, where it merges with the same field migrated through the TEAM_ARTICLE. Since there is only one physical TEAM_ID field in the PLAYER_ARTICLE table, you can never insert a row that would reference different teams.
To put it in more abstract terms: this is a diamond-shaped dependency, where TEAM is at the top and PLAYER_ARTICLE at the bottom of the diamond. The merger at the bottom (enabled by the usage of identifying relationships) ensures sides must always point to the same top.
Your example data would be represented like this...
PLAYER:
TEAM_ID PLAYER_NO
4 1 -- Tom
5 1 -- Robo
4 2 -- Bob
TEAM_ATRICLE:
TEAM_ID ARTICLE_ID
4 35
4 45
4 55
5 65
PLAYER_ARTICLE:
TEAM_ID PLAYER_NO ATRICLE_ID
4 1 13 -- Tom: this is impossible (FK violation).
4 2 35 -- Bob
4 2 45 -- Bob

MySQL Join Multiple (More than 2) Tables with Conditions

Assume I have 4 tables:
Table 1: Task
ID Task Schedule
1 Cut Grass Mon
2 Sweep Floor Fri
3 Wash Dishes Fri
Table 2: Assigned
ID TaskID (FK) PersonID (FK)
1 1 1
2 1 2
3 2 3
4 3 2
Table 3: Person
ID Name
1 Tom
2 Dick
3 Harry
Table 4: Mobile
ID PersonID (FK) CountryCode MobileNumber
1 1 1 555-555-5555
2 2 44 555-555-1234
3 3 81 555-555-5678
4 3 81 555-555-0000
I'm trying to display the
Task on a certain day
Name of person assigned to task
Phone numbers of said person
I think it should be something like the following, but I'm not sure how to set up the conditions so that the results are limited correctly:
SELECT T.ID, T.Task, P.Name, M.MobileNumber
FROM Task AS T
LEFT JOIN Assigned AS A
ON T.ID = A.TaskID
LEFT JOIN Person AS P
ON A.PersonID = P.ID
LEFT JOIN Mobile AS M
ON M.PersonID = P.ID
WHERE T.Schedule = Fri
My goal is to fetch the following information (it will be displayed differently):
Tasks Name MobileNumber
Sweep Floor, Wash Dishes Dick, Harry 44-555-555-1234, 81-555-555-5678, 81-555-555-0000
Of course, if JOIN is the wrong way to do this, please say so.
It's unclear what you want to do with duplicate data in this case, but you should be looking at using inner joins instead of outer joins, and using something like group_concat() to combine the phone numbers.