I'm quite new to SQL and very new (learning today in fact!) how to use JOINS or in particular INNER JOIN. I have read some guides but haven't seen any helpful information for when one has a table with a composite key.
Tables:
Matches
+-----------+------------+
| (MatchID) | StartTime |
+-----------+------------+
| 548576804 | 1393965361 |
| 548494906 | 1393123251 |
+-----------+------------+
And
+-----------------------------------+---------+
| (PlayerID) - (MatchID) | Result |
+-----------------------------------+---------+
| 38440257 548576804 | Win |
| 17164642 548494906 | Loss |
+-----------------------------------+---------+
Of the above tables, The MatchID in table Matches is a Foreign Key.
Problem
The columns in the parenthesis are Keys (so the composite key is in the MatchDetails table). I am trying to pull all of the Matches played by player 38440257, and the StartTime from the Matches table. The first Join I tried worked, however it pulled every game, regardless of player:
SELECT matchdetails.MatchID,
matches.StartTime,
matchdetails.Result
FROM matchdetails,
matches
WHERE matchdetails.MatchID = matches.MatchID
ORDER BY matches.StartTime ASC
Now, I am not sure how to add in the point that I want ONLY matches from a particular playerID in the query. Because the following does not work:
SELECT matchdetails.MatchID,
matches.StartTime,
matchdetails.Result
FROM matchdetails,
matches
WHERE matchdetails.MatchID = matches.MatchID,
matchdetails.PlayerID=76561197998705985
ORDER BY matches.StartTime ASC
In addition, the JOIN I am using above, is there an easier way to write it that I am missing? Or am I not writing a Join at all? I followed one of the queries from here, which stated they were equivalent queries. However it feels rather cumbersome to write.
Please let me know if I have neglected any information.
You just need AND between your predicates:
SELECT matchdetails.MatchID,
matches.StartTime,
matchdetails.Result
FROM matchdetails,
matches
WHERE matchdetails.MatchID = matches.MatchID
AND matchdetails.PlayerID=76561197998705985
ORDER BY matches.StartTime ASC;
However, I would highly recommend you move away from the ANSI 89 JOIN syntax and adopt ANSI 92 instead. As the names suggest, the syntax you have used is over 20 years out of date.
SELECT matchdetails.MatchID,
matches.StartTime,
matchdetails.Result
FROM matchdetails
INNER JOIN matches
ON matchdetails.MatchID = matches.MatchID
WHERE matchdetails.PlayerID=76561197998705985
ORDER BY matches.StartTime ASC;
SELECT matchdetails.MatchID,
matches.StartTime,
matchdetails.Result
FROM matchdetails
JOIN ON matchdetails.MatchID = matches.MatchID
WHERE
matchdetails.PlayerID=76561197998705985
ORDER BY matches.StartTime ASC
Related
I'm writing a query where I need to select student name by who has a MAX gradelevel_id. How ever it still selects the other row with the same id of the student where I already define what gradelevel_id should I select.
schoolyear_id | student_id | gradelevel_id
407 18 307
409 18 309`
Query:
SELECT
student_mt.student_id,
registration_mt.firstname, registration_mt.middlename, registration_mt.lastname,
MAX(grade.gwa)
FROM schoolyear_student_lt
INNER JOIN gradelevel_mt ON gradelevel_mt.gradelevel_id = schoolyear_student_lt.gradelevel_id
INNER JOIN student_mt ON student_mt.student_id = schoolyear_student_lt.student_id
INNER JOIN registration_mt ON registration_mt.registration_id = student_mt.registration_id
INNER JOIN student_grade ON student_grade.student_id = schoolyear_student_lt.student_id
INNER JOIN grade ON grade.grade_id = student_grade.grade_id
WHERE gradelevel_mt.gradelevel_id = 309
GROUP BY student_mt.student_id;
If I define 307 in my WHERE CLAUSE still selects the student name which I should not already see in my row.
Output:
student_id | firstname | middlename | lastname | MAX(grade.gwa)
18 Billie Joe Armstrong 88
(This is more of a comment) I guess you accidentally stumbled on the quirky MySQL behavior of GROUP BY.
When using GROUP BY, in the SELECT clause we can only put the GROUP BY predicate (student_mt.student_id) and aggregate functions (MAX(grade.gwa)). Even though MySQL allows this, the DBEngine assumes you know what you are doing but might result in anomalies.
Why not get take the approach of getting the student_id(pk) for the MAX(grade.gwa) as a inner sub query and then do the INNER JOIN's with other tables to select what you wanted in the outer subquery.
Because I'm working with a framework (Magento) I don't have direct control of the SQL that is actually executed. I can build various parts of the query, but in different contexts its modified in different ways before it goes to the database.
Here is a simplified example of what I'm working with.
students enrolments
-------- ------------------
id| name student_id| class
--+----- ----------+-------
1| paul 1|biology
2|james 1|english
3| jo 2| maths
2|english
2| french
3|physics
3| maths
A query to show all students who are studying English together with all the courses those students are enrolled on, would be:
SELECT name, GROUP_CONCAT(enrolments.class) AS classes
FROM students LEFT JOIN enrolments ON students.id=enrolments.student_id
WHERE students.id IN ( SELECT e.student_id
FROM enrolments AS e
WHERE e.class LIKE "english" )
GROUP BY students.id
This will give the expected results
name| classes
----+----------------------
paul|biology, english
james|maths, english, french
Counting the number of students who study english would be trivial, if it weren't for the fact that Magento automatically uses portions of my first query. For the count, it modifies my original query as follows:
Removes the columns being selected. This would be the name and classes columns.
Adds a count(*) column to the select
Removes any group by clause
After this butchery, my query above becomes
SELECT COUNT(*)
FROM students LEFT JOIN enrolments ON students.id=enrolments.student_id
WHERE students.id IN ( SELECT e.student_id
FROM enrolments AS e
WHERE e.class LIKE "english" )
Which will not give me the number of students enrolled on the English course as I require. Instead it will give me the combined number of enrolments of all students who are enrolled on the English course.
I'm trying to come up with a query which can be used in both contexts, counting and getting the rows. I get to keep any join clauses and and where clauses and that's about it.
The problem with your original query is the GROUP BY clause. Selecting COUNT(*) by keeping the GROUP BY clause would result in two rows with a number of classes for each user:
| COUNT(*) |
|----------|
| 2 |
| 3 |
Removing the GROUP BY clause will just retun the number of all rows from the LEFT JOIN:
| COUNT(*) |
|----------|
| 5 |
The only way I see, magento could solve that problem, is to put the original query into a subquery (derived table) and count the rows of the result. But that might end up in terrible performance. I would also be fine with an exception, complaining that a query with a GROUP BY clause can not be used for pagination (or something like that). Just return an anexpected result is probably the worst what a library can do.
Well, it just so happens I have a solution. :-)
Use a corelated subquery for GROUP_CONCAT in the SELECT clause. This way you will not need a GROUP BY clause.
SELECT name, (SELECT GROUP_CONCAT(enrolments.class)
FROM enrolments
WHERE enrolments.student_id = students.id
) AS classes
FROM students
WHERE students.id IN ( SELECT e.student_id
FROM enrolments AS e
WHERE e.class LIKE "english" )
However, I would rewrite the query to use an INNER JOIN instead of an IN condition:
SELECT s.name, (
SELECT GROUP_CONCAT(e2.class)
FROM enrolments e2
WHERE e2.student_id = s.id
) AS classes
FROM students s
JOIN enrolments e1
ON e1.student_id = s.id
WHERE e1.class = "english";
Both queries will return the same result as your original one.
| name | classes |
|-------|----------------------|
| paul | biology,english |
| james | maths,english,french |
But also return the correct count when modified my magento.
| COUNT(*) |
|----------|
| 2 |
Demo: http://rextester.com/OJRU38109
Additionally - chances are good that it will even perform better, due to MySQLs optimizer, which often creates bad execution plans for queries with JOINs and GROUP BY.
I have a table named phpbb_pcp_market with these rows: http://pastebin.com/ZAFjawD8 (There are more obviously)
And I have another table named phpbb_pcp_market_cart that looks like this:
+----+---------+-----------+------------+
| id | item_id | player_id | time |
+----+---------+-----------+------------+
| 14 | 49 | 3 | 1384806292 |
+----+---------+-----------+------------+
I need to join these two tables based on item_id, but for some reason it's not working.
This is the query I've used:
SELECT m.*, c.* FROM (phpbb_pcp_market_cart c)
LEFT JOIN phpbb_pcp_market m
ON (c.item_id = m.item_id)
WHERE c.player_id = 3
ORDER BY c.time
For some reason, it's returning nothing.
I can't figure what I did wrong in the query. And no, I'm not good at SQL.
Everything looks fine with your SQL-code.
Look in the rest of your PHP-code if there is something wrong. The bug is not related to the SQL-part ;)
First double check your data, your query seems to be OK.
If you want to select all items for specific player_id don't use LEFT JOIN but simple JOIN, because you will never get rows where it could be NULL.
Also braces can be left out for simplicity:
SELECT m.*, c.* FROM phpbb_pcp_market_cart c
JOIN phpbb_pcp_market m
ON c.item_id = m.item_id
WHERE c.player_id = 3
ORDER BY c.time
I have a query that selects all columns from multiple tables, but it's returning multiples of the same values (I only want distinct values).
How can I incorporate something like this? When I try this, it still
Select Distinct A.*, B.*, C.*....
Does distinct only work when selecting the column names and not all (*) ? In this reference it says distinct in reference to column names, not across all of the tables. Is there any way that I can do this?
edit - I added more info below
Sorry guys, I just got back onto my computer. Also, I just realized that my query itself is the issue, and Distinct has nothing to do with it.
So, the overall goal of my Query is to do the following
Generate a list of friends that a user has
Go through the friends and check their activities (posting, adding friends, etc..)
Display a list of friends and their activities sorted by date (I guess like a facebook wall kind of deal).
Here are my tables
update_id | update | userid | timestamp //updates table
post_id | post | userid | timestamp //posts table
user_1 | user_2 | status | timestamp //friends table
Here is my query
SELECT U.* , P.* ,F.* FROM posts AS P
JOIN updates AS U ON P.userid = U.userid
JOIN friends AS F ON P.userid = F.user_2 or F.user_1
WHERE P.userid IN (
select user_1 from friends where user_2 = '1'
union
select user_2 from friends where user_1 = '1'
union
select userid from org_members where org_id = '1'
union
select org_id from org_members where userid = '1'
)
ORDER BY P.timestamp, U.timestamp, F.timestamp limit 30
The issue I'm having with this (that I thought was related to distinct), is that if values are found to meet the requirements in, say table Friends, a value for the Posts table will appear too. This means when I'm displaying the output of the SQL statement, it appears as if the Posts value is shown multiple times, when the actual values I'm looking for are also displayed
The output will appear something like this (notice difference between post value in the rows)
update_id | update | userid | timestamp | post_id | post | userid | timestamp | user_1 | user_2 | status | timestamp
1 | update1 | 1 | 02/01/2013 | 1 | post1| 1 | 2/02/2013| 1 | 2 | 1 | 01/30/2013
1 | update1 | 1 | 02/01/2013 | 2 | post2| 1 | 2/03/2013| 1 | 2 | 1 | 01/30/2013
So, as you can see, I thought I was having a distinct issue (because update1 appeared both times), but the query actually just selects all the values regardless. I get the results I'm looking for in the Post table, but all the other values are returned. So, when I display the table in PHP/HTML, the Post value will display, but I also get duplicates of the updates (just for this example)
When you select distinct *, you select every row, including the one that makes the record unique. If you want something better than what you are getting, you have to type the individual column names in your select clause.
It would be easy if you explain a little more what is the connection between the tables you'r querying, because you can use joins, unions (as mentioned above) or even group by's ...
Your updated post shows one of the JOIN conditions as:
JOIN friends AS F ON P.userid = F.user_2 OR F.user_1
This is equivalent to:
JOIN friends AS F ON (P.userid = F.user_2 OR F.user_1 != 0)
and will include many rows that you did not intend to include.
You probably intended:
JOIN friends AS F ON (P.userid = F.user_2 OR P.userid = F.user_1)
I think you want this:
select *
from tableA
union
select *
from tableB
union
select *
from tableC
This assumes that HHS tables all have the same number of columns and they are of the same data type. This not, you'll have to select specific columns to make it so.
I've been looking for a solution to this, there's plenty of similar questions but none have any proper answers that helped me solve the problem.
First up, my questions/problem:
I want to sum and count certain columns in a multiple join query
Is it not possible with multiple joins? Do I have to nest SELECT queries?
Here's a SQL dump of my database with sample data: http://pastie.org/private/vq7qkfer5mwyraudb5dh0a
This is the query I thought would do the trick:
SELECT firstname, lastname, sum(goal.goal), sum(assist.assist), sum(gw.gw), sum(win.win), count(played.idplayer) FROM player
LEFT JOIN goal USING (idplayer)
LEFT JOIN assist USING (idplayer)
LEFT JOIN gw USING (idplayer)
LEFT JOIN win USING (idplayer)
LEFT JOIN played USING (idplayer)
GROUP BY idplayer
What I'd like this to produce is a table where the columns for goal, assist, gw, win and played are a sum/count of every row in that column, like so: (with supplied sample data)
+-----------+----------+------+--------+----+-----+--------+
| firstname | lastname | goal | assist | gw | win | played |
+-----------+----------+------+--------+----+-----+--------+
| Gandalf | The White| 10 | 6 | 1 | 1 | 2 |
| Frodo | Baggins | 16 | 2 | 1 | 2 | 2 |
| Bilbo | Baggins | 7 | 3 | 0 | 0 | 2 |
+-----------+----------+------+--------+----+-----+--------+
So, to iterate the above questions again, is this possible with one query and multiple joins?
If you provide solutions/queries, please explain them! I'm new to proper relational databases and I have never used joins before this project. I'd also appreciate if you avoid aliases unless necessary.
I have run the above query without sum and grouping and I get a set of rows for each column I do a SELECT on, which I suspect is then multiplied or added together, but I was under the impression that grouping and/or doing sum(TABLE.COLUMN) would solve that.
Another thing is that, I think, doing a SELECT DISTINCT or any other DISTINCT operation won't work since that will leave out some ("duplicate") results.
PS. If it matters, my dev machine is a WAMP but release will be on ubuntu/apache/mysql/php.
To understand why you're not getting the answers you expect, take a look at this query:
SELECT * FROM player LEFT JOIN goal USING (idplayer)
As you can see, the rows on the left are duplicated for the matching rows on the right. That procedure is repeated for each join. Here's the raw data for your query:
SELECT * FROM player
LEFT JOIN goal USING (idplayer)
LEFT JOIN assist USING (idplayer)
LEFT JOIN gw USING (idplayer)
LEFT JOIN win USING (idplayer)
LEFT JOIN played USING (idplayer)
Those repeated values are then used for the SUM calculations. The SUMs need to be calculated before the rows are joined:
SELECT firstname, lastname, goals, assists, gws, wins, games_played
FROM player
INNER JOIN
(SELECT idplayer, SUM(goal) AS goals FROM goal GROUP BY idplayer) a
USING (idplayer)
INNER JOIN
(SELECT idplayer, SUM(assist) AS assists FROM assist GROUP BY idplayer) b
USING (idplayer)
INNER JOIN
(SELECT idplayer, SUM(gw) AS gws FROM gw GROUP BY idplayer) c
USING (idplayer)
INNER JOIN
(SELECT idplayer, SUM(win) AS wins FROM win GROUP BY idplayer) d
USING (idplayer)
INNER JOIN
(SELECT idplayer, COUNT(*) AS games_played FROM played GROUP BY idplayer) e
USING (idplayer)
SQLFiddle