MySQL - very complicated random row - mysql

I've got my tables posts and user_unread_posts.
In posts, all the posts of a forum are saved, and in user_unread_posts all posts are saved which are read by a user.
user_undread_postslooks like this:
id uid pid
Now I want to allow users to open a random post which they haven't read. I've tried something like
SELECT * FROM posts
LEFT JOIN user_unread_posts uup
ON uup.pid=posts.id
WHERE uup.uid<>1
ORDER BY RAND()
LIMIT 1
(Whilst 1 is a placeholder UID)
But it doesn't work, like it should work, it return posts too, which are read... How can I fix that?

SELECT *
FROM posts
WHERE id NOT IN
(
SELECT pid
FROM user_unread_posts uup
WHERE uid = $myuserid
)
ORDER BY
RAND()
LIMIT 1

You wanted to use IS NULL with the LEFT JOIN. Using <> turns the LEFT JOIN into an INNER JOIN because NULL can never match the <> operator.
Here is a corrected query:
SELECT * FROM posts
LEFT JOIN user_unread_posts uup
ON uup.pid=posts.id
WHERE uup.uid IS NULL
ORDER BY RAND()
LIMIT 1

Related

Searching and Sorting Using MySQL Inner Join

I guess I can't explain my problem properly. I want to explain this to you with a picture.
Picture 1
In the first picture you can see the hashtags in the trend section. These hashtags are searched for the highest total and it is checked whether the date has passed. If valid data is available, the first 5 hashtags are taken.
Picture 2
In the second picture, it is checked whether the posts in the hashtag are in the post, if any, the oldest date value is taken, LIMIT is set to 1 and the id value from the oyuncular table is matched with sid. Thus, the name of the person sharing can be accessed.
Picture 3
My English is a little bad, I hope I could explain it properly.
SELECT
social_trend.hashtag,
social_trend.total,
social_trend.tarih,
social_post.sid,
social_post.tarih,
social_post.post,
oyuncular.id,
oyuncular.isim
FROM
social_trend
INNER JOIN
social_post
ON
social_post.post LIKE '%social_trend.hashtag%' ORDER BY social_post.tarih LIMIT 1
INNER JOIN
oyuncular
ON
oyuncular.id = social_post.sid
WHERE
social_trend.tarih > UNIX_TIMESTAMP() ORDER BY social_trend.total DESC LIMIT 5
YOu should use a sibquery
and add a proper join between subqiery and social_trend
(i assumed sing both sid)
SELECT
social_trend.hashtag,
social_trend.total,
social_trend.tarih,
t.sid,
t.tarih,
t.post,
oyuncular.id,
oyuncular.isim
FROM (
select social_post.*
from social_post
INNER JOIN social_trend ON social_post.post LIKE concat('%',social_trend.hashtag,'%' )
ORDER BY social_post.tarih LIMIT 1
) t
INNER JOIN social_trend ON social_trend.hashtag= t.post
INNER JOIN oyuncular ON oyuncular.id = t.sid
WHERE
social_trend.tarih > UNIX_TIMESTAMP() ORDER BY social_trend.total DESC LIMIT 5
but looking to your new explanation and img seems you need
SELECT
t.hashtag,
t.total,
t.tarih_trend,
t.sid,
t.tarih,
t.post,
oyuncular.id,
oyuncular.isim
FROM (
select social_post.sid
, social_post.tarih
, social_post.post
, st.hashtag
, st.total
, st.tarih tarih_trend
from social_post
INNER JOIN (
select * from social_trend
WHERE social_trend.tarih > UNIX_TIMESTAMP()
order by total DESC LIMIT 5
) st ON social_post.post LIKE concat('%',st.hashtag,'%' )
ORDER BY social_post.tarih LIMIT 5
) t
INNER JOIN oyuncular ON oyuncular.id = t.sid

Subquery with joins - need help returning rows even if empty - MySQL

