Tricky MySQL query involving NOT IN - php

I have two tables that I am joining together in order to get results.
One table contains a list of assessments with a unique ID (assessments), whilst the other table (staff_to_assessments) contains a list of assessments that are assigned to staff members. An example record for this table would contain the Staff member unique ID, and the assignment unique ID. This shows that a user has been linked to an assignment.
I have written a function which only grabs assessments from the assessment table where they have not been assigned to a staff member in staff_to_assessment table. This is to populate a drop-down box on the front end where these remaining assessments can be assigned to a staff member if required, therefore I don;t want to show any that are already assigned.
MySQL query is as follows:
SELECT * FROM assessments a
LEFT JOIN staff_to_assessment s2a ON a.assessment_id = s2a.assessment_id
WHERE a.assessment_id NOT IN
(SELECT assessment_id FROM staff_to_assessment WHERE staff_id = '" . (int)$options['staffId'] . "')";
This doesn't seem to generate the response I need. Can anybody see where I have gone wrong?
Thanks!!

I think your query is working too hard. You just need to find rows where there is no match to the left outer join:
SELECT a.*
FROM assessments a LEFT JOIN
staff_to_assessment s2a
ON a.assessment_id = s2a.assessment_id
WHERE s2a.assessment_id is null;
You don't need to use both a left join and a not in clause.

Queries like NOT IN [subquery] can (and should) always be rewritten as a LEFT JOIN.
SELECT a.*
FROM assessments a
LEFT JOIN staff_to_assessment s2a USING (assessment_id) -- equivalent to "ON a.assessment_id = s2a.assessment_id", as both columns have the same name
WHERE staff_id IS NULL
This is equivalent to:
SELECT a.*
FROM assessments a
WHERE a.assessment_id NOT IN (SELECT assessment_id FROM staff_to_assessment)
Both are usually optimised away to the same execution plan, but the former is preferable as it tends to produce quicker queries.

Related

MySQL Multi Join

I had a look at the other answers in relation to mysql join and am still a little confused as to whether i'm using it right for the right instance.
I have a query that I need to perform over multiple tables at the same time and keep getting told that my statement is ambiguous.
I have 4 tables... trecord, thours, torders and tphotos
The trecord table has it's own autonumber called bid, and all the other tables have their own autonumber as well as a bid column. The trecord table will only ever have one record that needs to be retrieved at a time as does the thours table, but the torders and tphotos table will more often than not have multiple records in them that will tie back to the trecord to always show the company info.
How can I bring together all the information from trecord and thours as well as all of the information from torders and tphotos that match the bid?
$id = isset($_POST['bid']) ? $_POST['bid'] : isset($_GET['bid']) ? $_GET['bid'] : null;
$sql = "SELECT * FROM torders
INNER JOIN trecord ON torders.bid=trecord.bid
INNER JOIN thours ON thours.bid=trecord.bid
INNER JOIN tphotos ON tphotos.bid=trecord.bid
FROM WHERE bid='" . mysql_real_escape_string($id) . "'";
If every table has a 'bid' column, you need to be more specific as to which one you are running your where clause against.

SQL query for Selecting from Multiple Tables in single database

I am having 3 tables (c19 , c19b2, g26) in a database
I want to write a SQL Query to search and display all fields of the matched record.
I am using following query:
$query = "SELECT * FROM c19,c19b2,g26 WHERE armyno LIKE '%$searchTerm%'";
But it only works for table c19,
Data from the other 2 tables is not fetched.Each table has a field armyno
Please help me with this
Thank you.
Alright, you are not looking for a JOIN, but a UNION.
SELECT * FROM c19 WHERE armyno LIKE '%$searchTerm%'
UNION
SELECT * FROM c19b2 WHERE armyno LIKE '%$searchTerm%'
UNION
SELECT * FROM g26 WHERE armyno LIKE '%$searchTerm%'
That will let you query all three tables at the same time.
Which DB are you using? This would have worked in SQL Server. However, notice you are doing a cross join of every record to every record... usually you only want to match some records by restriction of a matching key, for example:
select
*
from a
left join b on b.somekey = a.somekey
left join c on c.someotherkey = b.someotherkey
In SQL server you can just say *, but I'm taking it that in your DB engine that didn't work, so try specifying which table. This may in some environments require aliasing as well:
select
a.*,
b.*,
c.*
from tableA as a
left join tableB as b on b.somekey = a.somekey
left join tableC as c on c.someotherkey = b.someotherkey
Generally, you should see the columns from the first table, followed by the columns from the second table, followed by columns from the third table for a given row. If you wanted to get all columns from all tables, but separately, then that would be 3 separate selects.
Lastly, if all 3 tables have "armyno" then I'd expect it to throw an ambiguous field error. In such case you'd want to specify which table's "armyno" field to filter on.

SUM value from query changes when i add inner join to the query

