mysql duplicate select and subtract 1 from the duplicates - mysql

I have a bit of a strange question.
First let me tell you i cannot change the way the database is made.
Some people before me made a really good job of messing some things up pretty bad.
Its a news site and there are editors in that can edit the news posts that others post.
also editors can post news them self.
Now the question.
I have a table for news.
id | news_username (who post news)
news_text Table
news_id | username | news_text
(Here user: can be editor of the news or a person who posted news)
User Table
username | title
In that table there is a news 'id' and a news 'username'.
The username being the username of the user posting the news.
there are also allot more fields inside the table but they are not important.
Then there is a table news_text and within it the text for the news is placed.
This table has a 'news_id' and 'username' field.
This time the username is the field of the person who posted the news OR the username of the user that edited the post.
Then i have a user table with also a 'username' field and a 'title' field with the title for the user.
In this
Hope your still with me.
In short if a user posts a news article the text will be inside the 'news_text' table and when an editor edits the post the altered text will be inserted as a new text for the same article.
This is done to let the original poster see what was altered to his post.
now comes my challenge.
I need to figure out how i can get the number of edits that an editor made.
BUT because the editor itself can post news that means that i need to search for all news where the username is not equal to the original poster and where it is look for duplicates in the news_text table to see of the editor has edited his own post.
i really hope people understand a little of what i need to do.
hope you can help me.
in the comment Marcus Adams pointed me at the how the latest edit was used on the website.
This is don through a date/time field.
When an edit is placed the edit will be inserted into the news_text field and there will be date/time stamp for the new edit.
And with that it will determine witch text to grab for the news item.
hope this is clear

If I understand correctly, something like this should give the total number of edits all users have made but only where they were on posts that weren't themselves
SELECT
user.*,
COUNT(*) AS edits
FROM user
// Join posts that aren't this users
INNER JOIN news
ON news.username != user.username
// Join edits for the above posts that are this users
INNER JOIN news_text
ON news_text.news_id = news.id
AND news_text.username != user.username
And with a placeholder if you want to select a specific news article
SELECT
user.*,
COUNT(*) AS edits
FROM user
// Join posts that aren't this users
INNER JOIN news
ON news.username != user.username
AND news.id = [[SPECIFIC ID]]
// Join edits for the above posts that are this users
INNER JOIN news_text
ON news_text.news_id = news.id
AND news_text.username != user.username
Or maybe if you want to see how many edits a specific user has done on a specific article
SELECT
user.*,
COUNT(*) AS edits
FROM user
// Join posts that aren't this users
INNER JOIN news
ON news.username != user.username
AND news.id = [[SPECIFIC ID]]
// Join edits for the above posts that are this users
INNER JOIN news_text
ON news_text.news_id = news.id
AND news_text.username != user.username
WHERE user.username = [[SPECIFIC USERNAME]]
EDIT Alternative approach, if you want to count up all posts made by a user that ARE NOT original posts i.e. all edits, even if they are editing their own post
SELECT
user.*,
news.*,
COUNT(*)-IF(news.username=user.username,1,0) AS edits
FROM user
// This join will give us all posts made by user
INNER JOIN news_text
ON news_text.username = user.username
// Also join the news id
INNER JOIN news
ON news_text.news_id = news.id
GROUP BY user.username, news.id
This will return 1 row per user per news.id counting the number of edits a user has made against it, so to take this and return totals you could instead do this to return the number of edits performed by a single user by name
SELECT
username,
sUM(edits)
FROM (
SELECT
news_text.username.username,
COUNT(*)-IF(news.username=news_text.username,1,0) AS edits
FROM news_text
ON news_text.username = [[USER TO CHECK]]
// Also join the news id
INNER JOIN news
ON news_text.news_id = news.id
GROUP BY news.id
)

I would suggest that the easiest way to do this is in two queries:
One to find the total number of changes made by a user
e.g. SELECT COUNT(*) FROM news_text WHERE username = {USERNAME} GROUP BY username
And then one to find the total posts created by that user
e.g. SELECT COUNT(*) FROM news WHERE username = {USERNAME} GROUP BY username
And subtract one from the other.
Or do queries for all users and just take out the ones you need. (Or combine them into one query if you really want).

