Select all from table where another query retuns no results - php

I'm creating a register web application for someone and I want to make student search to add a student to the activities register slightly more accurate.
At the moment my prepared statement is:
("SELECT * FROM students WHERE first_name LIKE :pattern OR last_name LIKE :pattern OR id LIKE :pattern ORDER BY last_name ASC");
And that works great. I returns a list of all students that match the search query. However after doing that query, for each row that returns I want to check if the students.id and $_GET['activity'] do not appear in the participants table already.
$_GET['activity'] is an ID from activities.id
The final result I want is to display all students that are not already registered on that activity.
Is this possible in one query? As if it is I'd rather do that then have to run a query on each returns result to see whether it should be displayed or not.
I have looked into INNER JOIN as I've used it before, but I don't feel that is what I need. My main issue is how to run that query to check if each result is in the participants table already.
Hopefully that will make sense as I'm finding it hard to work out how to word it in my head.
My table structure:
students - id PRIMARY KEY AI, first_name (varchar255), last_name (varchar255), dob (date)
activities - id PRIMARY KEY AI, title (varchar255), description (varchar255)
participants - id PRIMARY KEY AI, student_id (INT), activity_id (INT)

EDIT Updating this to use the three tables in the question
If you want all students who do NOT have a certain activity, you use a query pattern like this. The LEFT JOIN retains all the records from the students table, and places NULL values in the columns from activities where the ON condition fails to match. You then throw in a WHERE condition to keep only those NULL values. Like so:
SELECT s.id, s.first_name, s.last_name
FROM students s
LEFT JOIN participants p ON s.id = p.student_id
LEFT JOIN activities a ON p.activity_id = a.activity_id AND a.activity LIKE :act
WHERE a.activity_id IS NULL
AND ( s.first_name LIKE :a OR s.last_name LIKE :b OR etc etc )
If your input is an activity_id, it's even easier.
SELECT s.id, s.first_name, s.last_name
FROM students s
LEFT JOIN participants p ON s.id = p.student_id AND p.activity_id = :act
WHERE p.activity_id IS NULL
AND ( s.first_name LIKE :a OR s.last_name LIKE :b OR etc etc )
As you've noticed, INNER JOIN can't do this, because it leaves out the rows from the first table that don't match the ON condition. But those rows are the very ones you want, hence the LEFT JOIN ... WHERE ... IS NULL.
Beware some things:
Don't use LIKE to match id values.
Don't use SELECT *. Instead, name the columns you want in your result set.
The sequence of OR ... LIKE clauses in your filter for the student table is not going to perform very well.

You could use a subquery to get the studentids that are registered for $_GET['activity'], and then remove them from the returned rows using NOT IN in your WHERE condition.
SELECT * FROM students
WHERE
(first_name LIKE :pattern OR last_name LIKE :pattern OR id LIKE :pattern)
AND
id NOT IN
(SELECT student_id FROM participants WHERE activity_id = :activity)
ORDER BY last_name ASC
where :activity is the placeholder for $_GET['activity']

try this query
"SELECT * FROM students WHERE first_name LIKE :pattern OR last_name LIKE :pattern OR id LIKE :pattern AND id NOT IN (select student_id From participants WHERE activity_id = activity)
Here 'activity' is $_GET['activity'] .

Related

Php - about SQL-query with WRONG(?) database

Easy question for you but hard solution for me.=)
I have a database - USERS. In USERS I have two tables - USERS_INFO and EVENTS.
USERS_INFO contain next fields:
user_id
user_name
...
and
EVENTS contains next:
event_id
user_id
obj_id (this element means, for examle, when user_1 will change information
event about user_2 in this table appears record like:
event_id=1
user_id=user_1
obj_id=user_2)
So, as you can see, information about user_id, obj_1 from table EVENTS I get from table USERS_INFO in field user_id.
I connected it.
The question is = how to create right query?
I need to see something like this:
user_1 user_4 some_event_like_edit (means that user_1 changed smth in user_4)
I can create query, but it works wrong. I did -
SELECT USERS_INFO.user_name, EVENTS.event FROM USERS_INFO,EVENTS WHERE USERS_INFO.user_id=EVENT.user_id
BUt I cant create query for another field obj_id!!
Result give me the fields where EVENTS.user_id=EVENTS.obj_id
What I should do??
You just need to join to user table twice, like this:
SELECT u_1.user_name as Who_Changed,u_2.user_name as Who_Got_Changed, e.event
FROM EVENTS e
INNER JOIN USERS_INFO u_1
ON (u_1.user_id=e.user_id)
INNER JOIN USERS_INFO u_2
ON (u_2.user_id=e.obj_id)

MySQL INNER JOIN with different column names

Okay, so I have two tables, a news table and a users table. They are set up as below:
news:
id title user_id contents
users:
id username password
When I run this query:
SELECT news.title, users.username, news.contents FROM news INNER JOIN users ON news.user_id=users.id WHERE news.id=:id
I can only access the user id using $query['id']. So it appears that the join is using the column names from table2, although I want them to map it to the column names of table1. So even though the column is called id in the users table, I want to access it under user_id, since I specified that in the JOIN query.
I need this because if I ever need the id from table1, they would both be mapped to the column called id and I would only be able to get the id of the users table.
So is there any way to do this? Access the column from table2 under the name of the column in table1?
In MySQL what you specify in the select statement is what it is called in the result set, so you need to tell it what to give you. you do that with the AS command
SELECT users.id AS user_id ... FROM ...
That way you can call it whatever you want
or grab the user_id from news with
SELECT news.user_id ... FROM ...
SELECT users.id AS id1, news.id AS id2, news.title, users.username, news.contents
FROM news INNER JOIN users ON news.user_id=users.id WHERE news.id=:id
echo $query['id1'] . $query['id2'];