okay guys, this is a big one that’s confusing as hell.
I’m creating a forum type dealio: I have an array of Topics and each topic has an array of Posts associated with the Topic. The Topics list page should show the latest Post from that specific Topic. (Each topic is in a category as well)
Topic Latest post
Check out my page John Smith - 10:00 PM
MYSQL
select `ct`.*, `a`.`username`, `x`.* from `community-topics` as `ct`
inner join `community-categories` as `cc` on `cc`.`communityCategoryId` = `ct`.`refCategoryId`
inner join `authentication` as `a` on `a`.`userId` = `ct`.`refCreatedBy`
inner join (
select `a2`.`username` AS lastPostUsername, `cp`.`createdAt` AS lastPostCreatedAt,
`cp`.`body`, `cp`.`refTopicId` from `community-posts` as `cp`
inner join `authentication` as `a2` on `a2`.`userId` = `cp`.`refCreatedBy`
order by `cp`.`createdAt` desc limit 1
) as x on `x`.`refTopicId` = `ct`.`communityTopicId`
where `cc`.`name` = 'general' order by `ct`.`createdAt` desc
This query actually works how I need it to. The only issue is that a row won’t return if the subquery is empty. I still want to return all of the data outside of the subquery even if the subquery is null / false / empty. I've tried using IFNULL / ISNULL, but I don't think I'm using it right with the additional joins in the subquery.
if I have 2 topics, 1 topic with 5 posts, 1 topic with 0 posts, only the topic with 5 posts will show up and the topic with 0 posts won’t show up because the subquery returns empty.
If the row is empty, I’d like to return something to the front end user showing something like “Sorry, no recent posts” or something along those lines.
Any help would be amazing!
You want a left join if you want to keep rows even when there is no match:
select `ct`.*, `a`.`username`, `x`.*
from `community-topics` `ct` inner join
`community-categories` `cc`
on `cc`.`communityCategoryId` = `ct`.`refCategoryId` inner join
`authentication` `a`
on `a`.`userId` = `ct`.`refCreatedBy` left join
(select `a2`.`username` AS lastPostUsername, `cp`.`createdAt` AS lastPostCreatedAt,
`cp`.`body`, `cp`.`refTopicId`
from `community-posts` as `cp` left join
`authentication` as `a2`
on `a2`.`userId` = `cp`.`refCreatedBy`
order by `cp`.`createdAt` desc limit 1
) x
on `x`.`refTopicId` = `ct`.`communityTopicId`
where `cc`.`name` = 'general'
order by `ct`.`createdAt` desc;
I would also recommend that you remove all the backticks. They just make the query harder to write and to read.
EDIT:
If I understand your comment:
select `ct`.*, `a`.`username`, `x`.*
from `community-topics` `ct` inner join
`community-categories` `cc`
on `cc`.`communityCategoryId` = `ct`.`refCategoryId` inner join
`authentication` `a`
on `a`.`userId` = `ct`.`refCreatedBy` left join
(select `a2`.`username` AS lastPostUsername, `cp`.`createdAt` AS lastPostCreatedAt,
`cp`.`body`, `cp`.`refTopicId`,
row_number() over (partition by refTopicId order by cp.created_at desc) as seqnum
from `community-posts` as `cp` left join
`authentication` as `a2`
on `a2`.`userId` = `cp`.`refCreatedBy`
) x
on `x`.`refTopicId` = `ct`.`communityTopicId` and seqnum = 1
where `cc`.`name` = 'general'
order by `ct`.`createdAt` desc;

Left join sql query

I want to get all the data from the users table & the last record associated with him from my connection_history table , it's working only when i don't add at the end of my query
ORDER BY contributions DESC
( When i add it , i have only the record wich come from users and not the last connection_history record)
My question is : how i can get the entires data ordered by contributions DESC
SELECT * FROM users LEFT JOIN connections_history ch ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
The order by should not affect the results that are returned. It only changes the ordering. You are probably getting what you want, just in an unexpected order. For instance, your query interface might be returning a fixed number of rows. Changing the order of the rows could make it look like the result set is different.
I will say that I find = to be more intuitive than EXISTS for this purpose:
SELECT *
FROM users u LEFT JOIN
connections_history ch
ON u.id = ch.guid AND
ch.date = (SELECT Max(ch1.date)
FROM connections_history ch1
WHERE ch.guid = ch1.guid
)
ORDER BY contributions DESC;
The reason is that the = is directly in the ON clause, so it is clear what the relationship between the tables is.
For your casual consideration, a different formatting of the original code. Note in particular the indented AND suggests the clause is part of the LEFT JOIN, which it is.
SELECT * FROM users
LEFT JOIN connections_history ch ON
users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date
)
We can use nested queries to first check for max_date for a given user and pass the list of guid to the nested query assuming all the users has at least one record in the connection history table otherwise you could use Left Join instead.
select B.*,X.* from users B JOIN (
select A.* from connection_history A
where A.guid = B.guid and A.date = (
select max(date) from connection_history where guid = B.guid) )X on
X.guid = B.guid
order by B.contributions DESC;

mysql: subquery with returned many row

I have this query for load user stream in my app , is it too hard if we have 10000 matched row in 'follow' ?
SELECT *
FROM post
WHERE user_id
IN (SELECT follow_id
FROM follow
WHERE id='$some_id')
AND type='accepted'
ORDER BY id DESC LIMIT $page , 20
Syntactically your code looks correct.. I don't see any errors so then if you're talking about efficiency I would join the tables and include the second filter on the JOIN
SELECT p.*
FROM post p
JOIN follow f
ON f.follow_id = p.user_id
AND f.id = '$some_id'
WHERE p.type = 'accepted'
ORDER BY p.id DESC LIMIT $page , 20
MySQL handles large sets of data a lot better through a join than with an IN()...
Think of it this way.. because the IN() can have pretty much anything inside of it, MySQL has to check it with everything returned for each row... instead of checking once when you JOIN..
With that many returning, I have a feeling a Join might be more efficient
SELECT *
FROM post p Join
follow f On p.user_id = f.follow_id
WHERE f.id='$some_id'
AND p.type='accepted'
ORDER BY p.id DESC LIMIT $page , 20

Dynamic SQL with embedded table

I have a query which does the job but instead of the trans_inventory (which is an ID of the location) I need to get the location_name.
This one is working to get the id
SELECT *
FROM {TABLE}
WHERE trans_product = 646
ORDER BY trans_date2 DESC Limit 1
But I wonder if I can do it this way, somehow embed the location table, I have tried but below doesn't work
SELECT *, site_location.location_name
FROM site_trans
cross join
(select *
From site_location)
site_location
WHERE trans_product=646 ORDER BY trans_date2 DESC Limit 1
sl.location in the JOIN must be the site_location location id field name - I used locastion_id but it might be likely id as well. I used LEFT JOIN to avoid missing site_trans in case there is no matching location id.
SELECT s.*, sl.location_name
FROM site_trans AS s
LEFT JOIN site_location AS sl ON sl.location_id = s.location_id
WHERE s.trans_product=646
ORDER BY s.trans_date2 DESC
Limit 1