As I understand it, you have duplicate news_text rows for a particular news item (news_id). Also, you have an edit_date field on news_text table, and you're getting the news_text with the latest edit_date to get the latest version of the news item.
I also understand that you want the to get the number of edits from each user, excluding self-edits.
Here's one solution. This is for a particular user:
SELECT COUNT(*) AS edits
FROM user u
JOIN news n
ON n.username <> u.username
JOIN news_text nt1
ON nt1.news_id = n.news_id
AND nt1.username = u.username
JOIN news_text nt2
ON nt2.news_id = n.news_id
AND nt2.edit_date < nt1.edit_date
AND nt2.username <> u.username
LEFT JOIN news_text nt3
ON nt3.news_id = n.news_id
AND nt3.edit_date > nt2.edit_date AND nt3.edit_date < nt1.edit_date
WHERE u.username = 'myuser'
AND nt3.news_id IS NULL
First, it gathers all the news items that weren't authored by the user (ON n.username <> u.username)
Then it gathers all the news_texts for each of those news items that our user edited (ON nt1.news_id = n.news_id AND nt1.username = u.username)
Then it gathers all the previous versions for the above that are from other users (ON nt2.news_id = n.news_id
AND nt2.edit_date < nt1.edit_date AND nt2.username <> u.username)
Then it gathers any versions between the current one and the previous one, for exclusion later (ON nt3.news_id = n.news_id
AND nt3.edit_date > nt2.edit_date AND nt3.edit_date < nt1.edit_date)
Then it filters for our user (WHERE u.username = 'myuser')
Then it filters out edits that were in between the current and the previous, ensuring we only have the version just prior (nt3.news_id IS NULL)

Related

mysql tables: Join to two tables in a request

Newbie here.. Here's what I'm trying to do.
I have a Posts table with the following columns
id, userid, bodyText, date
and I have another table for users users.
When users submit a post, the user-id saves into the "posts" table under "userid". Well, I want to display that users information, such as name and picture, on the posts using the "userid" to get each users information so that their name and picture shows along with the post they submitted.
Sorry if I'm not being clear, english is not my first language and like I said, I'm new at this and still trying to learn.
I had it where when they submit the post, their name and picture also saves into the "posts" table but I want to change it because if the user updates their name or picture, it will still show the name and picture they had when they submitted the post. I want their name and picture to update on the posts if they update their information on the users table.
What you are asking about is a JOIN. In general the Join will look like
SELECT p.id. p.bodyText, p.date, u.name, u.picture FROM posts as p INNER JOIN users as u ON p.user_id = u.id where p.id = 123;
This will select the post with the ID 123 from the posts table and joins the users information from the users table based on the user_id columns value from the posts table.
In Syntax this could be something like
$sth = $dbh->prepare("SELECT p.id. p.bodyText, p.date, u.name, u.picture FROM posts as p INNER JOIN users as u ON p.user_id = u.id where p.id = ?");
$sth->execute(array(123));
$red = $sth->fetchAll();

Select all posts that user hasn't responded to?

I have a website that shows posts, and a user can vote on it. After they vote once I don't want it to show to that user again. So I need to have a query that polls all posts and returns the ones the user has not yet responded to.
I was thinking it would be simplest to have a posts table, responses table, and user table. The response table could include a postId, and userId. This could allow me to see all the posts that a user has responded to, but I couldn't figure out how to flip it to show the negation. Also it seems like it would be pretty slow to do it that way.
To find all posts which have not yet been seen by a given user, you could try the following query:
SELECT p.*
FROM posts p
WHERE NOT EXISTS (SELECT 1 FROM responses r
WHERE r.postId = p.postId AND r.userId = <some value>)
As an alternative to using NOT EXISTS, you could also phrase this using a LEFT JOIN:
SELECT p.*
FROM posts p
LEFT JOIN responses r
ON p.postId = r.postId AND
r.userId = <some value>
WHERE r.postId IS NULL

Mysql query for fetching videos posted by friends of a particular user