MySQL NOT IN with condition

I need to write a query that will pull all pieces of hardware that are unassigned to a user. My tables that look like this:
table: hardware
ID, brand, date_of_purchase, purchase_price, serial_number, invoice_location
table: assigned_equipment
ID, user_id, object_id, object_type, is_assigned, date_assigned
Once a piece of hardware is checked out to a user, a new entry in assigned_equipment is made, and the column is_assigned is set to 1. It can be 0 if it is later unassigned.
That being said, my query looks like this:
SELECT * FROM hardware WHERE ID NOT IN (SELECT object_id FROM assigned_equipment);
I need a conditional statement that would add WHERE is_assigned = 0 otherwise if there's an entry it will not list. Ideas?
Simple extend the subquery to contain only assigned items:
SELECT * FROM hardware
WHERE ID NOT IN
(SELECT object_id FROM assigned_equipment WHERE is_assigned = 1);
So, every matching id is NOT in the subselect - therefore unassigned.
Columns in the assignment table with is_assigned=0 are no longer part of the subresult, and therefore part of your outer result.
You can't do this without a JOIN so you should ditch the subselect.
SELECT
hardware.*
FROM
hardware h
LEFT JOIN
assigned_equipment e
ON (e.object_id = h.id)
WHERE
e.id IS NULL
OR
(e.is_assigned = 0 AND e.user_id = ?);
If you take a semantic approach then the is_assigned column should not be required - as only assigned items should appear in the assigned_equipment table.
Which would make your query:
SELECT *
FROM `hardware`
WHERE `id` NOT IN (
SELECT `object_id`
FROM `assigned_equipment`
);
This of course means that when an item becomes unassigned you DELETE the row from the assigned_equipment table.
In my opinion this is better as it means you're not storing unnecessary data.

SQL Optimization WHERE vs JOIN

I am using mysql and this is a query that I quickly wrote, but I feel this can be optimized using JOINS. This is an example btw.
users table:
id user_name first_name last_name email password
1 bobyxl Bob Cox bob#gmail.com pass
player table
id role player_name user_id server_id racial_name
3 0 boby123 1 2 Klingon
1 1 example 2 2 Race
2 0 boby2 1 1 Klingon
SQL
SELECT `player`.`server_id`,`player`.`id`,`player`.`player_name`,`player`.`racial_name`
FROM `player`,`users`
WHERE `users`.`id` = 1
and `users`.`id` = `player`.`user_id`
I know I can use a left join but what are the benefits
SELECT `player`.`server_id`,`player`.`id`,`player`.`player_name`,`player`.`racial_name`
FROM `player`
LEFT JOIN `users`
ON `users`.`id` = `player`.`user_id`
WHERE `users`.`id` = 1
What are the benefits, I get the same results ether way.
Your query has a JOIN in it. It is the same as writing:
SELECT `player`.`server_id`,`player`.`id`,`player`.`player_name`,`player`.`racial_name`
FROM `player`
INNER JOIN `users` ON `users`.`id` = `player`.`user_id`
WHERE `users`.`id` = 1
The only reason for you to use left join is if you want to get data from player table even when you don't have matches in users table.
LEFT JOIN will get data from the left table even if there's no equal data from the right side table.
I guess at one point, that player table's data will not be equivalent to users table specially if the data on users table has not been inserted into player table.
Your first query might return null on cases that the 2nd table (player) has no equivalent data corresponding to users table.
Also, IMHO, setting up another table for servers is a good idea in terms of complying to the normalization rules in database structure. After all, what details of the server_id is the column on player table pointing to.
The first solution makes a direct product (gets and connects everything with everything) then drops away the bad results. If you have a lot of rows this will be very slow!
The left join gets first the left table then put only the matching rows from the right (or null).
In your example you don't even need join. :)
This'll give you the same result and it'll be good until you just check for user id:
SELECT `player`.`server_id`,`player`.`id`,`player`.`player_name`,`player`.`racial_name`
FROM `player`
WHERE `player`.`user_id` = 1
Another solution if you want more conditions, without join could be something like this:
SELECT * FROM player WHERE player.user_id IN (SELECT id FROM user WHERE ...... )

Mysql select oldest record for each user

I'm trying to create a list in PHP of the oldest entries for each user in the database.
SELECT *,
MIN(`entries`.`entry_date`)
AS entry_date
FROM (`entries`)
JOIN `user_profiles`
ON `user_profiles`.`user_id` = `entries`.`user_id`
WHERE `status` = 1
GROUP BY `entries`.`user_id`
I'm using the query to retrieve from the entries table the oldest dated entry using MIN()and joining with table user_profiles for other data. The query should select the oldest entry for each user. It seems to work but it retrieves the wrong entry_date field on some entries when I echo them. Please help, I can't spot what I'm doing wrong..
You need to use a subquery to obtain the (user_id, entry_date) pairs for each user, then join that with a query that selects the records of interest:
SELECT *
FROM entries
NATURAL JOIN (
SELECT user_id, MIN(entry_date) AS entry_date
FROM entries
GROUP BY user_id
) AS tmin
JOIN user_profiles USING (user_id)
WHERE status = 1
Have you tried approaching the problem from the user_profiles table instead of the entries table? If a user has no entries, they will not appear in the above query.
This may help, but I'm not sure if it's the full solution:
SELECT *, MIN(entries.entry_date) as entry_date
FROM user_profiles LEFT JOIN entries USING (user_id)
WHERE status = 1
GROUP BY user_profiles.user_id
Also, you're renaming the MIN(entires.entry_date) as entry_date... but you already have a column named entry_date. Try renaming the derived columns to something unique like "min_entry_date"?

Categories