MYSQL, where if exists - mysql

I have 3 tables. User, alert and pref. User is obviously my user info table. Alert is my table where I store user alert/notification settings and pref is where I store my user preferences.
Not all users will have entries for prefs and alerts. When I'm trying to make a call for my alerts I'm running into trouble with my SQL knowledge.
I'm trying to do a 3 way join on user, alert and pref where the uid's are the same. I want to Select all user.id and ignore all uids that have an alert.deleted and pref.deleted = 1
user | alert | pref
id uid uid
alert_id pref_id
deleted deleted

One way to do that is
SELECT id
FROM user
WHERE user.id not in (SELECT uid FROM alert WHERE deleted = 1)
AND user.id not in (SELECT uid FROM pref WHERE deleted = 1);
But if you meant to say you want all users that actually have an alert and pref, just not the ones that are deleted, you could use
SELECT id
FROM user
INNER JOIN pref
ON user.id = pref.uid
AND pref.deleted = 0
INNER JOIN alert
ON user.id = alert.uid
AND alert.deleted = 0;
In your question you are talking about getting the alerts, so maybe there aren't any prefs necessarily for a particular user, but you do want all the not-yet-deleted alerts, then use
SELECT id
FROM user
LEFT OUTER JOIN pref
ON user.id = pref.uid
AND pref.deleted = 0
INNER JOIN alert
ON user.id = alert.uid
AND alert.deleted = 0;
Take your pick.

SELECT id
FROM user u
WHERE NOT EXISTS (SELECT 1 FROM alert WHERE uid = u.id AND deleted = 1)
AND NOT EXISTS (SELECT 1 FROM pref WHERE uid = u.id AND deleted = 1)
;

Related

mySql query - web file sharing program

Here is my code. It is supposed to select the name of the person "to_whom" I'm sharing a particular file,
and the whole thing needs to return back the user_id (u_c.to_whom) and the user_name,
so I can populate my friends list with a checkboxe next to each other, that once clicked,
will share (save into a DB table) that particular file to a specific person.
This is from a web-app where users share files among each other.
SELECT u_c.to_whom, u.user_name
FROM files f
LEFT JOIN users_connections u_c
ON (u_c.who = :user_id OR u_c.to_whom = :user_id) AND u_c.friends = "Y"
LEFT JOIN users u
ON u.user_id = u_c.to_whom
WHERE f.file_id = 90
In the table files we have
file_id, file_name, file_desc, etc...
In the table users connections
id, who, to_whom, friends
1 1 4 Y
meaning that user 1 had initiated a friendship to user 4 and "Y" means the friendship is TRUE (N = still pending)
And in users we have
user_id, user_name
1 Jack
I cannot seem to get this working for some reason.
Can any advanced user help me out?
Thanks a bunch!!
I found the answer to this code. It is an essential code if you want to build a sharing file system.
$query_list_count = "SELECT COUNT(user_id)
FROM users_connections u_c
LEFT JOIN users u
ON u.user_id = u_c.who OR u.user_id = u_c.to_whom
LEFT JOIN files f
ON f.file_id = :file_id
WHERE (u_c.who = :user_id OR u_c.to_whom = :user_id AND friends = :friends)
GROUP BY u.user_id
";
$result_list_count = $db->prepare($query_list_count);
$result_list_count->bindValue(':user_id', $_SESSION['user_id'], PDO::PARAM_INT);
$result_list_count->bindValue(':friends', "Y", PDO::PARAM_STR);
$result_list_count->bindValue(':file_id', $file_id, PDO::PARAM_INT);
$count_ = $result_list_count->fetchColumn();

Error on using outer query column in subquery for date constraint

