SQL - Aggregate all EXCEPT group - mysql

Consider the following sample table from a soccer tournament (let's call this table matches)
+----------+---------+--------------+
| match_id | club_id | goals_scored |
+----------+---------+--------------+
| 1 | 1 | 1 |
| 1 | 2 | 0 |
| 2 | 1 | 1 |
| 2 | 3 | 1 |
| 3 | 1 | 0 |
| 3 | 4 | 2 |
| 4 | 2 | 2 |
| 4 | 3 | 4 |
| 5 | 2 | 4 |
| 5 | 4 | 0 |
| 6 | 3 | 1 |
| 6 | 4 | 1 |
+----------+---------+--------------+
The resulting table we want should give us each club's total goals scored AND goals conceded:
+---------+--------------+----------------+
| club_id | goals_scored | goals_conceded |
+---------+--------------+----------------+
| 1 | 2 | 4 |
| 2 | 6 | 4 |
| 3 | 6 | 4 |
| 4 | 3 | 5 |
+---------+--------------+----------------+
Getting goals scored is straight forward enough...
SELECT SUM(goals_scored),
club_id
FROM matches
GROUP BY club_id
but I am absolutely flummoxed as to how to get it for each team's opponents.
I could, of course, construct a pretty complex array of subqueries to get there. If this were application-side work I'd likely just stuff it in a loop and iterate over each club to get there, but my use case requires a SQL answer if possible. Any thoughts?
edit: also if anyone has any better ideas on how to title this question, I'm all ears - I'm not really sure exactly how to describe this problem in the first place.

We can use a self-join approach here:
SELECT
m1.club_id,
SUM(m1.goals_scored) AS goals_scored,
SUM(m2.goals_scored) AS goals_conceded
FROM matches m1
INNER JOIN matches m2
ON m2.match_id = m1.match_id AND
m2.club_id <> m1.club_id
GROUP BY
m1.club_id
ORDER BY
m1.club_id;
This approach brings the goals conceded by each club to the other club, for each match, into a single row. We then just aggregate by club to get the two sums.

Related

Does Cross Join not work between two different tables with same column name?

As written on the title, does CROSS JOIN not work for different tables with the same column name?
For example,
I have one table named Fruits:
| name | price |
| apple | 1 |
| banana | 2 |
and another table named Snacks:
| name | price |
| chips | 3 |
| cookies | 4 |
Then does
SELECT Fruits.price, Snacks.price FROM Fruits CROSS JOIN Snacks
does not work properly?
I am working on a same issue, but the result shows like:
| price | price |
| 3 | 3 |
| 4 | 4 |
| 3 | 3 |
| 4 | 4 |
But what I expect is:
| price | price |
| 1 | 3 |
| 1 | 4 |
| 2 | 3 |
| 2 | 4 |
As I mentioned in the comment, it is not possible. Either your tables values are different or your query.
Check this dbfiddle showing the result value same as your expected values.
In MySQL CROSS JOIN works as expected:
price price
------ -----
1 3
2 3
1 4
2 4
See running example at DB Fiddle.

MySQL Group By - Top 3 rows and nth row

I have a MySQL table like this:
+------+--------+--------+
| ID | UserID | Score |
+------+--------+--------+
| 1 | 3 | 12 |
| 2 | 3 | 11 |
| 3 | 3 | 12 |
| 4 | 2 | 14 |
| 5 | 4 | 8 |
| 6 | 2 | 13 |
+------+--------+--------+
From this I want to top 3 scores from entire table and a top score from particular user
+------+--------+--------+
| ID | UserID | Score |
+------+--------+--------+
| 4 | 2 | 14 |
| 6 | 2 | 13 |
| 3 | 3 | 12 |
| 5 | 4 | 8 |
+------+--------+--------+
Is this something I can get done in a single query?
Any help is appreciated
Thanks in advance!
spoiler alert ;)
(SELECT * FROM tableA ORDER BY score DESC LIMIT 3)
UNION
(SELECT * FROM tableB WHERE UserID = 4 ORDER BY score DESC LIMIT 1);
For certain definitions of "single query", sure... If you're willing to use sub queries or a union.
The best performance will probably come from two queries, but you can join them in a UNION for convenience if you wish.

