mySQL complex SELECT query? - php

Here is my table structure:
users:
id | email
emails:
id | subject | body
user_emails:
user_id | email_id
It's very simple design. However, I need to select the first (lowest) emails.id for all users.id's that are not associated in user_emails.
To illustrate:
users:
id | email
1 | email#domain.com
2 | test#lol.com
3 | user#test.com
emails:
id | subject | body
1 | sub1 | body1
2 | sub2 | body2
user_emails:
user_id | email_id
1 | 1
1 | 2
2 | 1
As the data shows:
User 1 received email 1 and 2 already so the select should exclude
that user.
User 2 only received email 1, so it should select user 2
and email 2.
User 3 didn't receive any emails, so it should select user 3
and email 1.
I'll be doing this in PHP so any logic should be in written in PHP if it can't be done with mySQL alone.
Thanks in advance
EDIT: I should probably mention that there will be thousands of user ID's and hundreds of email id's. The script that will send out all these emails will run once daily. So, this should be as optimized as possible.

Could try the below:
SELECT * from users u
LEFT JOIN users_emails e ON u.id=e.user_id
LEFT OUTER JOIN emails m on m.id=1
WHERE e.user_id is NULL
Or use the below if the id in emails not necessary to be 1 as above query.
SELECT * from users u
LEFT JOIN users_emails e ON u.id=e.user_id
LEFT OUTER join emails m on m.id=(SELECT id from emails order by id limit 1)
WHERE e.user_id is NULL

I thought of a much simpler solution to all this. I added a lastSent column to the users table.
// Load all emails into an array. Use the id as the key.
$sql = "SELECT * FROM emails";
$query = mysql_query( $sql );
while( $row = mysql_fetch_assoc( $query ) ) {
$emails[ $row['id'] ] = $row;
}
// Load all users into an array.
$sql = "SELECT id, email, lastSent FROM users";
$query = mysql_query( $sql );
while( $row = mysql_fetch_assoc( $query ) ) {
$users = $row;
}
// Loop through each user and send next email from the list based on lastSent value.
foreach ( $users as $user ) {
$id = $user['lastSent'] + 1;
$email = $user['email'];
$subject = $emails[$id]['subject'];
$body = $emails[$id]['body'];
$send = mail($email, $subject, $body);
if ( $send ) {
// Update lastSent value.
$sql = "UPDATE users SET lastSent='$id' WHERE id='" . $user['id'] . "'";
mysql_query( $sql );
}
}

Give a try with the below query
select t.id, min(t.q_id) from (SELECT u.id, e.id as q_id FROM users u CROSS JOIN emails e) as t where (t.id, t.q_id) not in (select * from user_emails) group by t.id

Related

Select where not exists pdo

