Getting data from a table meeting some requirements - mysql

I'm stuck in a SQL logic problem.
I have a table "games" with some data where player1_id and player2_id are user ids that played with each other on a checkers match:
Each game starts with a different ambient, already specified in another table (keyed by "game_id" column).
Assuming that everybody is online, an user can't replay the same game.
How can I select only users that wasn't played the games I didn't played yet to match with me?
An AJAX call will match two users that didn't played an specific game yet and put them to play.
No matter if the user was player1 or player2 , he can't repeat a game.
Thank you very much.
Tables:
game_match
+----+---------+------------+------------+
| id | game_id | player1_id | player2_id |
+----+---------+------------+------------+
| 1 | 1 | 16 | 17 |
| 2 | 1 | 18 | 23 |
| 3 | 1 | 19 | 21 |
| 4 | 1 | 20 | 22 |
| 5 | 2 | 20 | 17 |
| 6 | 2 | 16 | 18 |
| 7 | 2 | 19 | 23 |
| 8 | 1 | 25 | 15 |
+----+---------+------------+------------+
Users
+----+-----------+
| id | name |
+----+-----------+
| 17 | Donald |
| 18 | Margarida |
| 19 | Daisy |
| 20 | Mickey |
| 21 | Steve |
| 22 | Raul |
| 23 | Janis |
| 24 | Michael |
| 25 | Sergio |
| 26 | Bill |
| 27 | Alina |
| 28 | Alana |
| 29 | Harumi |
| 30 | Danielle |
| 31 | Lisa |
+----+-----------+
Thank you!

Assuming you have a table named games and a $userid, you can do this readily with not exists:
select g.*
from games g
where not exists (select 1
from game_match gm
where gm.game_id = g.game_id and gm.player1_id = $userid
) or
not exists (select 1
from game_match gm
where gm.game_id = g.game_id and gm.player2_id = $userid
) ;
Note that you can implement the same logic with or and one subquery. However, with the right indexes, two subqueries are more efficient.

To get your users I would use a union in a subquery:
select *
from users
where id not in
(select player1_id as played
from game_match
where player2_id = #logged_in_user
union
select player2_id
from game_match
where player1_id = #logged_in_user
) as played

