SQL - need a query to search messages by recipient - sql

I have a simple message schema where a "thread" ties 2 or more users to a stream of messages. Each message belongs to a single thread. It works just like SMS messages, or Facebook messages.
Given a string (representing a name or partial name of a user), I need a query that will find all threads that match where:
the current user (userID) is a member of the thread
the name or partial name of a user is also a member of the thread
Here are my tables:
MessageThreads:
threadID
lastUpdated
MessageThreadUsers:
threadFK
userFK
Users
userID
userFirstName
userLastName
userFullName
This query gets all threads that the current user belongs to:
SELECT DISTINCT threadFK FROM MessageThreadUsers
WHERE userFK = 'usr_developer'
But how would I join each thread with all users in the thread (not including the current user) that match by name or partial name?

This should show you the other users of all threads that the 'usr_developer' key is part of.
SELECT MT.ThreadId, U.userID, U.userFullName
FROM MessageThreads MT
INNER JOIN MessageThreadUsers MTCU on (MTCU.threadFK = MT.threadID)
INNER JOIN Users CU on (MTCU.userFK = CU.userID and CU.userID = 'usr_developer')
INNER JOIN MessageThreadUsers MTU on (MTU.threadFK = MT.threadID)
INNER JOIN Users U on (MTU.userFK = U.userID and U.userID <> 'usr_developer')
WHERE U.UserFullName like '%John%' -- Do your filters here
This joins against the tables twice, first, we join to the users table to only pull back the set of threads that have a user with the id of 'usr_developer'. Then we join the resulting threads against the users table again, this time where the user id is not usr_developer. Finally we can filter the result set by the name.

Related

SQL - query for searching data in both table at the same time?

Suppose I have a table called UserFollows, where I keep Follow relationships ;
UserId
FollowedId
Then I have a general Users table and it is like ;
Id
Username
I use a query like the following when I want to get all followers of a user with ID 100 ;
SELECT*FROM UserFollows WHERE FollowedId = 100;
And suppose that I also want to query the following case ;
I want to search among the followers of a particular user, with a specific Username.
So, It should be like GET ME ALL OF THE USERS THAT HAS THE WORD 'hey' IN THEIR USERNAMES AMONG THOSE FOLLOWING ME
If I had Username in my UserFollows table , I'd easily write it like ;
SELECT*FROM UserFollows WHERE FollowedId = 100 AND Username LIKE '%a';
But I don't keep their Usernames in UserFollows
You need to include the Users table in the query.
SELECT uf.*
FROM UserFollows uf
inner join Users u on u.Id = uf.UserId
WHERE uf.FollowedId = 100
AND u.Username LIKE '%a';
or, more clearly
SELECT uf.*
FROM UserFollows uf
inner join Users follower on follower.Id = uf.UserId
inner join Users followed on followed.Id = uf.FollowedId
WHERE followed.UserName = 'ME'
AND follower.Username LIKE '%a';
Although "followed" may be a keyword, so you would need different aliases or you would need to handle the names appropriately.

SQL - Select ids that appear at least once in other table

I'm trying to get a list of Users that appear at least once in OtherTable. The following is my very inefficient HQL query using Grails. In general, there will only be at most a few hundred users that will be run in the query but potentially a million references to those users in OtherTable.
List<User> users = User.executeQuery("select user " +
"from User as user where user.id = any(" +
"select otherTable.user.id from OtherTable as otherTable)")
How can I make this query more efficient?
This SQL might be more effcient,
select distinct u.id from user as u
inner join other_table ot
on u.id = ot.id
Here is an HQL,
select distinct user
from User as user
inner join user.otherTable as ot
Using Criteria API
User.createCriteria().listDistinct {
createAlias("otherTable","ot")
eq('id','ot.id')
}
Both the above would require proper mapping of your domain classes. In case, you don't have that, OtherTable, mapped in User. Try this,
select distinct user from User user, OtherTable ot
where user.id = ot.user_id
You may have noticed that we're avoiding full table scans here, completely; and it's a single query -- unlike the one you posted, which uses a subquery. Joining both entities/tables with id should be more efficient -- assuming id columns are indexed.
Try the following query:
List<User> users = User.executeQuery("select user " +
"from User as user where" +
"user.id in (select distinct otherTable.user.id from OtherTable as otherTable)")
Hope it will help!

Issues with subqueries for stored procedure

