This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I have 4 tables like shown below
Table: leave_request
+------------+----------+--------------+------------+----------------------+
| request_id | staff_id | applied_from | applied_to | status |
+------------+----------+--------------+------------+----------------------+
| 1 | 10 | 01-07-2012 | 02-07-2012 | approved |
| 2 | 12 | 02-07-2012 | 02-07-2012 | awaiting HR approval |
+------------+----------+--------------+------------+----------------------+
Table: leave_approval
+-------------+-------------+---------------+-------------+
| request_id | approved_by | approved_from | approved_to |
+-------------+-------------+---------------+-------------+
| 1 | 1 | 01-07-2012 | 02-07-2012 |
| 1 | 2 | 01-07-2012 | 02-07-2012 |
| 2 | 1 | 02-07-2012 | 02-07-2012 |
+-------------+-------------+---------------+-------------+
Table: staff
+-----------+-------+----------+
| staff_id | name | group_id |
+-----------+-------+----------+
| 1 | jack | 1 |
| 2 | jill | 2 |
| 10 | sam | 3 |
| 12 | david | 3 |
+-----------+-------+----------+
Table: group
+-----------+------------+
| group_id | group_name |
+-----------+------------+
| 1 | admin |
| 2 | HR |
| 3 | staff |
+-----------+------------+
I need to make a report by joining these tables, It should look like below:
+----------+------------+----------+-------------+-----------+--------------+-----------+
|applied_by|applied_from|applied_to|approved_from|approved_to|approved_admin|approved_hr|
+----------+------------+----------+-------------+-----------+--------------+-----------+
| sam | 01-07-2012 |02-07-2012|01-07-2012 |02-07-2012 | Jack | Jill |
| david | 02-07-2012 |02-07-2012|02-07-2012 |02-07-2012 | Jack | null |
+----------+------------+----------+-------------+-----------+--------------+-----------+
Thanks in advance :)
Let's take it step-by-step...
First, the entities you're selecting are in the leave_request table. So let's start there:
SELECT leave_request.* FROM leave_request
Now, you need to know the data for the applied_by column in the desired results. So you join the staff table:
SELECT
applied_staff.name AS applied_by
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
(Note that I'm using aliases for the table names. This will come in handy later.)
Now you need to know applied_from and applied_to, which you already have available:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
Now you need to know approved_from and approved_to, which are in the leave_approval table:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to,
admin_approval.approved_from,
admin_approval.approved_to
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
INNER JOIN leave_approval AS admin_approval ON leave_request.request_id = admin_approval.request_id
Uh oh, now we have a problem. There's a one-to-many relationship, so now we have duplicated leave requests in the results. We need to filter that down somehow. You don't specify how, so I'm going to make a couple assumptions: You want to know the approved_from and approved_to of the "admin" approval AND there will only be ONE "admin" approval.
Let's reflect those assumptions in the table joins:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to,
admin_approval.approved_from,
admin_approval.approved_to
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
INNER JOIN leave_approval AS admin_approval ON leave_request.request_id = admin_approval.request_id
INNER JOIN staff AS approved_staff ON admin_approval.approved_by = approved_staff.staff_id
INNER JOIN group AS approved_staff_group on approved_staff.group_id = approved_staff_group.group_id
WHERE
approved_staff_group.group_name = 'admin'
That should be better. Note that the table aliasing came in handy here because we now have two instances of the staff table for two different purposes in the same query. So we needed to distinguish them. (Keep in mind that I'm flying blind here and can't actually test any of this. So correct me if there are any problems encountered along the way. I'm also free-handing this code because I don't have MySQL handy, so let me know if there are syntax errors as well.)
Now let's add the approved_admin field to the results, which is already available:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to,
admin_approval.approved_from,
admin_approval.approved_to,
approved_staff.name AS approved_admin
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
INNER JOIN leave_approval AS admin_approval ON leave_request.request_id = admin_approval.request_id
INNER JOIN staff AS approved_staff ON admin_approval.approved_by = approved_staff.staff_id
INNER JOIN group AS approved_staff_group on approved_staff.group_id = approved_staff_group.group_id
WHERE
approved_staff_group.group_name = 'admin'
Finally, we need to know the approved_hr. And null is allowed? We're going to use a different join for this one, then. I'm also making similar assumptions to those above. Let's try this:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to,
admin_approval.approved_from,
admin_approval.approved_to,
approved_staff.name AS approved_admin,
hr_staff.name AS approved_hr
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
INNER JOIN leave_approval AS admin_approval ON leave_request.request_id = admin_approval.request_id
INNER JOIN staff AS approved_staff ON admin_approval.approved_by = approved_staff.staff_id
INNER JOIN group AS approved_staff_group on approved_staff.group_id = approved_staff_group.group_id
LEFT OUTER JOIN leave_approval AS hr_approval ON leave_request.request_id = hr_approval.request_id
LEFT OUTER JOIN staff AS hr_staff ON hr_approval.approved_by = hr_staff.staff_id
LEFT OUTER JOIN group AS hr_staff_group ON hr_staff.group_id = hr_staff_group.group_id
WHERE
approved_staff_group.group_name = 'admin'
AND hr_staff_group.group_name = 'HR'
I'm not entirely sure about those latter LEFT OUTER JOINs. The first one is definitely going to need to be a join that allows for null values, but I'm not sure how the query engine handles joins beyond that. I'd prefer that they be INNER JOINs within the scope of the initial LEFT OUTER JOIN. But I guess all of that really also depends on the integrity of the data, which I can't guarantee.
It's also worth noting that you claim to want "Jack" as output when the value is "jack". I didn't do any string manipulation in this code to make that happen. If the value should be capitalized in the data, then capitalize it in the data.
Again, I can't guarantee this code. But as a walk-through it should get you moving in the right direction. As I mentioned in a comment on the question, I really recommend picking up a book on MySQL if you're going to be writing MySQL code.
Edit: One recommendation I can give is to the structure of the data itself. Specifically that leave_approval table feels a bit messy, and it's that table alone which is causing the confusion. I have a couple recommendations:
Add an approval_type to the leave_approval table. At the very least this would indicate if it's an admin approval, an HR approval, or any other kind of approval. (Are there even other kinds? Will there ever be?) Then you could also use request_id and approval_type as a combined primary key, or at least a combined unique constraint, to enforce better data integrity and prevent duplicate approvals.
If there are only two kinds of approvals and that's probably not going to change, reflect them both in the leave_approval table. Have one set of columns for admin_approval_* and one set for hr_approval_*. (Each set would include the staff_id and relevant dates for the approval.) Then request_id itself could be a primary key on leave_approval making it one-to-one with leave_request. This would dramatically simplify the relational data, essentially turning a leave_approval record into an optional set of additional information for a leave_request record. The joins would become much simpler and the data would express itself much more clearly.
Related
I cannot quiet get using the JOIN clause around my head, even after I have ready multiple posts on the topic.
Here is my problem:
At the moment I have 2 tables.
Users Table:
ID | Username | Password
1 | Micky | 123
2 | Mouse | 145
Questions Table:
Question ID| Question_Title | Question | Rating | Category ID | User ID |
1 | Meaning of Life? | Same as Title | 100 | 2 | 1 |
2 | Foo is love? | Same as Above | 95 | 4 | 2 |
Now I simply want to run a query which will find a match in the title and the find out which user ID corresponds to that question and then get the name of the user, as well as the Question Title, Question and Rating then print them out.
So far I have:
"SELECT Question_Title, Question, Rating FROM Questions WHERE Question_Title LIKE '%$Term%'";
This works as in it will get me the Question Title, Question and Rating from the table wherever it finds a match but how would I use JOIN so that it will also get me the Username from the Users table?
Also this is just early implementation I will in the future have a lot more tables that will require similar query so I need to understand how this works.
P.S: I know their are plenty of examples like this but I had trouble understanding them so if one of you kind souls could break it down and explain I would be grateful.
Try this
SELECT
u.username
q.Question_Title,
q.Question,
q.Rating
FROM Questions q
join Users u on u.id = q.userid
WHERE q.Question_Title LIKE '%$Term%'";
Select a.* from a join b on a.id=b.a_id join c on c.b_id = b.id
in your case something like this:
SELECT Question_Title, Question, Rating,user.* FROM Questions join User on user_id=id WHERE Question_Title LIKE '%$Term%'";
I'm stuck with one SQL query. I have two tables:
users
________________________
| id | company | worker|
-------------------------
| 1 | my comp | John |
tasks
_________________________
| id | name | company |
-------------------------
| 1 | exm | my comp |
My problem is that I want to show tasks of these companies which worker is John. I'm in trouble in that for hours but I don't know how to do it. Is there any SQL query to do that?
You can do a simple join using company column from both tables and use where clause to filter results for John
SELECT t.*
FROM users u
JOIN tasks t USING(company)
WHERE u.worker ='John'
You want use the inner join tag. Just a modification on the other queries mentioned for better clarity.
SELECT task.name,user.worker,user.company
FROM tasks as task INNER JOIN users as user
ON user.company=task.company
WHERE user.worker='John';
You can do a simple join like...
$qry = "SELECT u.id,u.company,u.worker,t.id,t.name,t.company FROM users as u JOIN tasks as t ON u.company = t.company WHERE u.worker = 'John'";
I'm having trouble figuring out how to write an SQL query to return results from the following table structure.
The first thing I do is get a list of clients that have a status equal to 1 by:
SELECT * FROM clients WHERE status=1
Then I need to get all user email addresses that belong to a client. My plan was to loop through the results of the query above and running multiple queries for each client. As you can see from the table 'client_user_list' a single user can belong to multiple clients.
I tried doing something like this:
SELECT emailaddress
FROM users
INNER JOIN client_user_list ON users.user_id = client_user_list.user_id
WHERE users.client_id = 1
But it failed. As you can see I'm a total novice when it comes to this stuff. Any help would be appreciated, or feel free to point me to an appropriate resource to learn more. I've looked, but I haven't found anything that covers something complex like this.
Additional info: Using foreign keys there are relationships between clients <-> client_user_list and client_user_list <-> users
clients:
|---------------------------------------|
| client_id | client_name | status |
|---------------------------------------|
| 1 | John Doe | 1 |
| 2 | James Doe | 0 |
|---------------------------------------|
client_user_list:
|----------------------|
| client_id | user_id |
|----------------------|
| 1 | 5 |
| 2 | 6 |
| 1 | 6 |
|----------------------|
users:
|---------------------------------------|
| user_id | emailaddress |
|---------------------------------------|
| 5 | notan#email.com |
| 6 | afake#email.com |
|---------------------------------------|
Thanks so much in advance.
I'm not sure if this is your only problem, since you didn't specify what the exact problem is, but the WHERE-clause of your query contains an error. You query should be changed into this:
SELECT DISTINCT emailaddress
FROM users
INNER JOIN client_user_list ON users.user_id = client_user_list.user_id
WHERE client_user_list.client_id = 1
The users table does not have a field called client_id, the client_user_list table does.
You can get the clients with status = 1 and their users with only one query, by joining all three tables:
select clients.client_id, clients.client_name, users.user_id, users.emailaddress
from clients
inner join client_user_list on client_user_list.client_id = clients.client_id
inner join users on client_user_list.user_id = users.user_id
where clients.status = 1
order by clients.client_id, users.user_id
The following command should resolve this issue
I hope it is userful.
select distinct use.emailaddress
from clients cli
inner join client_user_list cul on (cli.client_id=cul.client_id)
inner join users use on (cul.user_id = use.user_id)
where cli.status = 1
Below is a gross over simplification of 2 very large tables I'm working worth.
campaign table
| id | uid | name | contact | pin | icon |
| 1 | 7 | bob | ted | y6w | yuy |
| 2 | 7 | ned | joe | y6e | ygy |
| 3 | 6 | sam | jon | y6t | ouy |
records table
| id | uid | cid | fname | lname | address | city | phone |
| 1 | 7 | 1 | lars | jack | 13 main | lkjh | 55555 |
| 2 | 7 | 1 | rars | jock | 10 maun | oyjh | 55595 |
| 2 | 7 | 1 | ssrs | frck | 10 eaun | oyrh | 88595 |
The page loops thru the records table and prints the results to an HTML table. The existing code, for some reason, does a separate query for each record "select name from campaign where id = $res['cid']" I'd like to get rid of the second query and do a some kind of join but what is the most effective way to do it?
I need to
SELECT * FROM records
and also
SELECT name FROM campaigns WHERE campaigns.id = records.cid
in a single query.
How can I do this efficiently?
Simply join the two tables. You already have the required WHERE condition. Select all columns from one but only one column from the other. Like this:
SELECT records.*, campaigns.name
FROM records, campaigns
WHERE campaigns.id = records.cid
Note that a record row without matching campaign will get lost. To avoid that, rephrase your query like this:
SELECT records.*, campaigns.name
FROM records LEFT JOIN campaigns
ON campaigns.id = records.cid
Now you'll get NULL names instead of missing rows.
The "most efficient" part is where the answer becomes very tricky. Generally a great way to do this would be to simply write a query with a join on the two tables and happily skip away singing songs about kittens. However, it really depends on a lot more factors. how big are the tables, are they indexed nicely on the right columns for the query? When the query runs, how many records are generated? Are the results being ordered in the query?
This is where is starts being a little bit of an art over science. Have a look at the explain plan, understand what is happening, look for ways to make it more efficient or simpler. Sometimes running two subqueries in the from clause that will generate only a subset of data each is much more efficient than trying to join the entire tables and select data you need from there.
To answer this question in more detail, while hoping to be accurate for your particular case will need a LOT more information.
If I was to guess at some of these things in your database, I would suggest the following using a simple join if your tables are less than a few million rows and your database performance is decent. If you are re-running the EXACT query multiple times, even a slow query can be cached by MySQL VERY nicely, so look at that as well. I have an application running on a terribly specc'ed machine, where I wrote a cron job that simply runs a few queries with new data that is loaded overnight and all my users think the queries are instant as I make sure that they are cached. Sometimes it is the little tricks that really pay off.
Lastly, if you are actually just starting out with SQL or aren't as familiar as you think you might eventually get - you might want to read this Q&A that I wrote which covers off a lot of basic to intermediate topcs on queries, such as joins, subqueries, aggregate queries and basically a lot more stuff that is worth knowing.
You can use this query
SELECT records.*, campaigns.name
FROM records, campaigns
WHERE campaigns.id = records.cid
But, it's much better to use INNER JOIN (the new ANSI standard, ANSI-92) because it's more readable and you can easily replace INNER with LEFT or other types of join.
SELECT records.*, campaigns.name
FROM records INNER JOIN campaigns
ON campaigns.id = records.cid
More explanation here:
SQL Inner Join. ON condition vs WHERE clause
INNER JOIN ON vs WHERE clause
SELECT *
FROM records
LEFT JOIN campaigns
on records.cid = campaigns.id;
Using a left join instead of inner join guarantees that you will still list every records entry.
Alright, I'm not the best with JOIN's and after all these years of working with mySQL you think I would be by now at the least minimally decent. Guess I've never really worked on anything superbly complex til now worth needing to join a table. So here I am, confused ever so slightly in need of a helpful example to get me on a roll, something that's pertinent to my actual data that I can make heads or tales of cause all the reading I'm doing online else where just gives me headaches for the moment. I think I might be stuck on the mythology of JOIN's being a hard thing to do, they don't seem like it but when ever I've tried I fail. So anyway I am working with PHP as my server side coding, and I believe MySQL 5.
So heres the construct to an extent.
I have table information and table connections.
Connections has: member_id, connection_id, active
Information has: firstname, lastname, gender, member_id
I should say the tables contain more data per table, but as I understand it I need write a query that I can use the member_id as the connector/foreign key. Where I can use both sides of the information. I need to know if active is 1, and then I need to know all of the columns above mentioned for information.
I tried
SELECT member_id,
connection_id,
active,
firstname,
lastname,
gender,
member_id
FROM connections, information
WHERE connection.member_id = information.member_id AND
connection.active = 1
and I've tried
SELECT * FROM connections, information
WHERE connection.member_id = information.member_id AND
connection.active = 1
With the first one I get member_id is ambitious which is understandable to a point i think cause of the matching columns between the two tables. Then the second query doesn't server me well as it only results with one person.
My Ultimate goal is to find all the connections for a specific member_id in the connections table, while gathering all the information about those connections from the information table using the connection_id as its the same thing as the member_id in the in the information table.
So in laymans terms if I am not making sense lets say I wanted to list out all my friends in from the DB. My connection table lets me know which people I am connected to where member_id is my id and connection_id is my friends member_id on another table. Hopefully that makes more sense. And this is where I am having trouble with my query and trying to write it correctly. If I could get a working sane sample of that I think I might be able to make better sense of JOIN's doesnt help that I also can't figure out what type of JOIN is best suited for my needs either I suppose.
EDIT....
Ok as per request from comment below. Sample data expected output from tables that look similar to this:
Connections Table
member_id, connection_id, active
1 | 2 | 1
1 | 3 | 1
1 | 4 | 1
1 | 5 | 1
2 | 1 | 1
2 | 5 | 1
3 | 1 | 1
Information Table
member_id, firstname, lastname, gender, ...other unimportant rows for this query
1 | Chris | Something | m | ....
2 | Tony | Something | m | ....
3 | Brandon | Something | m | ....
4 | Cassie | Something | f | ....
5 | Jeff | Something | m | ....
6 | John | Something | m | ....
now from the connections table I need to gather all the connection_id's associated with my member_id where active is 1 then from the information table gather everyone firstname, lastname, gender.. Now I know I can do this 2 step where I pool all the connection_id's from connections then run them through a loop of some sort and one by one get the resulting id's from the first query but to me that seems a bit obscure and process intensive which I want to avoid, which brings me here.. Currently from my original query posted to the many shared thus far trying to help my results are to the effect of
1 | Chris | Something | m | 2
1 | Chris | Something | m | 3
1 | Chris | Something | m | 4
1 | Chris | Something | m | 5
what I'd like to see returned is something like
2 | Tony | Something | m
3 | Brandon | Something | m
4 | Cassie | Something | f
5 | Jeff | Something | m
After looking at this I suppose I would also need to know the friendID column that matches my member_id as well but thats something to figure out after this initial hurdle
The error is in your WHERE Clause because instead of Connections you wrote it Connection which then the server generates the error.
...
WHERE connection.member_id = information.member_id AND
connection.active = 1
try this:
SELECT a.Member_ID,
a.FirstName,
a.LastName, a.gender,
b.Connection_ID
FROM Information a INNER JOIN Connections b
on a.Member_ID = b.Member_ID
WHERE b.`Active` = 1
UPDATE
if that's the case then you will most likely have a SELF JOIN
SELECT DISTINCT b.Connection_ID,
c.*
FROM Information a INNER JOIN Connections b ON
a.Member_ID = b.Member_ID
INNER JOIN Information c ON
b.Connection_ID = c.Member_ID
WHERE b.`Active` = 1
AND a.Member_ID = 'ID HERE' -- If you want to get for specific member
The error in the first is "ambiguous" not ambitious. Ambiguous means that the SQL engine doesn't know which column you are talking about, because you have two columns with the same name (different tables but still).
You can fix that by specifying the table name along with the column name where you list the columns for select.
For example
SELECT connections.member_id, connection_id, active, firstname, lastname, gender, information.member_id FROM connections, information WHERE connections.member_id = information.member_id AND connections.active = 1
The problem with returning only one suggests that there is only one record that matches your query but it's hard to guess.
Try changing to:
SELECT * FROM connections c JOIN information i ON i.member_id = c.member_id WHERE c.active = 1
Try this:
SELECT *
FROM information
LEFT OUTER JOIN connections
ON information.member_id = connections.member_id
WHERE connections.active = 1;
You're joining on the wrong tables. You said:
where member_id is my id and connection_id is my friends member_id on another table
Then, what you have to match is connections.connection_id with information.member_id. The simplest solution is:
select c.member_id, c.connection_id, c.active from connections c
join information i on c.connection_id = i.member_id
where c.active = 1 and c.member_id = #yourId
That's all :)