$sql = mysql_query("SELECT totals.*, sum(totals.payments) as total_payments
FROM totals
INNER JOIN users
GROUP BY totals.idseller;");
When i add the INNER JOIN the sum value is changed. Why?
In my SQL table i have one record in totals width this value: 8943.09 but when i do the some the result is giving me this value: 44715.45
What i am doing wrong?
$sql = mysql_query("SELECT totals.*, sum(totals.payments) as total_payments FROM totals
INNER JOIN users ON totals.idseller = users.idseller
GROUP BY users.UserName;");
Use this Hope this will help you.
When you INNER JOIN to another table, the returned data set is modified to only include rows that exist in both tables. In this case it is likely that there are rows in 'totals' that do not have a matching row in users - either the totals.idseller field might accept null values, or data has become orphaned when matching users have been deleted or edited.
If you want all data in 'totals' regardless of matching user you would user a LEFT JOIN instead in ms-sql, I suspect a similar approach will work in my-sql
You should give an "on" based on the ids. Such as like
inner join users on users.id = totals.idseller
Otherways the sql server will combine all possible rows in the tables, which is most cases not what you wish.
Because when you are adding inner join in your SQL Query, it means you are selecting the data which is common in both the tables.
EX:
SELECT * FROM TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.ID = TABLE_B.ID
If you are joining users table which contains 5 records. By joining table, as there is no any column mapping, this sum-up 5 times and this is reason for showing different values.
Please let me know something wrong in it.
Thanks,
Umehs

Creating a completion table (PHP and MYSQL)

My database has a challenges table where there are these columns: Challenge_Name, Challenge_Description. I have a 2nd table called completed_challenges_junction and it has these columns: Member_Name, Challenge_Name.
I need a way to display all of the challenge names from the challenges table along with the member names within the completed_challenges_junction. If there is no match then I would like it to display NULL.
I think I'm pretty close to having my SQL code working, here is what I have now.
SELECT challenges.Challenge_Name, challenges.Challenge_Description, completed_challenges_junction.Member_Names
FROM challenges
LEFT JOIN completed_challenges_junction ON challenges.Challenge_Name=completed_challenges_junction.Challenge_Name
This works but also bring duplicate entries of another member. If i use WHERE Member_Name='testmember' it only brings the entries of the member when I need it to still display all Challenge_Names.
SELECT
A.Challenge_Name,A.Challenge_Description,
GROUP_CONCAT(IFNULL(B.Member_Names,'')) Member_Names
FROM
challenges A
LEFT JOIN completed_challenges_junction B ON
A.Challenge_Name=B.Challenge_Name AND
A.Challenge_Description=B.Challenge_Description
GROUP BY
A.Challenge_Name,A.Challenge_Description
;
or
SELECT
A.Challenge_Name,A.Challenge_Description,
GROUP_CONCAT(IFNULL(B.Member_Names,'')) Member_Names
FROM
challenges A
LEFT JOIN completed_challenges_junction B
USING (Challenge_Name,Challenge_Description)
GROUP BY
A.Challenge_Name,A.Challenge_Description
;
You could add the WHERE clause into the ON condition like this
SELECT challenges.Challenge_Name, challenges.Challenge_Description, completed_challenges_junction.Member_Names
FROM challenges
LEFT JOIN completed_challenges_junction ON (
challenges.Challenge_Name=completed_challenges_junction.Challenge_Name
AND Member_Name='testmember'
)
This way only testmember entries will come up from completed_challanges_junction, but all challenges will be displayed.
I'm not sure what exactly you want, but maybe you are looking for 'group by' clause and group_concat function:
SELECT challenges.Challenge_Name, challenges.Challenge_Description,
GROUP_CONCAT(completed_challenges_junction.Member_Names SEPARATOR ', ')
FROM challenges
LEFT JOIN completed_challenges_junction
ON challenges.Challenge_Name=completed_challenges_junction.Challenge_Name
GROUP BY challenges.Challenge_Name
This query will return result where each Challenge_Name occurs only once.

MYSQL SELECT To fetch rows from two tables with or without a foreign key

I am looking for some help with a MYSQL query. I have two tables, one contains a list of tickets and the other is a list of members.
Table Tickets:
ticket_id,
member_id
Table Members:
member_id
A member can but doesn't have to be assigned to a ticket. What I would like to do is select all the tickets and then if the member_id field is set in the tickets table fetch the member information as well.
One approach is to do a SELECT * FROM Tickets and then loop through in PHP and check if the member_id field is set. If set then do another query on the members table to fetch the corresponding information. The only problem with this is that it would be a large number of queries.
Is there any way to fetch all the results in one join query?
Thanks
Select *
from Tickets
Left Join Members On tickets.member_id = members.member_id
select * from tickets left outer join members on tickets.member_id = members.ticket_id
SELECT t.ticket_id, t.member_id FROM tikcets t LEFT JOIN members m ON t.member_id = m.member_id
This will give you all the tickets whether they have been assigned to a member or not.
You should add to the query the additional fields that you want to fetch.
Try this:
SELECT t.ticket_id, m.member_id
FROM tickets AS t LEFT OUTER JOIN members AS m
ON t.member_id = m.member_id
The LEFT OUTER JOIN will cause all results from tickets to be returned, and any match from members, not disqualifying the ticket records, so this could do the trick.

Categories