MySQL - join on same table? - mysql

I'm trying to implement this answer, which shows how, using table aliases, you can effectively join to the same table you're running the query on.
Consider the following (simplified) table of football teams:
-----------------------------------------------------
| id | name | next_fixture_against | url_token |
=====================================================
| 1 | Hull City | 2 | hull |
-----------------------------------------------------
| 2 | Everton | 1 | everton |
-----------------------------------------------------
I'm trying to write a query that fetches the names of a given team and its next opponent.
Approach 1: left join
Here's what I came up with by adapting the above answer:
SELECT main_team.name, opposition_team.name as against
FROM teams AS main_team
LEFT JOIN teams AS opposition_team
ON main_team.name = opposition_team.next_fixture_against
WHERE main_team.url_token = 'hull'
That produces:
-----------------------
| name | against |
=======================
| Hull City | NULL |
-----------------------
If I remove the WHERE clause, it does work, but, strangely, it comes out in the reverse order from that which I'd expect, i.e.
-----------------------
| name | against |
=======================
| Everton | Hull City |
-----------------------
rather than name = Hull City, against = Everton.
(This is purely a point of interest - clearly I need the where clause as I'm targeting a particular team, not all teams.)
Approach 2: sub-query
I came up with the following, alternate approach:
SELECT
name,
(SELECT name FROM teams WHERE id = ???) AS against
FROM
teams
WHERE
url_token = 'hull'
...but I'm not sure what to replace ??? with to get the inner sub-query to reference the outer one.
Can anyone point me in the right direction? Thanks in advance.

You need ON main_team.id = opposition_team.next_fixture_against. You tried to use the team name rather than id in the join.
Don't worry, we've all done this.

Related

Error in Mysql query with LIKE %...%

I am trying to update a table using another table. I am not able to determine how to use LIKE %…% feature here. Please help me out.
Table1:
TableToBeUpdated:
id | location | value
------------
1 | california | I am going to be here soon.
2 | Nill | Hello I love playing chess and yes.
3 | Nill | my hotel room is just shitty!
4 | Nill | Why on earth God doesn’t live on earth!
5 | Nill | friends of friends and their dogs.
Table2:
TableToCheckFrom :
uniqueid | location | keyword
---------------------
1 | Texas | Why on earth
2 | NewYork | friends and their
3 | Washington | love playing chess
4 | NewYork | their dogs
The result should be:
id | location | value
------------
1 | California | I am going to be here soon.
2 | Washington | Hello I love playing chess and yes.
3 | Nill | my hotel room is just shitty!
4 | Texas | Why on earth God doesn’t live on earth!
5 |NewYork| friends of friends and their dogs.
——
I am using this formula but its giving me constant error:
UPDATE TableToBeUpdated, TableToCheckFrom
SET TableToBeUpdated.location = TableToCheckFrom.Location
WHERE TableToBeUpdated.Value LIKE %TableToCheckFrom.Keyword%
Thanks in Advance!
You need to put the % in quotes and concatenate them to the keyword. And you need to join the two tables so you can refer to columns from both of them.
UPDATE TableToBeUpdated AS u
JOIN TableToCheckFrom AS c ON u.Value LIKE CONCAT('%', c.Keyword, '%')
SET u.location = c.Location
You query is wrong (you never tell the DBM that you want to access the TableToCheckFrom table).
For an idea on how write such queries look at this question
Update mysql table with data from another table
If you find the like %% as an additional difficult strip it: first prepare a working query with a simply condition and then add the like one.

Split SQL field containing array into new table/rows

I need a list of user IDs (course_user_ids) that is currently stored in a single field of a larger table.
I have a table called courses that contains course information with course_id and course_students as such:
-----------------------------------------------------------
| course_id | course_students |
----------------------------------------------------------
| 1 | a:3:{i:0;i:12345;i:1;i:22345;i:2;i:323456;} |
-----------------------------------------------------------
| 2 | a:32:{ … } |
-----------------------------------------------------------
The course_students part contains 3 chunks of information:
the number of students (a:3:{…) -- not needed
the order/key for the array of each student ({i:0;… i:1;… i:2; …}) -- also not needed
the course_user_id (i:12345; … i:22345;… i:32345;)
I only need the course_user_id and the original course_id, resulting in a new table that i can use for joins/subqueries like this:
------------------------------
| course_id | course_user_id |
------------------------------
| 1 | 12345 |
------------------------------
| 1 | 22345 |
------------------------------
| 1 | 323456 |
------------------------------
(ideally able to continue to break out values for other course_ids and course_user_ids, but not a priority:)
| … | … |
------------------------------
| 2 | … |
------------------------------
| 2 | … |
------------------------------
| 97 | … |
------------------------------
| 97 | … |
------------------------------
| … | … |
------------------------------
Note: the course_user_id can vary in length (some are 5 digits, some are 6)
Any ideas would be much appreciated!
Update
My user table does have user_id which can be mapped to course_students or course_user_id, so that is a very helpful observation from below.
I also think I need to use a LEFT JOIN because some students are registered in multiple courses, and I'd like to see each instance/combo.
Let us assume that you have a table name users which contains all users data along with user_id.
Now you can join table courses and table users in following manner:
select c.course_id,u.user_id
from
courses c
join users u
on u.user_id=if(instr(c.course_students,concat(":",u.user_id,";"))>0,u.user_id,c.course_students)
You get the result as per your requirement.
Verify at http://sqlfiddle.com/#!9/3667d/2
Note: The above query works fine if no overlapping between user_id and array index. In case of overlapping, kindly filter data using where-clause
If I got your goal correctly you have users table. And {i:0;i:12345;i:1;i:22345;i:2;i:323456; equal users.id=12345,users.id=22345 etc.
If my guess is correct you can try this solution:
http://sqlfiddle.com/#!9/cfef27/5
SELECT * FROM courses
LEFT JOIN users u
ON courses.course_students LIKE CONCAT('%i:',u.id,';%')

How to formulate query to show all courses taken by a person

I'm having trouble formulating a MySQL query correctly. Everything I've tried doesn't give me what's needed, or gives a syntax error.
I have three tables: Clients, Courses, and CoursesForClients.
The Clients table just has basic coordinates for a person: ID, Name, Address, email, etc.
+----------+-----------------------------+------+
| ClientID | Name | Address | etc. |
+----------+-----------------------------+------+
| 10 | Joe Smith | 1 Main St. | ... |
| 20 | Bob Smith | 2 Main St. | ... |
| ... | ... ... | ... ... ... | ... |
+----------+-----------------------------+------+
The Courses table stores the course name and its ID.
+----------+-----------------------+
| CourseID | Name |
+----------+-----------------------+
| 100 | Intro. to Subject |
| 200 | Intermediate Subject |
| 300 | Advanced Subject |
| ... | ... ... ... ... |
+----------+-----------------------+
The CoursesForClients table has the CourseID and ClientID. A given Client can have taken multiple courses, so for every course that a Client has taken, there's a row, with the person's ID and the Course ID.
+----------+----------+
| CourseID | ClientID |
+----------+----------+
| 100 | 1 |
| 200 | 1 |
| 300 | 1 |
| 100 | 2 |
| 200 | 2 |
| ... | ... |
+----------+----------+
Now, what I need is to be able to list the Client - just once - together with all the Courses she has taken. So, the result of the query might look like this:
10:Joe Smith
1 Main St.
Somewhere, AL
Intro. to Subject
Intermediate Subject
Advanced Subject
---------------------------
20:Bob Smith
2 Main St.
Somewhere, AL
Intro. to Subject
Intermediate Subject
So this output reflects the relationships between the Client and the Course. The key thing here is that, no matter how many Courses a Client has taken, the Client's particulars appear only once, followed by the list of all the courses she's taken.
There's an additional twist in that there's another table that lists the Grade for the Course for the Client, and that GradeID is also stored in the CoursesForClients table, and there's another table of Grades, with ID and Grade Description. But I won't worry about this right now. For now, all I want is just the basic output shown, as described above.
It looks like it should be easy to set up a query for this, with a JOIN and maybe a GROUP BY, but I'm having a block here and can't seem to get it right. So, any help will be hugely appreciated. Thank you!
SQL deals in tables. By definition a table has a bunch of rows, each of which has the same columns as each other. Your query is going to yield a result set that duplicates the client's information for each course she took.
Your presentation layer is going to format that table, by noticing the first row of each new client and breaking out the client header. You'll do that in php or Java or Crystal Reports or some such presentation tech.
Your query is something like this.
SELECT a.id, a.name, a.address, a.etc,
c.Name
FROM Clients a
JOIN CoursesForClients b USING(ClientID)
JOIN Courses c USING(CourseID)
ORDER BY a.id, c.CourseID
#Strawberry makes a good point about the pitfall of using USING(). Here is the same query on ON.
SELECT a.id, a.name, a.address, a.etc,
c.Name
FROM Clients a
JOIN CoursesForClients b ON a.ClientID = b.ClientID
JOIN Courses c ON b.CourseID = c.CourseID
ORDER BY a.id, c.CourseID

One-to-one relation through pivot table

Okay so I have a soccer website im building when a user signs up they get a team and and 6 different stadium to chose from. so I have a teams table:
----------------------------------------
| user_id | team_name | stadium_id |
----------------------------------------
| 1 | barca | 2 |
----------------------------------------
Then I decided to make the stadiums its own table
----------------------------------------------
| id | name | capacity | price |
----------------------------------------------
| 1 | Maracana | 50000 | 90000 |
------------------------------------------------
| 2 | Morombi | 80000 | 150000 |
------------------------------------------------
to get the teams arena name I would have to get the arena_id from the teams table and then fetch the arena name with the id. Now I don't think this is very efficient so I gave it some thought and I think the best solution is adding a pivot table like so:
| id | arena_id | team_id |
---------------------- ----------------
| 1 | 2 | 1
--------------------------------------|
| 2 | 1 | 2
--------------------------------------|
I usually think of pivot tables as tables for many to many relationships not one to one relationships. Is using a pivot table in this instance the best solution or should I just leave the implementation I'm currently using?
You don't need to use a pivot-table for this. It can either be a One-To-One or a One-To-Many relationship. It's a One-To-One if every user/team does only relate to one stadium (no stadium can be used by two teams). In a One-To-Many relationship multiple teams/users could use the same stadium, which might become necessary if you have thousands of users and start running out of stadiums.
A JOIN statement would be efficient and sufficient here.
SELECT s.name, t.team_name -- Get the team's and stadium's name
FROM team t -- From the team table
JOIN stadium s -- Join it with the stadium table
ON (t.stadium_id = s.id) -- Join ON the stadium_id
This will return the team name and stadium name of every registered team.
You might need to adjust the query, but you should be able to catch the grasp of it after reading the MySQL reference I linked above.

MySQL IN() Operator not working

How to use IN() Operator not working it's.
Those table are example and look the same as the real database I have.I don't have the permitting to add tables or change
Those are the tables:
students
+------+------+
| id | name |
+------+------+
| 1 | ali |
| 2 | man |
| 3 | sos |
+------+------+
Classes
+------+---------+
| c_id | students|
+------+---------+
| 1 | 1,2,3,4 |
| 2 | 88,33,55|
| 3 | 45,23,72|
+------+---------+
When I use this query it return me only the student with id =1
because "id IN (students)" return 1 when the first value are equal.
select name,c_id from students,classes where id IN (students);
when I get the list out on PHP than add it. it work fine.But, this solution need a loop and cost many queries.
select name,c_id from students,classes where id IN (1,2,3,4);
FIND_IN_SET()
the same happened, it's only return 1 but if the value on other position it return 0.
The IN operator works just fine, where it's applicable for what it does.
First, consider restructuring your data to be normalized, and avoid storing values as comma separated lists.
Second, if you absolutely have to deal with columns containing comma separated lists of values, MySQL provides the FIND_IN_SET() function.
FOLLOWUP
Ditch the old-school comma syntax for the join operation, and use the JOIN keyword instead. And relocate the join predicates from the WHERE clause to the ON clause. Fully qualify column references, eg.
SELECT s.name
, c.c_id
FROM students s
JOIN classes c
ON FIND_IN_SET(s.student_id,c.students)
ORDER BY s.name, c.c_id
To reiterate, storing a "comma separated list" in a column is an anti-pattern; it flies against relational theory and normalization, and disregards the best practices around relational databases. O
One might argue for improved performance, but this pattern doesn't improve performance; rather it adds unnecessary complexity in query and DML operations.
You need three tables.
One table students, one table classes, and then one table, say, students_to_classes containing something like
c_id | student_id
1 | 1
1 | 2
1 | 3
1 | 4
2 | 88
and so on.
Then you can query
select c_id from students_to_classes where student_id in (1,2,3,4)
Google "n:m relationship" for background on this.
EDIT
I know you're not specifically asking for another table structure, but this is a way of having a data type (a single number) that works with IN. Please believe me that this is the right way to do it, the reason you run into trouble with something as simple as IN is that you're using a non-standard approach, which, for such a standard problem, is typically not a good idea.
That's not how the function IN is supposed to work. You use IN when you have a list of possible matches like:
instead of:
WHERE id=1 or id=2 or id=3 or id=4
you use:
WHERE id IN (1,2,3,4)
Anyhow, your logic is not correct. The relation of Class and Student is Many-to-Many, thus a third table is needed. Let's call it studend_class, where you can store the students of each class.
student
+------+------+
| id | name |
+------+------+
| 1 | ali |
| 2 | man |
| 3 | sos |
+------+------+
class
+------+---------+
| id | name |
+------+---------+
| 1 | math |
| 2 | english |
| 3 | science |
+------+---------+
student_class
+------------+-------------+
| class_id | student_id |
+------------+-------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 3 | 3 |
+--------------+-----------+
In the example above all students are in math class and ali is also in science class.
Finally, if you whant to know which students are in what class, let's say Math, you can use:
SELECT s.id, s.name, c.name
FROM student s
INNER JOIN student_class sc ON sc.student_id=s.id
INNER JOIN class c ON sc.class_id = c.id
WHERE c.name="math";