Selection of a random AND 'published' row from table in mysql - php

I've read many answers to this question but none seem to answer in the scenario when you need to pick one random row in a DB of articles, say a wordpress db, where in the wp_posts, you actually have both revisions, trashed and published articles.
Previous answers seems to return blank result if the id is random and the post is not published like in this code
SELECT * FROM wp_posts AS w
JOIN (SELECT (RAND() * (SELECT MAX(id) FROM wp_posts)) AS id) AS r2
WHERE w.id >= r2.id
AND w.post_status = 'publish'
ORDER BY w.id ASC
LIMIT 1

Just use
SELECT *
FROM wp_posts
WHERE post_status = 'publish'
ORDER BY RAND()
LIMIT 1
to get a random record being published.

Your method is intended to be more efficient than sorting all the published articles. The following is similar for only published articles:
SELECT *
FROM wp_postsw CROSS JOIN
(SELECT (RAND() * (SELECT MAX(id) FROM wp_posts WHERE post_status = 'publish')) AS id
) r2
WHERE w.id >= r2.id AND w.post_status = 'publish'
ORDER BY w.id ASC
LIMIT 1;
For performance, you want an index on wp_posts(post_status, id).
That is, you need the condition inside the subquery. And, you need to include the appropriate columns in the WHERE clause.

Related

MySQL Tag System - How to select all the tags associated to the last N published posts?

I'm implementing a Tag System for my website, using PHP + MySQL.
In my database, I have the following three tables (so called "Toxi Solution"):
Posts
Id
DateTime
...
Tags
Id
Tag
Slug
TagsMap
Id
Tag
where:
Id = post's Id in Posts
Tag = Tag's Id in Tags
Every post may have (in TagsMap) one or more tags associated to it.
Now I need to select all the tags associated to the last 20 published posts.
I've tried
SELECT T.Slug, T.Tag
FROM Posts A, TagsMap TM, Tags T
WHERE (SELECT Id from Posts WHERE DateTime <= NOW() ORDER BY Id DESC LIMIT 20)
AND T.Id = TM.Tag
AND A.Id = TM.Id
But I get
#1242 - Subquery returns more than 1 row
And here's where I'm stuck (I'm not very experienced with MySQL).
Would you give me any suggestions?
You can filter the table Posts first and then join to other 2 tables:
SELECT t.Slug, t.Tag
FROM Tags t
INNER JOIN TagsMap tm ON t.Id = tm.Tag
INNER JOIN (
SELECT Id
FROM Posts
WHERE DateTime <= NOW()
ORDER BY DateTime DESC LIMIT 20
) p ON p.Id = tm.Id
You may use SELECT DISTINCT ... if you are getting duplicate results.
try this
SELECT T.Slug, T.Tag
FROM Tags T
left join TagsMap TM on TM.Tag = T.id
left join Posts A on A.Id = TM.id
WHERE A.Id in (SELECT Id from Posts WHERE DateTime <= NOW() ORDER BY Id DESC LIMIT 20)

Mysql - get last post from category

I have this structure (tables) of forum
I want to select last post (row from forum_post table) from category.
SQL so far:
SELECT * FROM table_post
WHERE topic_id = (SELECT MAX(id) FROM table_topic WHERE category_id = {$id})
ORDER BY id ASC LIMIT 1
Question: How to modify this select to achieve my goal?
Assuming that "last" means the biggest id, I would suggest order by and limit:
select fp.*
from forum_post fp join
forum_topic ft
on fp.topic_id = ft.id
where ft.category_id = $id
order by fp.id desc
limit 1;

Combine two SELECT queries from different tables

Issue
I have one table (posts) with articles and article meta.
Another table (post_reviews) contains user-submitted ratings (a value out of 5) for each article, referencing posts by the id of the post in question.
I am trying to find the top three posts, by review, of the last 3 days. Therefore I need to:
find all the posts in that time period (3 days)
find the average rating for each post
sort them by average rating (desc)
Code
For the first part, I can successfully use the query:
SELECT * FROM `posts` WHERE `hub_id`=:hub_id AND `date`>=:start_date AND `date`<=:end_date)
To find each individual post's average rating, I use this query:
SELECT SUM(`review`) AS `total` FROM `post_reviews` WHERE `id`=:id
then get the number of rows from this to work out the average:
SELECT * FROM `post_reviews` WHERE `post_id`=:id
How can I combine these three, or process this data so I can order the posts in a time period by the average rating?
ANSWER
The end result looks like this:
SELECT `posts`.`id`, avg(`post_reviews`.`review`) as `average`
FROM `posts`
JOIN `post_reviews` ON (`posts`.`id`=`post_reviews`.`post_id`)
WHERE `hub_id`=:hub_id
AND `posts`.`date`>=:start_date
AND `posts`.`date`<=:end_date
GROUP BY `post_id`
ORDER BY avg(`review`) desc
Not sure what your hub_id represents, but I assume it's necessary; also assume the key field in Posts is posts.post_id and not posts.id:
SELECT `p`.`id`, avg(`pr`.`review`) AS `average`
FROM `posts` AS `p`
JOIN `post_reviews` AS `pr` ON (`p`.`id`=`pr`.`post_id`)
WHERE `hub_id` =:hub_id
AND `p`.`date` BETWEEN CURRENT_DATE-3 AND CURRENT_DATE
GROUP BY `p`.`id`
ORDER BY avg(`review`) DESC;
See Example: sqlfiddle
Not sure about the syntax for MySQL, but the would need a join and a group by.
Something like....
SELECT post_id. avg(review)
FROM Posts P Inner Join Post_reviews PR on (p.post_id = pr.Post_id)
WHERE `hub_id`=:hub_id AND `date`>=:start_date AND `date`<=:end_date)
Group by Post_id
order by 2 desc
Here is a query that works with sqlfiddle to prove it.
SELECT
p.hub_id
,p.post_id
,p.article
,p.articleMeta
,p.date
,IFNULL(AVG(r.ratings), 0) averageRating
FROM posts p
LEFT JOIN post_reviews r ON
r.post_id = p.post_id
WHERE
hub_id = 1
AND date >= CURDATE() - 3
AND date <= CURDATE()
GROUP BY
p.hub_id
,p.post_id
,p.article
,p.articleMeta
,p.date
ORDER BY
p.date DESC
,averageRating DESC
http://sqlfiddle.com/#!2/39315/1