MySQL GROUP By Only when Two column matches

I am trying to group a record only if two of the fields repeat themselves.
I am designing a social sharing photo app. users can share, like and comment on thers photo. Each action (share, comment, like) will appear on their friends wall.
The Problem is that when a user do all the three actions, the picture appears three times instead of one with the three action on it.
Data in database is like this (activities_tb)
id | photoID | uiID | action | date
-------------------------------------------
1 | 1 | 2 | like | 01/01/2015
2 | 1 | 2 | share | 02/01/2015
3 | 1 | 4 | share | 03/01/2015
4 | 1 | 2 | comment | 04/01/2015
5 | 2 | 4 | like | 04/01/2015
6 | 2 | 2 | like | 05/01/2015
7 | 2 | 3 | share | 05/01/2015
8 | 2 | 4 | comment | 06/01/2015
8 | 3 | 3 | like | 07/01/2015
9 | 3 | 5 | like | 08/01/2015
10 | 3 | 5 | comment | 08/01/2015
The query result I want to get
id | photoID | uiID | action | date
-------------------------------------------
3 | 1 | 4 | share | 03/01/2015
4 | 1 | 2 | comment | 04/01/2015
6 | 2 | 2 | like | 05/01/2015
7 | 2 | 3 | share | 05/01/2015
8 | 2 | 4 | comment | 06/01/2015
8 | 3 | 3 | like | 07/01/2015
10 | 3 | 5 | comment | 08/01/2015
This is my statement
SELECT id, photoID, uiID, action, date
FROM activities_tb
GROUP BY photoID, uiID.
This combines all the photos by their id returning only three results
I will be glad if anyone can be of help, thank you
You can first select required ids and join on your table:
select tb.*
from activities_tb tb
join(select max(id) as id
from activities_tb
group by photoID, uiID) t on t.id = tb.id
You are looking for "SELECT DISTINCT"
SELECT DISTINCT photoID, uiID, action, date
FROM activities_tb
GROUP BY photoID, uiID.

Problems with subquery in MySql

I'm stuck with a problem in MySql, please help me.
In this example I have two tables, one with the results of a bunch of competitors and one that defines which three competitors that makes a team. In reality I have a number of other tables as well, but they are not really needed to describe this problem.
Table with results for each competitor
| competitor_id | result1 | result2 | result3 | result4 |
| 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 2 | 1 |
| 3 | 2 | 3 | 2 | 1 |
| 4 | 1 | 5 | 3 | 2 |
| 5 | 4 | 3 | 2 | 3 |
| 6 | 3 | 2 | 1 | 2 |
| 7 | 2 | 1 | 4 | 2 |
| 8 | 2 | 1 | 2 | 1 |
| 9 | 1 | 2 | 3 | 2 |
Table showing teams
| team_id | competitor1 | competitor3 | competitor3 |
| 1 | 1 | 3 | 4 |
| 2 | 2 | 8 | 9 |
| 3 | 7 | 6 | 5 |
I would now like to create a query that gives me the total sum of each team. I need to have it i one query (maybe with subqueries) because I need to sort desc on the total result.
In other words, I need a result set giving me team.id sorted desc on the total result of each team.
Anyone?
EDIT: Here's an update showing the desired result
First, let´s sum the results of each competitor:
Competitor 1: 1+1+1+1=4
Competitor 2: 1+2+2+1=6
Competitor 3: 2+3+2+1=8
Competitor 4: 1+5+3+2=11
Competitor 5: 4+3+2+3=12
Competitor 6: 3+2+1+2=8
Competitor 7: 2+1+4+2=9
Competitor 8: 2+1+2+1=6
Competitor 9: 1+2+3+2=8
Then let's look at the team table.
Team 1 consists of competitors 1, 3 and 4.
Team 2 consists of competitors 2, 8 and 9.
Team 3 consists of competitors 7, 6 and 5.
Total sum of team with id = 1 is 4+8+11=23
Total sum of team with id = 2 is 6+6+8=20
Total sum of team with id = 3 is 9+8+12=29
Given all of this, I would like my result set to be
| id | team_sum |
| 3 | 29 |
| 1 | 23 |
| 2 | 20 |
Why not redesign your database like you have only two tables one for competitors and one for team like :
Competitors Table:
`competitor_id`, `team_id`, `result1`, `result2`, `result3`, `result4`
Team Table:
`team_id`, `team_name`
And your query would be very easy like:
SELECT A.team_id, B.team_name, SUM(result1+result2+result3+result4) as TotalResult
FROM competitors A
INNER JOIN team B
ON A.team_id=B.team_id
GROUP BY A.team_id, B.team_name
See my fiddle demo

