MySQL conditional statement on NULL with subquery - mysql

Two tables:
user (id, myValue, ...)
user_preferred (id, userid, preferredValue) fk userid -> user(id)
Explanation:
user is a table of all users. user_preferred is a table of any user's preferred values. A user can be listed multiple times in user_preferred but must have different values. Query should return all users that have a myValue that matches the preferred value of the given user. $userid is the php variable of the user passed.
The Trick:
A user could have no preference, in which case there is no entry in the user_preference table. When the above query is done, I want to return every user if the given user has no preference.
Analogy:
I'm at a bar and the bartender asks me what I want to drink. I say give me everything he has that matches my preference. The first round I say I like crappy beers. So he gives me a Fosters. Second round I say I have no preference and he gives me 12 pints ranging from Bud Light to Guinness. Instead of beers, these would be users. Get it?
Query (so far):
SELECT * FROM user WHERE
IF ((SELECT preferredValue FROM user_preferred WHERE userid = $userid) IS NULL,
1,
user.myValue ANY (SELECT preferredValue FROM user_preferred WHERE userid = $userid)
)
Additional Trick:
I don't want to run "SELECT preferredValue FROM user.preferred where id = $userid" twice. Can I save the results from the first run-time and somehow use it in place of the second?

SELECT *,(SELECT preferredValue FROM user_preferred WHERE userid = $userid) AS Result FROM user WHERE
IF (Result IS NULL,1,RESULT)

To return a list of users:
SELECT o.*
FROM `user` o
WHERE o.id IN
( SELECT DISTINCT m.userid
FROM user_preferred m
WHERE EXISTS
( SELECT 1 FROM user_preferred p
WHERE p.preferredValue = m.preferredValue
AND p.userid <> m.userid
AND p.userid = $userid )
)
OR ( o.id <> $userid AND NOT EXISTS
( SELECT 1
FROM user_preferred q
WHERE q.userid = $userid
)
)

Related

SQL, where attribute in subquery if it return anything or else return everything