Try the following solution using NOT EXISTS and INNER JOIN:
select name
from Users AS u
where not exists(
select 1
from game_match AS gm INNER JOIN
(select distinct game_id
from game_match
where #current_user_id = player1_id OR #current_user_id = player2_id) gids
USING(game_id)
where u.id = gm.player1_id OR u.id = gm.player2_id
);

Related

Get distinct records of two columns in a join with 6 columns

I have two MySQL tables SPONSORSHIPS and EVENTS. I want to display a list of SPONSORSHIPS sorted by the category of the events they sponsor, but to only show a sponsorship once under each event. Sample join table:
SPONSORSHIPS
sponsorhipid | sponsorid | eventid | date |
-------------|-----------|---------|------------|
1 | 3 | 20 | 06/01/2013 |
2 | 2 | 20 | 06/02/2013 |
3 | 3 | 20 | 06/03/2013 |
4 | 2 | 21 | 06/04/2013 |
EVENTS
eventid | name | premium |
--------|-----------|------------|
20 | Lunch | 0 |
21 | Dinner | 1 |
What I'd like to have as a result of the JOIN is:
sponsorhipid | sponsorid | eventid | date | name | premium |
-------------|-----------|---------|------------|---------| ---------|
1 | 3 | 20 | 06/01/2013 | Lunch | 0 |
2 | 2 | 20 | 06/02/2013 | Lunch | 0 |
4 | 2 | 21 | 06/04/2013 | Dinner | 1 |
I tried DISTINCT and GROUP BY but these collapse the events so if sponsor #2 sponsors two different events they'd still be shown only once. How can I achieve this? Here is my last SQL query:
SELECT DISTINCT (sponsorships.sponsorshipid), sponsorships.*, events.*
FROM events
INNER JOIN sponsorships
ON events.eventid = sponsorships.eventid
Thanks so much for any pointers!
You need to use nested sub-queries like this:
SELECT s.sponsorhipid, s.sponsorid, s.eventid, s.date
,e.name, e.premium
FROM EVENTS e
JOIN
(
SELECT s1.* FROM SPONSORSHIPS s1
JOIN
(
SELECT sponsorid, MIN(Date) As minDate
FROM SPONSORSHIPS
GROUP BY eventid,sponsorid
) s2
ON s1.sponsorid = s2.sponsorid
AND s1.date = s2.minDate
) s
ON e.eventid = s.eventid;
Output:
| SPONSORHIPID | SPONSORID | EVENTID | DATE | NAME | PREMIUM |
|--------------|-----------|---------|------------|--------|---------|
| 1 | 3 | 20 | 06/01/2013 | Lunch | 0 |
| 2 | 2 | 20 | 06/02/2013 | Lunch | 0 |
| 4 | 2 | 21 | 06/04/2013 | Dinner | 1 |
See this SQLFiddle

How can I SELECT rows from a table when I MAX(ColA) and GROUP BY ColB

I found this question which is very similar but I'm still having some troubles.
So I start with table named Scores
id | player | time | scoreA | scoreB |
~~~|~~~~~~~~|~~~~~~|~~~~~~~~|~~~~~~~~|
1 | John | 10 | 70 | 80 |
2 | Bob | 22 | 75 | 85 |
3 | John | 52 | 55 | 75 |
4 | Ted | 39 | 60 | 90 |
5 | John | 35 | 90 | 90 |
6 | Bob | 27 | 65 | 85 |
7 | John | 33 | 60 | 80 |
I would like to select the best average score for each player along with the information from that record. To clarify, best average score would be the highest value for (scoreA + scoreB)/2.
The results would look like this
id | player | time | scoreA | scoreB | avg_score |
~~~|~~~~~~~~|~~~~~~|~~~~~~~~|~~~~~~~~|~~~~~~~~~~~|
5 | John | 35 | 90 | 90 | 90 |
2 | Bob | 22 | 75 | 85 | 80 |
4 | Ted | 39 | 60 | 90 | 75 |
Based on the question I linked to above, I tried a query like this,
SELECT
s.*,
avg_score
FROM
Scores AS s
INNER JOIN (
SELECT
MAX((scoreA + scoreB)/2) AS avg_score,
player,
id
FROM
Scores
GROUP BY
player
) AS avg_s ON s.id = avg_s.id
ORDER BY
avg_score DESC,
s.time ASC
What this actually gives me is,
id | player | time | scoreA | scoreB | avg_score |
~~~|~~~~~~~~|~~~~~~|~~~~~~~~|~~~~~~~~|~~~~~~~~~~~|
1 | John | 10 | 70 | 80 | 90 |
2 | Bob | 22 | 75 | 85 | 80 |
4 | Ted | 39 | 60 | 90 | 75 |
As you can see, it has gotten the correct max avg_score, from record 5, but gets the rest of the information from another record, record 1. What am I missing? How do I ensure that the data all comes from the same record? I'm getting the correct avg_score but I want the rest of the data associated with that record, record 5 in this case.
Thanks in advance!
SELECT x.*
, (scoreA+scoreB)/2 avg_score
FROM scores x
JOIN
( SELECT player, MAX((scoreA+scoreB)/2) max_avg_score FROM scores GROUP BY player) y
ON y.player = x.player
AND y.max_avg_score = (scoreA+x.scoreB)/2;
Try
SELECT s.*,
q.avg_score
FROM scores s JOIN
(
SELECT player,
MAX((scoreA + scoreB)/2) AS avg_score
FROM scores
GROUP BY player
) q ON s.player = q.player
AND (s.scoreA + s.scoreB)/2 = q.avg_score
ORDER BY q.avg_score DESC, s.time ASC
Sample output:
| ID | PLAYER | TIME | SCOREA | SCOREB | AVG_SCORE |
----------------------------------------------------
| 5 | John | 35 | 90 | 90 | 90 |
| 2 | Bob | 22 | 75 | 85 | 80 |
| 4 | Ted | 39 | 60 | 90 | 75 |
Here is SQLFiddle demo

Join multiple tables and result multiple records

I have 3 tables with information.
Table1: Orders
+---------+----------------+------------+---------------+
| OrderID | OrderDate | Community | Status |
+-------------------------------------------------------+
1 | 1 march 2013 | S1 | Approved
2 | 5 march 2013 | S2 | Aporoved
3 | 7 march 2013 | Z1 | Approved
+-------------------------------------------------------+
Table2: OrderArtickles
+------------------------------------------------------------------+
|Ordertitem | OrderID | ArtikelID | UnitPrice | Delivered |
+------------------------------------------------------------------+
| 1 | 1 | 20 | 5 | yes
| 2 | 1 | 20 | 5 | yes
| 3 | 2 | 21 | 10 | yes
| 4 | 3 | 30 | 50 | yes
+-------------------------------------------------------------------+
Table3: users
+-----------------------------------------------------+
| Userid | Username | Community | Department |
+-----------------------------------------------------+
| 1 | User1 | S1 | S
| 2 | User2 | S2 | S
| 3 | User3 | Z1 | Z
+-----------------------------------------------------+
I need a MySQL query that give the following output:
+--------------------------------------+
| Department | TotalPriceOfArtikels
+--------------------------------------+
| S | 20
| Z | 50
+--------------------------------------+
I tried with JOIN, SUM, GROUP BY but without result. The problem that I have is that the one order gives multiple articles. Who can help me?
try this
select Department , sum(UnitPrice) as TotalPriceOfArtikels
from users u
inner join Orders o
on o.Community = u.Community
inner join OrderArtickles oa
on oa.OrderId = o.OrderId
group by Department
DEMO HERE
OUTPUT:
Department TotalPriceOfArtikels
S 20
Z 50
Something like this:
select us.Department,
sum ( art.UnitPrice ) as TotalPriceOfArtikels
from user us
left join orders ors
on ( us.Community = ors.Community)
left join OrderArtickles art
on ( ors.OrderID = art.OrderID)
group by us.Department
------------------EDITED------------------------
I copy the same values and structures of your tables in my mysql, and the result is fine, it give me:
Department TotalPriceOfArtikels
S 20
Z 50
Maybe you want to check a condition, like if delivered = yes or status = aprobed??....with this query the result it's the same that you have posted ;)
Saludos ;)