What happens if I select two tables with no WHERE clause?

I had a technical interview last week, and my interviewer asked me what happens if I run the following query:
SELECT * FROM tbl1, tbl2
I think I answered it correctly, but it wasn't an in-depth answer.
I said that I would select all the columns in both tables. For example if tbl1 has 3 columns, and tbl2 has 4 columns. The result set would have 7 columns.
Then he asked me why 7? and I said because I was selecting everything from each table.
That was a bad answer, but I couldn't think of anything else.
To cut to the chase, after the interviewed I executed the latter statement using two tables.
Table A, had 3 animal: dog, cat and elephant.
Table B had 2 names: Mat and Beth
This is the result set that I got after the statement being executed:
*********************************************
| id_tbl1 | name_tbl1 | id_tbl2 | name_tbl2 |
*********************************************
| 1 | dog | 1 | Mat |
| 2 | cat | 1 | Mat |
| 3 | elephant | 1 | Mat |
| 1 | dog | 2 | Beth |
| 2 | cat | 2 | Beth |
| 3 | elephant | 2 | Beth |
*********************************************
So my question is, why does the statement behaves like that?
In other words:
Why does the Table B's records repeat themselves until I reach the end of table A, and then it starts all over again?
How would you have answered the question in a way that it would've "WOW'd" the interviewer?
If this question does not belong to SO, feel free to delete it or close it!
If you do a select like this, all rows in one resultset are joined to all rows in the other resultset (Cartesian Product).
So you get a list of all rows of the first table with the first row of the second table, Then all entries for the second row and so on. The order may be an implementation detail. Not sure if it is defined that the first order is by the first table, it might be different across implementations.
If you join three tables (or more), then the same happens with all rows of all tables. This, of course, is not only for tables, but for any result set from joins.
The result will be a cartisian product
take a look at this example
SQL Example
You can see there are two tables one has 5 records and the other has 4 and the result is 20 records. Means 5 * 4 = 20 instead of 5 + 4 = 9 as you are assuming.
Table1
| IDX | VAL |
---------------
| 1 | 1val1 |
| 1 | 1val2 |
| 2 | 2val1 |
| 2 | 2val2 |
| 2 | 2val3 |
Table2
| ID | POINTS |
---------------
| 1 | 2 |
| 2 | 10 |
| 3 | 21 |
| 4 | 29 |
Result of below query
SELECT * FROM Table1 , Table2
| IDX | VAL | ID | POINTS |
-----------------------------
| 1 | 1val1 | 1 | 2 |
| 1 | 1val1 | 2 | 10 |
| 1 | 1val1 | 3 | 21 |
| 1 | 1val1 | 4 | 29 |
| 1 | 1val2 | 1 | 2 |
| 1 | 1val2 | 2 | 10 |
| 1 | 1val2 | 3 | 21 |
| 1 | 1val2 | 4 | 29 |
| 2 | 2val1 | 1 | 2 |
| 2 | 2val1 | 2 | 10 |
| 2 | 2val1 | 3 | 21 |
| 2 | 2val1 | 4 | 29 |
| 2 | 2val2 | 1 | 2 |
| 2 | 2val2 | 2 | 10 |
| 2 | 2val2 | 3 | 21 |
| 2 | 2val2 | 4 | 29 |
| 2 | 2val3 | 1 | 2 |
| 2 | 2val3 | 2 | 10 |
| 2 | 2val3 | 3 | 21 |
| 2 | 2val3 | 4 | 29 |
I think you are confusing yourself by running an example with two tables that have identical fields. You are referring to a Union, which will combine the values of 1 table with another, and using your example this would give you 3 + 4 = 7 results.
The comma separated FROM statement is doing JOIN, which will go through all values in Table X and pair them with all the values of Table Y. This would result in Size of X * Size of Y results, and using your example this would be 3 * 4 = 12.