I am trying to add a condition(subquery) that basically would filter the output but incase the subquery does not return anything, I want this condition to be ignored somehow.
my main query is very long and complicated so I will try to give a simple example here.
set #userid := 55;
select project, userid
from users_projects
where userid IN
(select userid from activeusers)
AND
project IN (select userid from paidprojects where userid = #userid)
So if select userid from paidprojects where userid = #userid returns an empty output, I want this last condition to be ignored.
I tried this
set #userid := 55;
select project, userid
from users_projects
where userid IN
(select userid from activeusers)
AND
project IN (IF count(select userid from paidprojects where userid = #userid)>0, (select userid from paidprojects where userid = #userid), (select userid from activeusers))
but it's not working and I am getting syntax error:(
So if select userid from paidprojects where userid = #userid returns an empty output, I want this last condition to be ignored.
That is, you want only paid projects if there are any. Otherwise, you want all (unpaid) projects.
First, I assume that you want to compare projects in the last query not a project to a user. That just seems broken.
An explicit way to write this logic is:
select up.project, up.userid
from users_projects up
where up.userid in (select au.userid from activeusers au) and
(up.project in (select pp.project from paidproject pp where pp.userid = #userid) or
not exists (select pp.project from paidproject pp where pp.userid = #userid)
);
Why would you do that ?
Let me see if I understood
you have
user
You’re users table
project
Where yow projects are
user_projects
I is guessing that’s an N to M
Question
Why do you have a
paidprojects table
That table can easily and should be a column on user_projects table as well as active users table that table can also be a column on users table is user active then true else false
Your problem can be solve by a simple left join
SELECT s.user_tbl_col, p.product _tbl_col
FROM users AS s
LEFT JOIN users_projects AS up ON u.id = up.userId
LEFT JOIN products AS p ON up.productID = p.id
If The user hadn’t buy none the users info would be the only data

How to pass parent field in sub union query

I've this query and its giving an error that unknown U.UserID column in where clause but I can't see any error in it as my Users table instance is U
SELECT GROUP_CONCAT(U.UserID),
(
SELECT COUNT(DISTINCT(UserID))
FROM (
SELECT FriendID as UserID
FROM Friends
WHERE UserID=U.UserID AND Status=1
UNION All
SELECT UserID
FROM Follow
WHERE Type='user' AND TypeEntityID=U.UserID
) tbl
) as NoOfFriendsFollow
FROM `Users` `U`
WHERE `U`.`UserID` IN('1')
LIMIT 10
Any solution for this query or let me know where I'm wrong
SELECT GROUP_CONCAT(UserID),
(
SELECT COUNT(DISTINCT(UserID)) FROM
(
SELECT FriendID as UserID FROM Friends F
INNER JOIN Users U ON U.UserID=F.FriendID
WHERE F.Status=1
UNION All
SELECT FF.UserID FROM Follow FF
INNER JOIN Users U ON U.UserID=FF.UserID
WHERE Type='user' AND TypeEntityID=U.UserID
) tbl
) as NoOfFriendsFollow
FROM Users WHERE UserID IN('1') LIMIT 10;
Just try above code.
Hope this will helps.
Unfortunately, MySSQL does not permit you to use an outer table reference "two levels down". So you can do:
SELECT U.UserID,
(SELECT COUNT(DISTINCT UserID)
FROM (SELECT fr.FriendID as UserID, fr.FriendId as compareId
FROM Friends fr
WHERE fr.Status = 1
UNION All
SELECT f.UserID, f.TypeEntityID as compareId
FROM Follow f
WHERE f.Type = 'user'
) tbl
WHERE tbl.UserID = compareId
) as NoOfFriendsFollow
FROM Users U
WHERE U.UserID IN (1)
LIMIT 10;
Notes:
This moves the comparison into the middle subquery, so the query parses.
Presumably UserId is an integer. Only use single quotes for string and date constants.
The GROUP_CONCAT() doesn't make sense. It turns the query into an aggregation query that returns only one row.
I double the LIMIT is needed either. There should be one row per UserId.
Always use qualified column names when you have multiple tables in a query.

Select MySQL results based on most current row in join table

I'm working with a third-party database of a vendor we use for our account management. One of the queries we need to run programmatically is to see which accounts are currently active. There is no single column for this -- status is tracked in a separate table from the main information table that tracks all changes in status, from activation to deletion.
I want to do a simple join like this:
SELECT u.id ,
s.status
FROM user_table u
JOIN status_table s ON u.id = s._id
WHERE s.acct_status = "ACTIVE"
OR s.new_status = "ACTIVE";
But this doesn't work because there might be a later record that sets the accounts status to TERMINATED or something else. Note: every account will have a status entry of one sort or another.
For the purposes of this question, it doesn't matter what the user table is like. The status table is very simple:
_id
date_entered
acct_status
new_status
I'm pretty sure that this query would get me the latest status update (thanks to this post, but I'm not sure how to throw in a join here:
select
*
from
(select
_id, new_status
from
aria.get_acct_status_history
order by date_entered desc) as t1
group by _id;
Any ideas?
If you need the latest record per user from your status table then you use a self join on data column by getting max date per user entries and i assume _id from status_table table refers to user id
SELECT s.* FROM status_table s
JOIN (
SELECT _id ,MAX(date_entered) date_entered
FROM status_table
GROUP BY _id
) s1
ON(s._id = s1._id AND s.date_entered = s1.date_entered )
WHERE s.acct_status = "ACTIVE" or s.new_status = "ACTIVE";
Later on you and join you users table to get the user info,joining with max of date column ie. AND s.date_entered = s1.date_entered will satisfy your criteria to have recent row per user entries
SELECT u.id, s.status
FROM user_table u
JOIN status_table s ON u.id = s._id
JOIN (
SELECT _id ,MAX(date_entered) date_entered
FROM status_table
GROUP BY _id
) s1
ON(s._id = s1._id AND s.date_entered = s1.date_entered )
WHERE s.acct_status = "ACTIVE" or s.new_status = "ACTIVE";

MYSQL join subquery using count(*) to find the number of relationships a user has

I have a table named users which has fields id, email, username, firstname, and lastname.
I have another table named friends which has fields id, user1, user2, and relationship.
I am having a really hard time with this join query that shouldn't be so hard :(.
I want to find the most popular users that are not already related to you. For example, I have a relationship array already generated and I want to find the user info and the amount of relationships they have that are users not already related to you.
Here is my query so far, but I can't get it to work for some reason.
select id, email,username,firstname,lastname
from users as userInformation
left join (select count(*)
from friends
where friends.user1 = userInformation.id or friends.user2 = userInformation.id
) as x
where users.id NOT IN (2,44,26,33,1)
the "2,44,26,33,1" in the not in part is arbitrary depending on the logged in user.
the part that I can't get working properly is the left join which adds the relationship count.
Just to help out, here are the two queries that work. I just need to join the second one to be a column on the first query for each user
select id, email,username,firstname,lastname from users where id NOT IN (2,44,26,33,1)
select count(*) from friends where user1 =2 or user2 = 2
But the second query should be for each id in the first query. hope that clears it up.
This is getting closer
select id, email,username,firstname,lastname
from users as help
left join (
select count(*)
from friends
where user1 = help.id or user2 = help.id) as friendCounter
where help.id NOT IN (2,44,26,33,1)
For some reason it wont recognize help.id in the where clause in the end.
How 'bout this?
select userinformation.id, email,username,firstname,lastname,count(*)
from users as userInformation
left join friends on friends.user1 = userInformation.id or friends.user2 = userInformation.id
where userInformation.id NOT IN (2,44,26,33,1)
group by email,username,firstname,lastname
I'm going to re-phrase your problem statement as I understand it. Let me know if it's wrong.
For a given user, find most popular unrelated users:
declare #GivenUser as varchar(20)
set #GivenUser = '1' --replace '1' here with the user id you want
select id, email, username, firstname, lastname, Connections
from userInformation u1
inner join (
select TheUser, count(*) as Connections
from (
select user1 as TheUser
from friends
where user1 <> #GivenUser
and user2 <> #GivenUser
union all
select user2 as TheUser
from friends
where user1 <> #GivenUser
and user2 <> #GivenUser
) u
group by User
order by sum(Connections) desc
) u2
on u1.id = u2.TheUser
select * from (
select id, email,username,firstname,lastname,count(*) N
from users as userInformation
left join friends on friends.user1 = userInformation.id
or friends.user2 = userInformation.id
where userInformation.id NOT IN (2,44,26,33,1)
group by id,email,username,firstname,lastname) Aliased
order by N desc

How to restrict access to rows of my query based on a contacts table of user ids

I have a query that produces a result like this:
The data is sorted by date DESC, then by time DESC, but also the common 'markers_name' elements are chunked together. (They're only chunked together for each date).
To do this I get the list of MAX(time) values for every combination of (markers_id, date) in conditions, then join that list to the row set I am getting from the present query, and use the MAX(time) values for sorting:
SELECT
m.name AS markers_name,
c.time AS conditions_time,
c.date AS conditions_date,
s.name AS station_name
FROM markers m
INNER JOIN conditions c ON c.markers_id = m.id
INNER JOIN station s ON c.station_id = s.id
INNER JOIN (
SELECT
markers_id,
date,
MAX(time) AS time
FROM conditions
GROUP BY
markers_id,
date
) mx ON c.markers_id = mx.markers_id AND c.date = mx.date
ORDER BY
c.date DESC,
mx.time DESC,
m.name DESC,
c.time DESC
Now I need to restrict user access to some of the rows.
The conditions table has a 'private' column. If 'private' is set to 1 only some people are allowed to see the query row. The people that can see it include the person that created the conditions report and that person's contacts. The conditions table has a 'user_id', which contains the id of the person that created the conditions report. The contacts are obtained from a 'contacts' table which has two fields: 'user_id' and 'friend_id'. I have the user_id of the person requesting the information.
To restate another way, I need to do something like this:
Do the query above, but also check to see if c.private is set to one.
If c.private is set to one use c.user_id to get that user's 'friend_id's from the contacts table.
If the user_id of the person requesting the information matches c.user_id or any of c.user_id's friends return the query row. If not, don't return that row or return something that indicates that the row is private (I could have a markers_name with the name "Private" in the database, for example.
If anyone has any ideas I would love to hear them. I struggled with this all day yesterday. Thank you.
Possibly just adding a filter like this would be enough:
…
WHERE c.Private <> 1
OR c.user_id = #user_id
OR c.user_id IN (
SELECT friend_id
FROM contacts WHERE user_id = #user_id
)
where #user_id is the ID of the person requesting the info.
This would prohibit the private rows from appearing in the output where inappropriate.
If, however, you'd rather like them to be there but be marked as Private (for being dealt with later in the client or something like that), you could use that filter as a column in your SELECT clause:
SELECT
NOT (
c.Private <> 1
OR c.user_id = #user_id
OR c.user_id IN (
SELECT friend_id
FROM contacts WHERE user_id = #user_id
)
) AS IsPrivate,
…
FROM …
I don't have similar tables to play around with, so I'm writing this for you to try out, alright? Here is the WHERE clause for you to add to your query:
WHERE
-- all non-1 valued `private` columns are public, right? remove if not
c.private <> 1
-- all private but #user_id is entitled to view
OR (
c.private = 1
AND (
(c.user_id = #user_id) -- requestor is creator
OR (
EXISTS (
SELECT ct.friend_id
FROM contacts ct
WHERE ct.user_id = c.user_id
AND friend_id = #user_id
) -- requestor is a friend of the creator
)
)
)
Again, I wasn't able to test this.. I'll leave that up to you for now.. so please let me know how it goes. :)