Ordering by posts in another table

I have created a feature where users can start new topics (similar to forums).
At the moment on a page, the query for the topics are as follows:
$q = "SELECT ".TBL_COMMUNITYTHREADS.".title, ".TBL_COMMUNITYTHREADS.".id,
".TBL_COMMUNITYTHREADS.".date, ".TBL_COMMUNITYTHREADS.".author, ".TBL_USERS.".username FROM ".TBL_COMMUNITYTHREADS."
INNER JOIN ".TBL_USERS." ON ".TBL_COMMUNITYTHREADS.".author = ".TBL_USERS.".id
WHERE type = '$type'
ORDER BY date DESC LIMIT $offset, $rowsperpage ";
The tables are constants and the offset and rowsperpage are variables passed in to limit how many posts are on a page.
At the moment though, all the topics are ordered by the date.
I want them to be ordered by the latest response.
Similarly to forums, when the reponse inside the topic is newest, that topic will go to the top.
The topics are stored in tbl_communitythreads and the replies in tbl_communityreplies.
How can I ordered them by the latest repsonse.
They are linked by the threadid in tbl_communityreplies. Also in that one is the date column.
Thankyou for reading, I just cant think of how to do this.
This:
SELECT c.title, c.id, c.date, c.author, u.username,
(
SELECT MAX(reply_date)
FROM tbl_communityreplies cr
WHERE cr.thread = c.id
) AS last_reply
FROM TBL_COMMUNITYTHREADS c
JOIN TBL_USERS u
ON u.id = c.author
ORDER BY
last_reply DESC, c.id DESC
LIMIT $offset, $rowsperpage
or this:
SELECT c.title, c.id, c.date, c.author, u.username
FROM (
SELECT cr.thread, cr.reply_date, cr.id
FROM tbl_communityreplies cr
WHERE (cr.last_reply, cr.id) =
(
SELECT last_reply, id
FROM tbl_communityreplies cri
WHERE cri.thread = cr.thread
ORDER BY
thread DESC, last_reply DESC, id DESC
)
ORDER BY
last_reply DESC, id DESC
LIMIT $offset, $rowsperpage
) q
JOIN TBL_COMMUNITYTHREADS c
ON c.id = q.thread
JOIN TBL_USERS u
ON u.id = c.author
ORDER BY
q.reply_date DESC, q.id DESC
The former query is more efficient for large values of $offset, or if you have few threads with lots of replies.
Issue the following commands:
CREATE INDEX ix_communitythreads_replydate_id ON TBL_COMMUNITYTHREADS (reply_date, id)
CREATE INDEX ix_communitythreads_thread_replydate_id ON TBL_COMMUNITYTHREADS (thread, reply_date, id)
for this to work fast.
by join with another table , then you can order by column from this table.

Running a query with PHP/MySQL then reordering the results by another column

This SQL query gives me the results I want; however, I want the results ordered by a different column:
SELECT *
FROM post
INNER JOIN account ON post.account_id = account.account_id
WHERE post_id > new
ORDER BY post_date
ASC LIMIT 10;
I can not simply change ORDER BY post_date ASC to ORDER BY post_id DESC, while that will in fact order the query the way I want it... it will give me the wrong 10 posts.
I simply want to take the EXACT RESULTS of the above query then reorder the results by the post_id.
I would like to do this with SQL if possible, if not I could order the results by adding the results into a new array reversed.
Use a subquery to reorder:
SELECT * FROM (
SELECT *
FROM post
INNER JOIN account ON post.account_id = account.account_id
WHERE post_id > neww
ORDER BY post_date ASC LIMIT 10;
) ORDER BY post_id
Use a subquery:
SELECT * FROM (
SELECT *
FROM post
INNER JOIN account
ON post.account_id = account.account_id
WHERE post_id > neww
ORDER BY post_date ASC
LIMIT 10) AS T1
ORDER BY post_id DESC

Categories