We have a Mariadb table with users details in (users)
We have a 2nd table for review dates (reviewdates)
| reviewID |USERID |A review date |
| 001 | 123 |2017-01-08 09:02:10 |
etc...
That records review meeting dates against each user.
We have a 3rd table (userdata) with multiple types of user data in. Field id 101 is new targets for the review. Field id 98 is old targets from the previous review.
|dataID|Userid |Field ID |FieldValue |UpdatedOn |UpdatedBy|
-------------------------------------------------------------
|0001 |123 | 101 |my new target|2017-01-10|145 |
|0002 |123 | 98 |my old target|2017-01-10|0 |
New Target (field ID 101) gets copied to old targets (field id 98) when the review is completed.
Either field can be updated at any time.
Each user has many review dates. I need to compare the first value of the old field after the review is complete with the last value before the review date to make sure they have copied over correctly. As users can change either field it has to a comparison of immediately before and after the completion process.
so I join users and reviewdates
select users.userid,users.username,reviewdates.meetingdate
from companyusers users
join reviewdates on reviewdates.userid = users.userid
and this gives me all the review dates for all users
I then tried to find the most recent entry for the 101 field :
select users.userid,users.username, reviewdates.meetingdate, latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101)
) as latest101 on (latest101.userid = users.userid)
and this works OK too but when I try to find the most recent entry before each review date:
select users.userid,users.username,reviewdates.meetingdate,latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101
#date limit
and u2.updatedOn < reviewdates.meetingdate)
) as latest101 on (latest101.userid = users.userid)
I get an
"unknown column reviewdates.meetingdate in where clause"
error. I've found loads of statements saying I can't refer to an outer join in a subquery but none that provide possible answers that apply to these date constraints.
Any help or pointers would be appreciated.
I do not see that you are filtering the most outer 'reveiwdates' table with anything specific, it is just used to display the 'meetingdate' based on the inner queries,
in that case firstly, there is no reason to use the same reference inside subquery.
Secondly, there are so many 'meetingdates', which specific meeting date are we comparing against the 'updatedOn' ?
You cannot join two tables on an in-equality condition.
Either a constant review date filter needs be applied or a procedure needs be written to loop through each meeting date for a user in the 'reviewdates' table.
If you just care about the latest review for that user then you could fetch and compare the latest 'reviewdate' just like how you are comparing the latest updated on, 'updatedOn' does not seem to have time component and to avoid other issues use date() or equivalent while comparing.
select users.userid,users.username,reviewdates.meetingdate,latest101.fielddetails,latest101.updatedon
from users
join reviewdates on reviewdates.userid = users.userid
left join (select userdata.* from userdata u1
where u1.fieldid = 101
and u1.updatedOn = (select max(u2.updatedon)
from userdata u2
where u1.userid = u2.userid
and u2.fieldid = 101
#date limit
and u2.updatedOn < (select date(max(reviewdates.meetingdate)) from reviewdates where u2.userid = reviewdates.userid))
) as latest101 on (latest101.userid = users.userid)

SQL - LEFT JOIN multiple conditions - priority

I have 2 tables with a structure similar with this:
table: user
fields: id, active_office_address_id (this can be 0)
table: user_address
fields: id, user_id, type (home, office)
A user can have a "home" address (not mandatory) and multiple "office" addresses. I have a join to get a user address, but I want that if the user have a "home" address to get that address, not "office" address.
So, how can I get "home" address if exists, and only if that not exists to get "office" address. (In reality the query is much more complicated and the join is done on 4-5 tables)
SELECT * FROM user LEFT JOIN user_address ON (user.id = address.user_id AND
(user_address.type = "home" OR user.active_office_address_id = user_address.id))
group by user.id
You can use COALESCE() and join to your address table twice:
SELECT user.id
,COALESCE(home.address, office.address) AS Address
FROM user
LEFT JOIN user_address AS home
ON user.id = home.user_id
AND home.type = "home"
LEFT JOIN user_address AS office
ON user.active_office_address_id = office.user_id
GROUP BY user.id
Two left joins and a case statement will give you the address id you want.
SELECT user.*,CASE WHEN home_addr.id IS NOT NULL THEN home_addr.id ELSE ofc_addr.id END AS addr_id
FROM user
LEFT JOIN user_address AS home_addr
ON (user.id = home_addr.user_id AND home_addr.type = 'home')
LEFT JOIN user_address AS ofc_addr
ON (user.active_office_address_id = ofc_addr.id)
You could feed this back in as a sub-select for a particular user:
SELECT * FROM user LEFT JOIN user_address
WHERE user.id = ?
AND user_address.user_id = user.id
AND user_address.id IN
(SELECT CASE WHEN home_addr.id IS NOT NULL THEN home_addr.id ELSE ofc_addr.id END AS addr_id
FROM user
LEFT JOIN user_address AS home_addr
ON (user.id = home_addr.user_id AND home_addr.type = 'home')
LEFT JOIN user_address AS ofc_addr
ON (user.active_office_address_id = ofc_addr.id)
WHERE user.id = ?)
This assumes that only one home address exists per user.
At least in SQL Server, not sure about MySql, you can use a case statement in the order by clause, for example:
order by user.id, case user_address.type when 'home' then 1 else 2 end, --additional ordering clauses here

