Need MySQL select query assistance - mysql

I currently have a web app where users can share posts and connect with other users. I store each post in a table named "posts" and all of the relationships between users in a table called "relations"
The structure of "posts" is:
+-------------+-----------+---------+------------+----------------+
| post_id | user_id | text | date | privacy |
+-------------+-----------+---------+------------+----------------+
| 1 | 4 | Hello | 1/13/2014 | 2 |
+-------------+-----------+---------+------------+----------------+
Privacy can either be 1 or 2
The structure of "relations" is:
+-------------+-----------+------------+------------+
|rel_id | sender | recipient | status |
+-------------+-----------+------------+------------+
| 1 | 17 | 4 | 1 |
+-------------+-----------+------------+------------+
Status can either be 1 or 2
Now, I want to have a "News Feed" like page where the user can see all of the posts from the people they are either friends with (status= 2) or following (status= 1). But I am having trouble with the query. I know how to do simple select queries, but I don't think that is possible with this.
So, I would like to select all of the posts from the "posts" table where the 'user_id' is the same as 'recipient' in the "relations" table where also the sender equals '17' (I am going to use a variable). Now on top of that, if the status of that row from "relations" is '1' and the 'privacy' from the "posts" row is '2', then skip that post.
How would I write this query?

Use joins
SELECT * FROM `posts`
join `relations` on `recipient` = `user_id`
WHERE `status` = 2

Use joins and where clauses, as follows:
SELECT *
FROM posts p
JOIN relations r ON p.user_id = r.recipient
WHERE (r.status = 1 OR r.status = 2)
AND (r.status != 1 OR p.privacy != 2);
For succinctness, it helps to alias the tables (eg "posts p") so that you can subsequently refer to fields from each of them specifically (eg "p.privacy").
This will join the tables, including any where relations.status is 1 or 2, yet skipping any where both relations.status is 1 and posts.privacy is 2.

Related

How to select only if the last element of an inner-join matches?

I have two tables : processes and notes. Each note is linked to a process. A process have several notes (one to many relationship). Notes also have a creation date.
I want to select each process whose last note contains a certain text (say 'some content'), but ONLY if this note is the last created one for the process.
For example :
processes table:
id | name
----------
42 | 'foo'
notes table:
content | creation_date | process_id
-------------------------------------------
'note1' | '09/13' | 42
'note1' | '09/14' | 42
'some_content'| '09/15' | 42
The process_id field in notes is a foreign key. In this example, the 'foo' process should be selected by my query.
If a new note is added, the notes tables becomes something like this:
content | creation_date | process_id
-------------------------------------------
'note1' | '09/13' | 42
'note1' | '09/14' | 42
'some_content'| '09/15' | 42
'note4' | '09/16' | 42
In this case the 'foo' process should not be selected, because the last note content is not 'some_content' anymore.
Is it possible to do such a thing in a single query?
I'm using MySQL.
One possibility is aggregation:
select p.id, p.name
from processes p join
notes n
on n.process_id = p.id
group by p.id, p.name
having max(n.creation_date) = max(case when n.note like '%some_content%' then n.creation_date end);
You can use a correlated sub query like so:
SELECT *
FROM processes
WHERE (
SELECT content
FROM notes
WHERE notes.process_id = processes.id
ORDER BY creation_date DESC
LIMIT 1
) = 'some_content'
Yet another method simply uses exists
select *
from processes p
where exists (
select * from notes n
where n.process_id=p.id
and n.content='some_content'
and n.creation_date=(select Max(creation_date) from notes)
)

How to find all the opposite combinations between two columns in SQL

I am making a web dating app that needs to match users and let them chat with each other.
I want to figure out how to find all the matches for a particular user.
Right now I have a table called follows that has 2 columns.
UserID | MatchUserID
--------------------
1 | 2
2 | 1
1 | 3
1 | 4
1 | 5
4 | 1
5 | 4
The idea is that for two users to match they need to follow one another. The table above shows which user follows which.
Assuming that the user who is currently logged on is UserID = 1.
I need a query that will return from the MatchUserID table the following results:
2, 4
In a way, I am looking to find all the opposite combinations between the two columns.
This is the code I use to create the table.
CREATE TABLE Match
(
UserID INT NOT NULL,
MatchUserID INT NOT NULL,
PRIMARY KEY (UserID, MatchUserID)
);
You can do it with a self join:
select m.MatchUserID
from `Match` m inner join `Match` mm
on mm.MatchUserID = m.UserId
where
m.UserId = 1
and
m.MatchUserID = mm.UserId
See the demo.
Results:
| MatchUserID |
| ----------- |
| 2 |
| 4 |
The simplest way possibly is to use EXISTS and a correlated subquery that searches for the other match.
SELECT t1.matchuserid
FROM elbat t1
WHERE t1.userid = 1
AND EXISTS (SELECT *
FROM elbat t2
WHERE t2.matchuserid = t1.userid
AND t2.userid = t1.matchuserid);

MySQL - Searching From 3 tables with one query