Could anyone please help me to write a query in mysql to fetch the videos posted by friends of a particular user. I have 3 tables (users,friends and videos) and the table structure are as follows.
users - id
friends - id, user_id [from user table], friend_id [from user table]
videos - id, user_id [from user table], video_name
For ex: User with id 1 has 3 friends 2,3,and 4 mapped in friends table. Each friends has posted 2 videos, mapped in videos table. So when I login as user 1, i need to list the videos posted by 2,3 and 4 with their details fetched from user table.
Thanks in advance
JOIN is what you are looking for. This can be used to "link" multiple tables based on what is common between them. This is the backbone of Normalization
The first part will be to link the "Friends" table back to the "Users" table. I am going to use a local variable (#UserID) and set it to be yours. I really don't know what information you will want from the friends information, so I am going to use generics for last name and first name in all of the queries
SET #UserID = 1 -- Your UserID
SELECT u.LastName, u.FirstName
FROM Friends AS f
INNER JOIN Users AS u ON f.Friends_ID = u.User_ID
WHERE f.User_ID = #UserID
Then the second part is going to be linking the Users to the videos. This query will return the user name information along with the names of all the videos:
SELECT u.LastName, u.FirstName, v.video_name
FROM Users AS u
INNER JOIN Videos AS v ON u.User_ID = v.User_ID
So now we have the basics. For your example you want a user to get all of the videos from all of their friends. We can actually combine these 2 queries into one query to get this:
SET #UserID = 1 -- Your UserID
SELECT u.LastName, u.FirstName, v.video_name
FROM Friends AS f
INNER JOIN Users AS u ON f.Friends_ID = u.User_ID
INNER JOIN Videos AS v ON u.User_ID = v.User_ID
WHERE f.User_ID = #UserID

MySQL Query -- based on user and business ID's

I am working on a project right now and am rather stumped with a specific sql query I (need) to execute. Let me start off by showing the DB structure I need to pull from.
--posts_table--
ID
post_title
post_text
bus_id
This next table is what is screwing with me. The only way data related to the logged in user is in here is if they have "liked" a specific post -- otherwise there is no data related to that user in this table. Now there could be plenty of data related to a particular post, just generated from other users.
--likes_table--
ID
user_id
post_id
like
What I need this to do is grab all the posts from the post_table above where a specific business id is specified. From there, I need it to grab the "like" column in the likes_table if there is data in there related to the logged in user. If there is no data there, just leave that field null in the query. Below is a query I wrote that works until there is other "like" data in the like_table from other users.
SELECT posts.id, posts.post_text, posts.post_title, likes.post_id, likes.like
FROM posts LEFT JOIN likes ON posts.id = likes.post_id WHERE
posts.bus_id = 1 AND likes.user_id IS NULL OR likes.user_id = 1;
This works up until data has been entered in the table about a specific post being liked by a different user before that user has done anything with that post, whether they like or dislike it. I am not sure if this specific type of query is even possible, any help would be much appreciated.
Edit:
After looking at it again -- I got it, finally. I just needed to add one more AND. Below is the proper query I was looking for.
SELECT posts.id, posts.post_text, posts.post_title, likes.post_id, likes.like
FROM posts LEFT JOIN likes ON posts.id = likes.post_id AND posts.user_id = 1 WHERE
posts.bus_id = 1 AND likes.user_id IS NULL OR likes.user_id = 1;
Ahh, I think I get you -- is it that if a particular post hasn't been commented on by user_id number 1 at all, the row for that doesn't show up at all?
In that case, put your l.user_id=1 into the JOIN condition instead of the WHERE condition --- this will put a NULL in if user_id 1 hasn't liked or disliked a particular post.
SELECT p.id, p.post_text, p.post_title, l.post_id, l.likes
FROM posts p
LEFT JOIN likes l ON p.id = l.post_id AND l.user_id=1
WHERE p.bus_id = 1
The l.user_id IS NULL OR l.user_id=1 has been incorporated into the LEFT JOIN -- it doesn't make rows for the other user_ids.

Making a user profile activity feed

I need to build an activity feed to go on each users profile page showing what they have been doing on the site.
There is three tables: comments, ratings, users
I want the feed to include the comments and ratings that the user has posted.
in the comments and ratings table it stores the user id of the user who posted it, not the username, so in for each item in the news feed it needs to select from the users table where the user id is the same to retrieve the username.
All the entries in the feed should be ordered by date.
Here is what ive got even though i know it is not correct because it is trying to match both with the same row in the users table.
SELECT comments.date, comments.url AS comment_url, comments.user_id, ratings.date, ratings.url AS rating_url, ratings.user_id, users.id, users.username
FROM comments, ratings, users
WHERE comments.user_id=%s
AND comments.user_id=users.id
AND ratings.user_id=%s
AND ratings.user_id=users.id
ORDER BY ratings.date, comments.date DESC
JOIN. It seems you know that, but here's how:
SELECT * FROM comments LEFT JOIN users ON comments.user_id = users.id
Thus, as far as I can tell, you're trying to order two separate things at the same time. The closest I think I can come up with would be something like:
(SELECT comments.date AS date, users.username AS name, comments.url AS url CONCAT('Something happened: ',comments.url) AS text
FROM comments LEFT JOIN users ON comments.user_id = users.id
WHERE users.id = %s)
UNION
(SELECT ratings.date AS date, users.username AS name, ratings.url AS url CONCAT('Something happened: ',ratings.url) AS text
FROM comments LEFT JOIN users ON comments.user_id = users.id
WHERE users.id = %s)
ORDER BY date DESC LIMIT 0,10
Note that the columns of both parts of the union match up. I'm pretty sure that that is required for something like this to work. That's why I have that CONCAT statement, which lets you build a string that works differently between ratings and comments.