database query to get notification read by friends of users

I have two tables for notification and one table for friend_list. One notification tbale is that has the notification info who created and type and all and second which marks which friend has read the notification. the was able to retrieve the notification created by the user to be displayed only to user's friend. But now I want from the second table named notification read to display notification which are unread to the user's friend.
The query I made is (from notification + friend_list table)
$sql3=mysql_query("select n.*,f.friend_id,f.uid,f.status from notification n,friend_list f where f.uid='$id' and f.status='1' and n.title_text='Global Notification' and n.user_id in ('$id' , f.friend_id) and n.owner_user_id=f.friend_id order by n.time_stamp desc");
now how to get notification unread by friend?
The second table is
uid (friend_id)
notification_id
is_read
now I want a query with my existing query to get notifications unread by friends.
try this:
SELECT n.*,f.friend_id,f.uid,f.status
FROM notification n
INNER JOIN friend_list f
ON n.owner_user_id = f.friend_id
INNER JOIN second_table s
ON f.friend_id = s.uid AND
n.notification_id = s.notification_id
WHERE f.uid='$id' AND
f.status='1' AND
n.title_text='Global Notification' AND
n.user_id IN ('$id' , f.friend_id) AND
s.is_read = 0
ORDER BY n.time_stamp DESC;
Let's supose that:
'$id' is the id of user that raise notification
n.user_id is user that raises notification
n.owner_user_id is user that receive notification
The query is:
select
n.*,f.friend_id,f.uid,f.status
from
notification n
inner join
friend_list f
on n.owner_user_id=f.friend_id
inner join
notification_read nr
on nr.notification_id = n.notification_id and
nr.uid = f.friend_id
where
f.status='1' and
n.title_text='Global Notification' and
n.user_id = '$id' and
nr.is_read = 0
order by n.time_stamp desc
Edited due new OP comment.

How to Fix This MySQL Query So It Works?

I have the following query:
UPDATE lessonstatus
INNER JOIN user ON lessonstatus.user_id = user.user_id
SET user_id = (SELECT user_id FROM user WHERE username = 'too_many_accounts')
WHERE last_name = 'stupid'
AND first_name = 'user'
AND username != 'too_many_accounts'
AND lessonstatus.lesson_id NOT IN (SELECT lesson_id FROM lessonstatus WHERE user_id = 1);
However, I get the following error when trying to execute it:
Error Code : 1093
You can't specify target table 'lessonstatus_rtab' for update in FROM clause
How would I fix this query so that it works?
You can't SELECT from a table (even in a subquery) that you're updating in the same query. That's what the error "can't specify target table" means.
But you can join user and lessonstatus multiple times in the UPDATE statement, and use the join criteria creatively to pick out the individual row you want.
The way to simulate NOT IN with a join is to do a LEFT OUTER JOIN. Where the right side of that join is not matched, that's where NOT IN would be true.
UPDATE lessonstatus l1
INNER JOIN user u1 ON (l1.user_id = u1.user_id)
INNER JOIN user u2 ON (u2.username = 'too_many_accounts')
LEFT OUTER JOIN lessonstatus l2
ON (l1.lesson_id = l2.lesson_id AND l2.user_id = 1)
SET l1.user_id = u2.user_id
WHERE u1.last_name = 'stupid' AND u1.first_name = 'user'
AND u1.username != 'too_many_accounts'
AND l2.lesson_id IS NULL; -- equivalent to "l NOT IN l2"
nb: I have tested this query for syntax, but not with real data. Anyway, it should get you started.
There are more errors ("user" table and "user_rtab" alias do not match, use of non-qualified field names is not recommended), but UPDATE syntax itself should be similar:
UPDATE lessonstatus
SET user_id = (SELECT TOP 1 user_id FROM user WHERE username = 'too_many_accounts')
FROM lessonstatus
INNER JOIN user ON lessonstatus.user_id = user_rtab.user_id
WHERE last_name = 'stupid'
AND first_name = 'user'
AND username != 'too_many_accounts'
AND lessonstatus.lesson_id NOT IN (
SELECT lesson_id FROM lessonstatus WHERE user_id = 1
);