I have 3 table
Table news:
id_post | news | id_user
3 | IT news | 1
4 | game news | 2
Table user:
id_user | username
1 | bocah
2 | gundul
And Table vote
id_vote | id_post | id_user | LIKE
10 | 3 | 2 | 1
And this is my sql query:
SELECT post.*, username, like, SUM(vote.like) AS like FROM post
INNER JOIN user ON post.id_user=user.id_user
INNER JOIN vote ON post.id_post=vote.id_post
WHERE
(`title` LIKE '%$word%' OR `username` LIKE '%$word%') AND post.id_user=user.id
LIMIT 15
I just want to create search form from searching post or user based on keyword. Then display post, user's username which is also the author of post and total like in that news.
The problem is when keyword not match with any post or any user, my expectation, it should return an empty row. But it's not, it's return 1 row with NULL value.
Any answer to solve this?
Apart from getting one row when there is no match, you probably also get one row when there are multiple matches. I think this is because you have an aggregation (sum) without having a group by.
If I'm correct, adding a group by clause should solve the problem:
SELECT post.*, username, like, SUM(vote.like) AS like FROM post
INNER JOIN user ON post.id_user=user.id_user
INNER JOIN vote ON post.id_post=vote.id_post
WHERE
(`title` LIKE '%$word%' OR `username` LIKE '%$word%') AND post.id_user=user.id
GROUP BY post.id /* Or what's its name */
LIMIT 15
It will then, however, return results by post, so if you search for a user, you will still get all their posts (that is, the top 15), but my guess is that that's exactly what you want.

select statement with only rows which have set true in second table

i have two tables
activity
id | user_id | time | activity_id
1 | 1 | | 3
2 | 1 | | 1
and preferences
user_id | running | cycling | driving
1 | TRUE | FALSE | FALSE
i need result set of
id | user_id | time |
2 | 1 | |
i only need rows from first table whose values are set true in preferences table.
e.g activity_id for running is 1 which is set true in preferences table, so it returns while others doesn't.
If you can edit the schema, it would be better like this:
activity
id | name
1 | running
2 | cycling
3 | driving
user_activity
id | user_id | time | activity_id
1 | 1 | | 3
2 | 1 | | 1
preferences
user_id | activity_id
1 | 1
A row in preferences indicates a TRUE value from your schema. No row indicates a FALSE.
Then your query would simply be:
SELECT ua.id, ua.user_id, ua.time
FROM user_activity ua
JOIN preferences p ON ua.user_id = p.user_id
AND ua.activity_id = p.activity_id
If you want to see the activity name in the results:
SELECT ua.id, ua.user_id, ua.time, activity.name
FROM user_activity ua
JOIN preferences p ON ua.user_id = p.user_id
AND ua.activity_id = p.activity_id
JOIN activity ON ua.activity_id = activity.id
What I would probably do is join the tables on a common column, looks like user_id is a common column in this case, which gives access to the columns in both tables to query against in the where clause of the query.
Which type of join depends on what information you want from preferences
Handy Visual Guide for joins
So you could query
SELECT * FROM activity LEFT JOIN preferences ON activity.user_id = preferences.user_id WHERE preferences.columnIWantToBeTrue = true
I'm using left join since you mentioned you want the values from the first table based on the second table.
Mike B has the right answer. The relational model relates rows together by common values.
You've got a table named activity with an id column which looks like the primary key. The column name activity_id would typically be the name of a column in another table that is a foreign key to the activity table, referencing activity.id.
It looks like you've used the activity_id column in the activity table as a reference to either "running", "cycling" or "driving".
It's possible to match activity.activity_id = 1 with "running", but this is a bizarre design.
Here's an example query:
SELECT a.id
, a.user_id
, a.time
FROM activity a
JOIN preferences p
ON p.user_id = a.user_id
AND ( ( p.running = 'TRUE' AND a.activity_id = 1 )
OR ( p.cycling = 'TRUE' AND a.activity_id = 2 )
OR ( p.driving = 'TRUE' AND a.activity_id = 3 )
)
But, again, this is a bizarre design.
As a start, each table in your database should have rows that represent either an entity (a person, place, thing, concept or event that can be uniquely identified, is important, and we need to store information about), or a relationship between the entities.
From the limited information we have about your use case, the entities appear to be "user", an "activity_type" (running, cycling, driving), an "activity" (an amount of time, for a user and an activity_type) and some user "preference" about which activity_types the user prefers.
See the answer from Mark B for a possible schema design.

LEFT JOIN and NOT ISSET

I have two table:
Site:
id | name
1 | google
2 | stackoverflow
3 | cnn.com
Confirm:
id | site_id | type // (type = 1, 2 or 3)
1 | 1 | 2
2 | 2 | 1
so and would like get Sites which is not added to table Confirm with type for example 1.
SELECT *
FROM Site
LEFT JOIN Confirm
ON Site.id = Confirm.site_id
and what next?
A natural way to write this query is as a NOT EXISTS query:
SELECT s.*
FROM Sites s
WHERE NOT EXISTS (SELECT 1 FROM Confirm c WHERE c.site_id = s.id AND type = 1);
It is almost a 1-1 translation of the problem statement: "would like to get sites that are not added to Confirm with type = 1".
I do not understand what you meant. But if you are in doubt about how to work with result sets from the database, look at this, maybe it can help you: http://www.codeproject.com/KB/database/Visual_SQL_Joins/Visual_SQL_JOINS_orig.jpg