Multiple WHERE clause query doesn't work - sql

EDIT: I've edited this question.
Here, $id = $_SESSION['id'] and let's say $id = 1 (Paul).
EVENTS
sender recipient type
1 2 message
2 4 like
USERS
id firstname
1 paul
2 anna
CONNECTION
subscribing subscribed
1 2
Here's where I am at right now:
SELECT events.sender, events.recipient, events.type, users.firstname, users.lastname,
connection.subscribing, connection.subscribed
FROM events, users, connection
WHERE events.sender=users.id
AND connection.subscribing =$id`
The goal of this query is to JOIN events and info from user 2 (Anna) since user 1 (Paul) has subscribed to her feed (in the connection list). User 1 should only see events + info from users he has subscribed to (here, Anna only)
My current query doesn't work for the following reasons:
1) Rows are duplicate
2) No matter the value of $id, all the rows from the table events show up: the filter
doesn't work.
Any ideas how to fix it?

I'm not sure about what is your goal, but here is some samples:
SELECT u.id, u.firstname, sender, recipient, post_id, type
FROM events e
inner join users u
ON (e.sender = $id) -- why you need additional table there?
inner join post_list p
ON (e.post_id = p.post_id)
Another example:
SELECT u.id, u.firstname, sender, recipient, post_id, type
FROM events e, users u, connection c, post_list p
where
e.sender=u.id AND
e.post_id=p.post_id AND
u.id=c.subscribed AND
u.id=$id
Last quest will return Cartesian product of all tables and "filter" only proper ones, you can safely replace query above with JOIN syntax (be careful with tables order):
SELECT u.id, u.firstname, sender, recipient, post_id, type
FROM events e
inner join post_list p
ON (e.post_id = p.post_id)
inner join users u
ON (e.sender = u.id AND u.id=$id)
inner join connection c
on (u.id = c.subscribed)
---- UPDATE
here is proper query:
select u.id, u.firstname, e.sender, e.recipient, e.type
from users u inner join events e on (u.id=.e.sender) -- this query return ALL events
-- next part "filters" results
where
u.id in (select subscribed from connection where subscribing=$id)
also you can move filter condition in ON clause
select u.id, u.firstname, e.sender, e.recipient, e.type
from users u inner join events e on (u.id=.e.sender and u.id in (
select subscribed from connection where subscribing=$id))
for performance reasons, I suggest to use another variant:
select e.* from events e
where e.sender in (select c.subscribing from connection c where c.subscribed=$id)
this query return all events, without user's information. All users should be stored in memcache (for example) and during output to page you can add user's names, avatars, etc
also, you can loop over results via php and get list of users which should be displayed, and fetch information from db only for them, sometimes this will be faster, try benchmarking all variants

If I'm understanding your question and table schemas correctly, this should work for you:
select u.id, u.firstname,
e.sender, e.recipient, e.post_id, e.type,
pl.post
from events e
join users u on e.sender != u.id
join connection c on c.subscribed = e.sender
join post_list pl on e.post_id = pl.post_id
where e.sender = $id
I don't exactly understand your relationship between events and users, but this should get you going in the right direction.
A Visual Explanation of SQL Joins

Related

Show rows with at least one occurrence in a relation table?

I have the following table schema:
user (id, name, alias, password, active, mail, age)
comment(id, news(FK), user(FK), text, date)
new_cat(news(FK), category(FK))
I'm trying to select all the users who have commented on AT LEAST one new of EVERY category.
This is what I'm trying without any success:
SELECT * FROM user AS u, comment AS c
WHERE u.id = c.user AND c.news IN (SELECT news FROM new_cat);
I believe this is not iterating properly and checking for EVERY category and just checks if the condition applies just on one category.
How can I properly do this?
Join the tables, group by user and set the condition in the HAVING clause.
If there is a category table where you store all the categories:
SELECT u.*
FROM user u
INNER JOIN comment c ON c.user = u.id
INNER JOIN new_cat n ON n.news = c.news
GROUP BY u.id
HAVING COUNT(DISTINCT n.category) = (SELECT COUNT(*) FROM category);

SQL Sets (intersect, Union, And Not)

I have what appears to be a simple query, but is alluding my boolean challenged mind (not enough java (the liquid kind) today).
Three tables:
Users = (UserID, Username, Enabled, LoggedIn, SessionID, Email, SettingsTableVersion, FullName, Initials, UserData, InitialStatusID)
Groups = (GroupID, Groupname, Description, AutoAdd)
GroupMembers = (GroupID, UserID, ProjectID, IsMember)
I have a bunch of users and a dozen or so groups. I have a World group that has every User in it. I have a Terminated Users Group that has just 4 users in it.
What I want is a query that looks at World (everyone is in it) and takes out the Terminated User group users names. This yields me all active users! Blimey if this isn't causing me to pull my hair out. I would surmise its essentially World minus the intersection of World and Terminated Users. No luck thus far. SQL Server 2012.
TIA
A nested query?
Select *
from users
where userid not in ( select userid
from groupmembers
where groupid=[terminated])
Or is that too slow?
If I can assume
The groupnames are 'world' and 'terminatedusers'
duplicates do not exist in group members (groupID and userID are a Unique index)
you really mean World is ALL users an a terminated user coudnl't exist w/o being in the world group.
.
SELECT U.userID
FROM USERS U
INNER JOIN GROUPMEMBERS GM
on U.UserID = GM.UserID
INNER JOIN GROUPS G
on G.GroupID = GM.GroupID
WHERE GroupName in ('World', 'TerminatedUsers')
GROUP BY U.UserID
HAVING count(GM.GroupID) = 1
This basically find all the groups each user is in for the two groups. then only return those who are in just 1 group.
You could join to two defined sets as well:
SELECT U.userID
FROM Users U
INNER JOIN GroupMembers GMWorld
on U.userID = GMWorld.userID
and GMWorld.GroupID = [ID for world]
LEFT JOIN GroupMembers GMTerminated
on U.userID = GMTerminated.UserID
and GMTerminated.GroupID = [ID for terminated]
WHERE GMTerminated.userID is null
If you don't want to use the ID's you could left join twice to Group for GMterminated and GMWorld to use the names.
The 2nd query basically joins users to a worldset and a terminated set we keep all users in world except those having a record in the terminated set.
This one did the trick! Issue was I had one user that was deleted (flips the Enabled bit) and then re-added so they appeared in the list until I added the Enabled = 1. Thanks xQbert... again! Awesome stuff.
SELECt fullname
FROM Users U
LEFT JOIN GroupMembers GMWorld
on U.userID = GMWorld.userID
and GMWorld.GroupID = 3
LEFT JOIN GroupMembers GMTerminated
on U.userID = GMTerminated.UserID
and GMTerminated.GroupID = 14
WHERE GMTerminated.userID is null and Enabled = 1

Conditional inner joins based on row values MSSQL

I have a MSSQL table that is used for messages. I have clients, workers, and users, each with their own respective tables. For this messaging component, the message can be sent to a client worker or user. To differentiate between which type the the sender/receiver is, I have a column in the message table that is a tiny-int, 1 for users, 2 for clients, 3 for workers.
So, when i am attempting to receive the messages, what is the ideal way to retrieve the name of the sender from the corresponding table(clients, users, or workers) without doing multiple queries.
I know I can first do a query to check the type column, then do another query to get the name but I would rather not do this for performance issues.
You could create a generic view which simply lists the relevant columns from each plus the message type and then join to that from your messages table.
CREATE VIEW ExampleView
AS
begin
SELECT
firstname,
lastname,
1 AS MessageType
FROM
dbo.Users
union ALL
SELECT
firstname,
lastname,
2 AS MessageType
FROM
dbo.Clients
union ALL
SELECT
firstname,
lastname,
3 AS MessageType
FROM
dbo.Workers
END
Query:
SELECT
a.MessageText,
isnull(b.Firstname,'')+ISNULL(b.lastname,'') AS Name
FROM Messaging a
INNER JOIN ExampleView b ON a.MessageType = b.MessageType
If space isn't a massive issue you could store the message type directly on the Users,Clients and Workers table and just join to that column... Better performance but not really as nice database design.
This is a rather large guess at your table structure based on your description. The performance will be down to indexes you have.
Select m.*,
COALESCE(c.ClientName, w.WorkerName, u.UserName) as Name
FROM Messages m
LEFT JOIN Clients c on c.ClientId = m.UserId AND m.type = 2
LEFT JOIN Workers w on w.WorkerId = m.UserId AND m.type = 3
LEFT JOIN Users u on u.UserId = m.UserId AND m.type = 1
Where (c.ClientId is not null
OR
w.WorkerId is not null
OR
u.UserId is not null)

SQL Query trouble

I am trying to write an SQL query to retrieve all of a users pending events, however it is difficult with how my tables are structured.
My tables are as follows :
event {
event_id
name
group_id}
Pending {
GroupID
UserID
}
Users{
Username
UserID
}
Ever user is identified by a UserID, and every group by a GroupID. Events have in them a GroupID which points to a list of users. I need to retrieve all pending events for a certain user, so :
SELECT * FROM event
WHERE event.group_id = (SELECT GroupID FROM Pending)
But how do I then link this so only the Pending events for a user with a certain UserID are returned?
select e.* from event e
inner join pending p on
e.group_id = p.GroupID
inner join Users u
on p.UserID = u.UserID
where u.UserID = 123
actually you can skip the join with the Users-table if you already have the UserID:
select e.* from event e
inner join pending p on
e.group_id = p.GroupID
where p.UserID = 123
The typical way to write this is using inner join. Traditionally it has been better performing than sub selects, but modern DBMS:s optimize them into the same query. If you really want to write with a sub select you type like this
SELECT * FROM event
WHERE event.group_id in (SELECT GroupID FROM Pending WHERE UserID = 123)
You want to use the JOIN clause to link the tables together rather than using the WHERE clause to filter
SELECT u.username,
e.name
FROM users u
INNER JOIN pending p
ON u.userid = p.userid
INNER JOIN event e
ON p.groupid = e.groupid
WHERE
u.UserID = SomeID
SELECT
event.event_id
FROM event
LEFT JOIN Pending ON event.group_id = Pending.GroupID
LEFT JOIN Users USING (UserID)
WHERE Users.Username = 'foobar'
USING() can be utilized when columns on both sides of selection have the same name. It makes for easier reading. That is one of main reason why i would recommend to have same name for same data throughout the database. For example, if you Documents table and primary key document_id, then in all the other tables, where you are referencing the ID of a document, you use the same name for the column.
To learn more about JOINs : in mysql or postgresql read the links. And for visual representation of what each join does: this article.
Also you should get some book about your preferred RDBMS and learn all the basics, then you can expand your knowledge by reading SQL Antipatterns book. Or you could just carefully look through slides, made by book's author.
You can try :
select e.*, u.* from pending as p, users as u, event as e
WHERE
p.group_id = e.group_id
AND p.user_id = u.userID
SELECT * FROM event E,
pending P,
Users U
WHERE E.group_id = P.GroupID
AND P.UserID = U.UserID
AND U.UserID = (Some XYZ id)
XYZ should be some Integer value.

Sql Query - Selecting rows where user can be both friend and user

Sorry the title is not very clear. This is a follow up to my earlier question where one of the members helped me with a query.
I have a following friends Table
Friend
friend_id - primary key
user_id
user_id_friend
status
The way the table is populated is - when I send a friend request to John - my userID appears in user_id and Johns userID appears in user_id_friend.
Now another scenario is say Mike sends me a friend request - in this case mike's userID will appear in user_id and my userID will appear in user_id_friend
So to find all my friends - I need to run a query to find where my userID appears in both user_id column as well as user_id_friend column
What I am trying to do now is - when I search for user say John - I want all users Johns listed on my site to show up along with the status of whether they are my friend or not and if they are not - then show a "Add Friend" button.
Based on the previous post - I got this query which does part of the job - My example user_id is 1:
SELECT u.user_id, f.status
FROM user u
LEFT OUTER JOIN friend f ON f.user_id = u.user_id and f.user_id_friend = 1
where u.name like '%'
So this only shows users with whom I am friends where they have sent me request ie my userID appears in user_id_friend.
Although I am friends with others (where my userID appears in user_id column) - this query will return that as null
To get those I need another query like this
SELECT u.user_id, f.status
FROM user u
LEFT OUTER JOIN friend f ON f.user_id_friend = u.user_id and f.user_id = 1
where u.name like '%'
So how do I combine these queries to return 1 set of users and what my friendship status with them is. I hope my question is clear
Thanks
You need to join to your friend table twice:
select u.user_id, f1.status, f2.status
from user u left outer join friend f1 on f1.user_id = u.user_id and f1.user_friend_id = 1
left outer join friend f2 on f2.user_friend_id = u.user_id and f2.user_id = 1
where u.name like '%'
The first status should show people who have you as a friend, the second status should show people who are your friends. Not sure how this is going to help you find who are friends of a friend, though...
The simplest way to combine the two queries is by performing a union:
SELECT u.user_id, f.status
FROM user u
LEFT OUTER JOIN friend f ON f.user_id = u.user_id and f.user_id_friend = 1
where u.name like '%'
UNION
SELECT u.user_id, f.status
FROM user u
LEFT OUTER JOIN friend f ON f.user_id_friend = u.user_id and f.user_id = 1
where u.name like '%'
will produce the results that you want
This is what I'm currently using a - union of 2 queries - they are written in Zend framework but pretty straight forward to see the sql:
$select1 = $this->select()
->from(array('f'=>'friend'), array('user_id_friend as friends_id'))
->where('user_id='.(int)$user_id)
->where('status = 1');
$select2 = $this->select()
->from(array('f'=>'friend'), array('user_id as friends_id'))
->where('user_id_friend='.(int)$user_id)
->where('status = 1');
$select = $this->getAdapter()->select()->union(array( '('.$select1.')', '('.$select2.')'));
$stmt = $select->query();
return $stmt->fetchAll();
Hope this helps