Joining 3 tables

First off, sorry if this is a near enough duplicate. I've found this question, which nearly does what I want, but I couldn't wrap my head around how to alter it to my needs.
I've got these 3 tables:
cs_Accounts:
+----+-----------------------------+-------------+
| id | email | username |
+----+-----------------------------+-------------+
| 63 | jamasawaffles#googlil.com | jamwaffles2 |
| 64 | jamwghghhfles#goomail.com | jamwaffles3 |
| 65 | dhenddfggdfgetal-pipdfg.com | dhendu9411 |
| 60 | jwapldfgddfgfffles.co.uk | jamwaffles |
+----+-----------------------------+-------------+
cs_Groups:
+----+-----------+------------+-------------+
| id | low_limit | high_limit | name |
+----+-----------+------------+-------------+
| 1 | 0 | 0 | admin |
| 2 | 1 | 50 | developer |
| 3 | 76 | 100 | reviewer |
| 4 | 51 | 75 | beta tester |
| 5 | 1 | 50 | contributor |
+----+-----------+------------+-------------+
cs_Permissions:
+----+---------+----------+
| id | user_id | group_id |
+----+---------+----------+
| 4 | 60 | 4 |
| 3 | 60 | 1 |
| 5 | 60 | 2 |
| 6 | 62 | 1 |
| 7 | 62 | 3 |
+----+---------+----------+
I've been wrestling with a 3 way join for hours now, and I can't get the results I want. I'm looking for this behaviour: a row will be returned for every user from cs_Accounts where there is a row in cs_Permissions that contains their ID and the ID of a group from cs_Groups, as well as the group with the group_id has a high_lmiit and low_limit in a range I can specify.
Using the data in the tables above, we might end up with something like this:
email username cs_Groups.name
----------------------------------------------------------
jwapldfgddfgfffles.co.uk jamwaffles admin
jwapldfgddfgfffles.co.uk jamwaffles developer
jwapldfgddfgfffles.co.uk jamwaffles beta tester
dhenddfggdfgetal-pipdfg.com dhendu9411 admin
dhenddfggdfgetal-pipdfg.com dhendu9411 reviewer
There is an extra condition, however. This condition is where rows are only selected if the group the user belongs to has a high_limit and low_limit with values I can specify using a WHERE clause. As you can see, the table above only contains users with rows in the permissions table.
This feels a lot like homework but with a name like James I'm always willing to help.
select a.email,a.username,g.name
from cs_Accounts a
inner join cs_Permissions p on p.user_id = a.id
inner join cs_Groups g on g.id = p.Group_id
where g.low_limit > 70
and g.high_limt < 120
This is the query
SELECT ac.email, ac.username, gr.name
FROM cs_Accounts AS ac
LEFT JOIN cs_Permissions AS per ON per.user_id = ac.id
INNER JOIN cs_Groups AS gr ON per.user_id = gr.id
You can add a WHERE clause to this query if you want

Complex 3 way SQL join (where table 3 has to join table 2 before joining table 1)

I have three existing SQL tables we will call "teams", "miles", and "riders". Leaving out the fluff, their structure looks like this:
Table: teams
------------+-------------+---------+
| team_name | captains_id | team_id |
------------+-------------+---------+
| superbads | 11 | 1 |
| superflys | 12 | 2 |
------------+-------------+---------+
Table: riders
--------------+-----------+----------+
| rider_name | team_id | rider_id |
--------------+-----------+----------+
| donatello | 1 | 10 |
| leonardo | 1 | 11 |
| michelangelo| 2 | 12 |
| raphael | 2 | 13 |
--------------+-----------+----------+
Table: miles
--------------+-----------+----------+
| rider_id | miles | id |
--------------+-----------+----------+
| 10 | 100 | 1 |
| 10 | 62 | 2 |
| 11 | 110 | 3 |
| 11 | 100 | 4 |
| 12 | 8 | 5 |
| 12 | 22 | 6 |
| 13 | 29 | 7 |
| 13 | 2 | 8 |
--------------+-----------+----------+
I need to return a list of teams with total miles generated by that team (I also need to return the team captain's name, but that's a bit easier).
The difficulty is that I need to join miles on riders, sum the "miles" field, and then join that on teams somehow.
Changing the table structure is pretty much out, as this is an existing application. This is a LAMP environment, so manipulating PHP arrays after the query is an option if needed.
This should do it:
select t.team_id, t.team_name, t.captains_id, sum(m.miles) as total_miles
from teams t
inner join riders r on r.team_id = t.team_id
inner join miles m on m.rider_id = r.rider_id
group by t.team_id, t.team_name, t.captains_id