The query I am trying to perform is
With getusers As
(Select userID from userprofspecinst_v where institutionID IN
(select institutionID, professionID from userprofspecinst_v where userID=#UserID)
and professionID IN
(select institutionID, professionID from userprofspecinst_v where userID=#UserID))
select username from user where userID IN (select userID from getusers)
Here's what I'm trying to do. Given a userID and a view which contains the userID and the ID of their institution and profession, I want to get the list of other userID's who also have the same institutionID and and professionID. Then with that list of userIDs I want to get the usernames that correspond to each userID from another table (user). The error I am getting when I try to create the procedure is, "Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.". Am I taking the correct approach to how I should build this query?
The following query should do what you want to do:
SELECT u.username
FROM user AS u
INNER JOIN userprofspecinst_v AS up ON u.userID = up.userID
INNER JOIN (SELECT institutionID, professionID FROM userprofspecinst_v
WHERE userID = #userID) AS ProInsts
ON (up.institutionID = ProInsts.institutionID
AND up.professionID = ProInsts.professionID)
Effectively the crucial part is the last INNER JOIN statement - this creates a table constituting the insitutionsids and professsionids the user id belongs to. We then get all matching items in the view with the same institution id and profession id (the ON condition) and then link these back to the user table on the corresponding userids (the first JOIN).
You can either run this for each user id you are interested in, or JOIN onto the result of a query (your getusers) (it depends on what database engine you are running).
If you aren't familiar with JOIN's, Jeff Atwood's introductory post is a good starting place.
The JOIN statement effectively allows you to explot the logical links between your tables - the userId, institutionID and professionID are all examples of candidates for foreign keys - so, rather than having to constantly subquery each table and piece the results together, you can link all the tables together and filter down to the rows you want. It's usually a cleaner, more maintainable approach (although that is opinion).

Sql Query - stuck with simple left join

I've been stuck with this simple Sql query for last 3 hours and my mind is drawing blank.
I have a User table that captures name user_id etc
Then I have a friends table that captures who is friend of whom and their status
User
user_id
name
Friend
friend_id
user_id
user_friend_id
status (accepeted,pending etc)
What I want to do is - when a logged in user searches for a name say "John" - display a list of all johns on the website and next to each John - do a lookup with a friends table to see if the record exists - ie if the logged in User is a friend of any of the Johns
if it exists show the current status
if it does not exist then show "Add Friend"
So should I first run a query on User table like select user_id from User where name Like 'John%' and then use those Id's and run them against Friends table.
I am not able to get it.
Thanks
You start with all users that match your criterium (I'm not sure about the exact MySQL syntax):
SELECT u.user_id
FROM User u
WHERE u.name LIKE '%John%'
This gives you all target users. Next you want an outer join on the Friend table where you restrict the results to the currently logged in user:
SELECT u.user_id, f.status
FROM user u
LEFT OUTER JOIN Friend f ON f.friend_id = u.user_id
WHERE f.user_id = <currentUserId> AND u.name LIKE '%John%'
What you get is a result set that has the user ids of all users named John and a status flag that is either null or an actual status. If it is null, you have a potential new friend, if not, you have an existing friend.
Something like this might get you started.
COALESCE
SQL Statement
SELECT u.name, COALESCE(f.Status, 'Add Friend')
FROM User u
LEFT OUTER JOIN Friend f ON f.User_ID = u.User_ID
WHERE u.name = 'John'
Following query must solve your problem
select a.user_name,case when a.user_name like 'John%' then b.status else 'Add Friend' end case from users_det a,friend b where a.user_id = b.user_id

Get data from two tables depending on data from one in a single query

I'm making a simple forum, and in the listing of the threads i want the classical "last post by (user) at (date)" to show. I'm using only one table to contain all the posts in the forum, where posts with the parentPost-field not equal to null are replies to a thread.
These are the tables and necessary fields i need data from:
forumPosts
id: integer, primary key
title: string value
date: string value
parentPost: integer (This field = ID of the initial post in the thread. If it's NULL, the post is an "initial thread post")
author: integer, foreign key to the users table
countAnswers: integer
lastPost: integer, (This field = the ID of the last post which was replied to it. Only used when the parentPost-field = NULL)
users
userid: integer, primary key
firstName: string value
lastName: string value
As of now, i have this query:
SELECT forumPosts.id, forumPosts.title, forumPosts.date, forumPosts.author, forumPosts.countAnswers, users.firstName, users.lastName
FROM forumPosts
INNER JOIN users ON forumPosts.author = users.userid
WHERE forumPosts.parentPost IS NULL ORDER BY id DESC;
Returns all I need (ID, Title, Date, Author and how many replies it has), except for the date and author of the last posted reply.
In addition, I'd like to fetch the following:
The date from the last post in the thread, that is the post with the highest ID and parentPost = forumPosts.id. I'd also want to fetch the firstName and lastName from the user that posted this reply.
I've been fooling around now for a long time with different joins and derived tables and such, but I can't seem to get this right. Ideally, i'd want a table returned with the following fields:
id
title
date
firstName
lastName
countAnswers
lastReplyDate
lastReplyFirstName
lastReplyLastName
To me, it seems possible, but I obviously can't make it.
I hope you understood my somewhat complex question, and i thank you a lot for all answers.
The sollution
SELECT forumPosts.id, forumPosts.title, forumPosts.date, forumPosts.author, forumPosts.countAnswers, users.firstName, users.lastName,
lastPost.date AS lastPostDate, lastUser.firstName AS lastPostFirstName, lastUser.lastName AS lastPostLastName
FROM forumPosts AS lastPost INNER JOIN
users AS lastUser ON lastPost.author = lastUser.userid INNER JOIN
forumPosts INNER JOIN
users ON forumPosts.author = users.userid ON lastPost.id = forumPosts.lastPost
WHERE (forumPosts.parentPost IS NULL)
ORDER BY forumPosts.id DESC
That worked. So typical, finding my own answer a minute after i've spent a lot of time writing a post here :-\
If "thread" is an important concept to your system--and it appears that's the case--I would model it as a separate entity. Identifying threads by posts that have no parent is going to cause you grief as you're just beginning to discover.
For one thing to find all the threads:
SELECT ... FROM posts WHERE parent IS NULL
depending on database vendor isn't typically performant because NULL values usually aren't indexed.
Create a Thread table and all these problems go away.
SELECT f.id, f.title, f.date,
u1.firstName, u1.lastName, f.countAnswers,
r1.date AS lastReplyDate,
u2.firstName AS lastReplyFirstName
u2.lastName AS lastReplyLastName
FROM forumPosts f
INNER JOIN users u1 ON (f.author = u1.userid)
LEFT OUTER JOIN (forumPosts r1 INNER JOIN users u2 ON r1.author = u2.userid)
ON (f.id = r1.parentPost)
LEFT OUTER JOIN forumPosts r2
ON (f.id = r1.parentPost AND r1.id < r2.id)
WHERE f.parentPost IS NULL
AND r2.id IS NULL
ORDER BY f.id DESC;