I have a query
SELECT *
FROM `user`
WHERE NOT
EXISTS (
SELECT *
FROM roleInEvent
WHERE user.userId = roleInEvent.userId
AND eventId = 1
)
AND user.disciplineId =5
Whenever I run this in my mysql console it returns 1 row. This is correct.
However in my php script it returns zero rows while it is exact the same query.
My php script looks like this:
$db = DatabaseHelper::get();
$st = $db->prepare('SELECT *
FROM `user`
WHERE NOT
EXISTS (
SELECT *
FROM roleInEvent
WHERE user.userId = roleInEvent.userId
AND eventId = 1
)
AND user.disciplineId =5');
$st->execute();
if ($st->errorCode() !== \PDO::ERR_NONE) {
return null;
}
Somebody knows what is the problem here?
The solution was easy.
I forgot to return the results to my javascript
You can use too the IN with a NOT like:
SELECT * FROM user WHERE id_user NOT IN ( SELECT other_data FROM roleInEvent WHERE user.userId = roleInEvent.userId AND eventId = 1 ) AND user.disciplineId =5
If the query: SELECT other_data FROM roleInEvent WHERE user.userId = roleInEvent.userId AND eventId = 1 bring to you something, this result not have to exist in the user table.
Other example is:
Users Table:
---------------------------
id_user | name_user
1 fernando
2 urban
And in other table we have the block users:
Block Users:
id_user_block | id_user | name_user
1 1 fernando
2 2 john lennon
So,i want bring to me only users that not exists in the block users table:
The query will be:
SELECT * FROM Users WHERE id_user NOT IN ( SELECT id_user FROM blocked_users )
So, the last query we are getting:
id_user | name_user
2 john lennon
Greetings!

Mysql query array

I have two tables:
1 - hotels[id,name,extras] ( name of hotels with column extras which I've select for each one)
2 - extras[id,name] ( here's the extras of hotel like wifi,tv,swim... )
$name = $_GET['name'];
$hotels_q = mysql_query("SELECT * FROM `hotels` WHERE `name`='$name'") or die (mysql_error());
$hotels_row = mysql_fetch_array($hotels_q);
$id = $hotels_row['id'];
$extras = explode(",", $hotels_row['extras']);
$ekstras_q = mysql_query("SELECT * FROM `extras` order by id") or die(mysql_error());
While($ekstras_row = mysql_fetch_array($ekstri_q)){
$eid = $ekstras_row['id'];
$ename = $ekstri_row ['name'];
echo '<ul><li><input type="checkbox" name="extras['.$eid.'][]" value="'.$ename.'"';
if (in_array($eid, $ekstras)) echo'checked';
echo'/>'.$ename.'</li></ul>';
Problem is here extras_q displays all entries with checked ones from table, but I want only to display only checked items!
Since you're storing your extra IDs in one column as a comma separated string, I think you should be able to do this by not exploding that value, and then using the CSV string as an IN criteria in your second query.
//...
$extras = $hotels_row['extras']; // don't explode
// Use IN with the $extras CSV here
$ekstras_q = mysql_query("SELECT * FROM `extras`
WHERE id IN ($extras) ORDER BY id") or die(mysql_error());
If you are able to modify your database, you can instead create a many-to-many relationship between hotels and extras by adding a table to join those two items together instead of using a CSV column as you currently are. This can make it easier to write queries to select the related records.
If you add a third table hotel_extras with columns hotel_id and extra_id, you can insert one row for each extra that each hotel has. For example, if the hotel with id 1 has several different extras, its entries in that table would look like this:
table: hotel_extras
_______________________
| hotel_id | extra_id |
=======================
| 1 | 1 |
| 1 | 3 |
| 1 | 4 |
-----------------------
Here is an example using PDO of how you could query data from a setup like that:
$sql = "SELECT e.id, e.name
FROM hotels h
INNER JOIN hotel_extras he ON h.id = he.hotel_id
INNER JOIN extras e ON he.extra_id = e.id
WHERE h.`name` = ?";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(1, $_GET['name']);
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$eid = $row['id'];
$ename = $row['name'];
'<li><input type="checkbox" name="extras['.$eid.'][]" value="'.$ename.'" checked/>'.$ename.'</li>';
}

SQL joining other table with condition

I have this query:
SELECT *
FROM `classes`
JOIN `classes_students`
ON `classes`.`id` = `classes_students`.`class`
And I need to add condition for selecting just classes, in which are not currently logged student (user ID is not in classes_students connected with class id) and also count how many students are in that class.
Table structure:
classes: id, name, etc
classes_students: class_id, user_id, etc
Table data:
classes:
1 | test
2 | test2
3 | test3
classes_students:
1 | 1
1 | 2
2 | 3
3 | 4
3 | 5
Expected output if im user with id 1:
classes names (with number of students in):
2 (1 student)
3 (2 students)
All this in one query. It is possible? If yes, how?
Select classid, count(*)
from class
left join student on student.classid = class.classid
group by classid
Glad help for you
Try this query:
$user_id = 1; // current user_id
$query = "SELECT `classes`.`id`, `classes`.`name`, COUNT(*) as students FROM `classes`
JOIN `classes_students`
ON `classes`.`id` = `classes_students`.`class_id`
AND `classes_students`.`user_id` != $user_id
GROUP BY `classes_students`.`class_id`
";
I figured it out! :)
Thank you guys for ur help.
$user_id = 1; // current user_id
$query = "SELECT `classes`.`id`, `classes`.`name`, COUNT(*) as students FROM `classes`
LEFT JOIN `classes_students`
ON `classes`.`id` = `classes_students`.`class_id`
WHERE `classes`.`id` NOT IN (SELECT `class_id` FROM `classes_students` WHERE `user_id`='.$user_id.')
GROUP BY `classes_students`.`class_id`
";

I want to return data form a table depending on whats in another table?

I have a table passenger_table and a table users!
passenger_table
user_id j_id
1 1
34 1
54 1
users
user_id Firstname Lastname
1 Patrick Connell
34 John Murphy
54 Connell Jones
I know this might be straight forward enough but I just want to loop out all the users firstnames and lastnames where journey id is = 1.
So the output would be the 3 users mentioned above first and lastnames.
This is what I got so far.....
//Get passenger id
$id = $_GET['id'];
$resultp = mysql_query("SELECT * FROM passenger_journey WHERE j_id=$id")
or die(mysql_error());
$rowp = mysql_fetch_array($resultp);
$passenger_id = $rowp['user_id'];
//Get Passenger Details
$resultpd = mysql_query("SELECT * FROM users WHERE user_id=$passenger_id")
or die(mysql_error());
while($rowpd = mysql_fetch_array($resultpd)) {
echo '' . $rowpd['firstname'] . ' ' . $rowpd['lastname'] . '<br/>';
}
At the moment I can only retrieve one record.
You should join the table, on the associative ID. Read more about table joins here: http://dev.mysql.com/doc/refman/5.0/en/join.html
SELECT u.user_id, u.firstname, u.lastname, p.j_id
FROM users u
LEFT JOIN passenger_table p ON p.user_id=u.user_id
WHERE p.j_id=1;

Checking users options from default

I have table:
user_id | song_id| points
--------|----------------
2 | 1 | 0
2 | 2 | 1
2 | 3 | 2
2 | 4 | 3
2 | 5 | 4
And I need to check if the user have changed the points value.
Therefore it should be something like:
while ($row = mysql_fetch_array($query)) {
$userID = $row['user_id'];
$songID = $row['song_id'];
$points = $row['points'];
if($songID-$points==1){
echo $userID."<br>";
}
But this will print out every occasion of userID where the song-id - points=1.
I need to print out only these user_id's that have all the values =1 and the username must echo'd only once.
EDIT:
SELECT DISTINCT user_id WHERE (song_id - points) = 1
This is half way there. This echo's user_ids' where the song_id - points = 1, but if the user is reordered (i use jQuery sortable) the list, then there can be some rows that is "song_id - points = 1".
My script must echo only these user_id-s, where users every song_id - points = 1, not only one
SELECT DISTINCT user_id FROM table WHERE (song_id - points) = 1
After edit:
SELECT table.user_id
FROM table
LEFT JOIN (
SELECT user_id, COUNT(*) AS C FROM table) AS T2
ON table.user_id = T2.user_id
WHERE (table.song_id - table.points) = 1
GROUP BY table.user_id
HAVING COUNT(*) = T2.C
You can first filter the users which has modified point values:
SELECT DISTINCT user_id FROM table
WHERE (song_id - points) != 1
Then you can use fetch the users which doesn't fit the above condition:
SELECT DISTINCT user_id FROM table
WHERE user_id NOT IN (
SELECT DISTINCT user_id FROM table
WHERE (song_id - points) != 1
)
According to your last edit this last SQL statement might work.
You can check a working example.
Here is what you're looking for:
select user_id from (
select user_id, if(song_id - points = 1, 0, 1) flag from t
) as S
group by user_id
having sum(flag) = 0
And here is a working example.
In case I didn't understand the requirements this shows all users who don't even have one row in which song_id - points != 1, i.e, all users who have all rows that match song_id - points = 1
Or maybe, if you prefer a different approach that might be more efficient:
select distinct t1.user_id from t t1
where not exists (
select * from t t2
where t2.song_id - t2.points != 1 and t1.user_id = t2.user_id
)
Here is the working example.
Not sure I understand the why of the situation, but a simple control-break structure will achieve the desired result ...
$old_id = '';
$good = false;
while($row = mysql_fetch_array($query)){
//check to see if we have a new user ...
if($row['user_id'] != $old_id){
//check to see if all values were == 1
if($good){
echo $old_id . '<br />';
}
//re-initialize variables
$good = true;
$old_id = $row['user_id'];
}
//if value != 1, we won't print the user ...
if($row['song_id'] - $row['points'] != 1){
$good = false;
}
}
//final end-of-loop condition ...
if($good){
echo $old_id . '<br />';
}
OK, here's a query that's a lot more simple than the join above:
SELECT user_id, sum(song_id) as song_total, sum(points) as point_total, count(*) AS cnt FROM table GROUP BY user_id
If the difference between song_id and points is 1 for every song, then the difference between the totals will equal the number of rows for that user ... so using this query:
while($row = mysql_fetch_array($query)){
if($row['cnt'] == ($row['song_total'] - $row['point_total'])){
echo $row['user_id'] . '<br />';
}
}

Categories