Advanced MySQL ORDER BY - php

I am making a basic forum using PHP and MySQL. I am using one table for all of the threads. It is named forum_posts. It has these fields:
id, creator, time, title, message, thread_reply, forum_id, locked, sticky
If the post is a new thread, then thread_reply is set to 0. Otherwise, thread_reply is set to the id of the thread that this post is replying to. On the page that shows all of the threads, I want to order the threads by the time of the last post that was replied to it. I am using this query:
SELECT `id`, `title`, `creator` FROM `forum_posts` WHERE `thread_reply` = 0 AND `forum_id` = 1 ORDER BY -`time`
The only problem is that query only orders by the time the thread was created, instead of the time the last reply was to the thread. If you don't understand what I am talking about, go look at the home page of any forum and look how it orders the threads by the time of the last post to the thread, not the time the thread was created.

I made a custom forum last week (as phpbb just didn't suit my needs).
I would first advise the following structure:
table for users
table for categories (main sections of forum)
table for threads (these are inside categories)
table for posts (these are in individual threads)
To order the posts by the last action, I simply created a field in the Threads table that was called last_action or something along those lines.
Then, when the thread is first created, I set that value to the date it was created. Then, whenever a post is edited or a new post is added to that thread, that value updates. This then means you just do:
SELECT blah, blah, blah FROM threads WHERE cat=4 ORDER BY last_action DESC
Hopefully that helps you.

Easiest way is probably to make it so that instead of using the test thread_reply=0, you use the test thread_reply=id. Make a thread starting-thread be a reply to itself. If you use that for something else it might make it a bit more messy, but you can thus do something like
SELECT * FROM
(SELECT `id`, `title`, `creator`, b.rtime FROM `forum_posts`
WHERE a.`thread_reply` = a.`id` AND `forum_id` = 1 ) a
LEFT JOIN (SELECT `thread_reply`, MAX(`time`) rtime FROM `forum_posts`
WHERE `forum_id`=1
GROUP BY `thread_reply`) b ON a.`id`=b.`thread_reply`
ORDER BY `rtime` DESC
Note that the first subquery is unnecessary, however it is probably more efficient to first filter by forum and "is it a first post".
Also, why use ORDER BY -time ASC when you could use ORDER BY time DESC?
But yes, the best way is probably just to have a table for topics, and a table for replies. (First reply in a topic is the first post; the topic itself has no inherent body text)

Use
SELECT `op`.`id`, `op`.`title`, `op`.`creator`
FROM `forum_posts` `op`, `forum_posts` `rep`
WHERE (`op`.`thread_reply` = 0 OR `op`.`thread_reply` = `rep`.`id`)
AND `op`.`forum_id` = 1
ORDER BY IF(`op`.`thread_reply` = 0, -`op`.`time`, -`rep`.`time`)
Basically what I do here is find the reply if applicable and order by its time.

If you have the possiblity:
Create two tables:
Threads: id, subject, date_created, date_last_reply, ..
Posts: id, thread_id, date, ..
SELECT * FROM threads ORDER BY date_last_reply

As you're only getting the "start" post with that query you've got no way to order by the latest reply. You need to add another column (last_reply_time etc) and have your application update that field every time a new thread is posted, and then order by that.

Related

Ignoring Duplicate Records But Bumping Them Up Top

I am making a feed system similar to the way facebook works now what I am trying to implement is the duplicate functionality.
if a user writes the same post= then don't display it duplicatly as PHP normally does by default but bump it up the top so users knows another user as re-wrote it
Here's my query
$select_posts_from_groups_query = $db->query("SELECT * FROM spud_groups_posts LEFT JOIN spud_groups_members
ON spud_groups_posts.group_url = spud_groups_members.gurl WHERE member_name='$mybb_username' GROUP BY post_body ORDER BY time_posted DESC" );
how can I get it to bump it self as the latest update once a user re duplicates it it would let users know that some one else has shared it
Thanks ;)
Three solutions
Assuming you have a update timing field on your data record called time_updated, there are a couple of things you could do:
you could simply max the two:
ORDER BY max(time_posted,time_updated) DESC
This might give issues with NULL values though
substitute NULLs in the time_updated
ORDER BY nvl(time_updated, time_posted) DESC
(Oracle sql syntax nvl() substitutes NULL in the 1st param with the 2nd param - don't know what DB you are using)
always store time_updated (the best solution IMO). When creating the post (initially) set time_updated = time_created AND use
ORDER BY time_updated DESC

PHP, MySQL selecting posts and comments

I'm having problems with my script which need to select all my posts and related comments.
Right now I've following query:
$sql = "SELECT posts.post_title, posts.post_modified, post_content,update_modified, update_content
FROM posts
LEFT JOIN updates
ON posts.post_ID = updates.update_post_ID";
The query works great besides if the post has multiple comments it gives me multiple entries.
I've searched around but unfortunately I wasn't able to re-script my query for my needs.
I really hope someone can help me out?
I think you want the DISTINCT keyword, used as SELECT DISTINCT ... to avoid duplicates. However if I understand correctly your comments are in the updates table and you're pulling update_modified and update_content into your recordset. So assuming those are (potentially) unique values then DISTINCT will not collapse them down. It might be best to only pull updates.update_post_ID with DISTINCT, then pull whatever you need from updates based on the IDs you retrieve when you need it.
If you want to return only 1 row per post, with all the comments with the post, the easiest way is using GROUP_CONCAT(). This returns a csv of all the column data. Assuming that update_content is the post comments, try something like -
SELECT posts.post_title, posts.post_modified, post_content, GROUP_CONCAT(update_modified), GROUP_CONCAT(update_content)
FROM posts
LEFT JOIN updates
ON posts.post_ID = updates.update_post_ID
GROUP BY updates.update_post_ID
note - GROUP_CONCAT() has a group_concat_max_len default of 1024. If your comments become too long you will want to increase this before running the GROUP_CONCAT() query or the comments will be truncated -
SET [GLOBAL | SESSION] group_concat_max_len = 10240; // must be in multiples of 1024
SELECT id, name
GROUP_CONCAT(comment) AS comment
FROM table
GROUP BY name;
you will also need to be aware of max_allowed_packet as this is the limit you can set var_group_concat_max_len to.

mysql how to show some rows of table depending on one field and depending on one field in other table?

I have a script where members login and read posts by catagory that I have in
Table called posts, then they click a button and an entry is inserted into Table
called postsread collecting the postname, their memberid, and the date showing
that it had been read by them. What I am looking for is a query that will
display to them only the posts that they have not already read.
**Tables** **Fields**
posts id, name, date, from, topic, info, cat
postsread id, postname, memberid, date
users id, memberid, pass, fname, lname, email
Sessions is already holding their $_SESSION['memberid'], but am unsure of how
to query between the two tables to get what I'm looking for.
It would be like: Show all posts in Posts except those in Postsread with the members
memberid next to the corresponding postname.
I am using php version 5.3, and mysql 5.0.96.
I am using the following to display posts from database:
$sql=mysql_query("SELECT * FROM posts WHERE cat='1' ORDER BY date DESC");
But this does not differentiate between if the member has clicked stating they have
seen them yet or not.
I have looked many places and see examples that are close but just cant get any to
fit what I am needing. I don't fully understand how to write this. I have tried many
with no success. If you need extra description please ask. Thank you for your time.
You need to construct a JOIN condition against the posts_read table, or use an IN clause to exclude them. You should be very careful with your indexes as these sorts of queries can get extremely slow and database intensive with non-trivial amounts of data.
SELECT * FROM posts WHERE cat=:cat AND name NOT IN (SELECT postname FROM postsread WHERE memberid=:memberid)
Here :cat and :memberid are placeholders for the appropriate values. Using PDO you can bind directly to those using the execute function.
As a note, joining on strings is a lot slower than joining on id type values. You might want to make your postsread table reference id from posts instead.
sql=mysql_query("SELECT * FROM posts WHERE cat='1' AND name NOT IN (SELECT postname FROM postsread WHERE memberid='$memberid') ORDER BY date DESC") or die (mysql_error());
Tadman was right on with his answer; I had something in the code that was not supposed to be there, but could not find it. Eventually I just rewrote the thing and it worked. The above worked for me, and I thank you Tadman for your help.

How do display different mysql table rows on a page, in the order they where inserted

How do social network like facebook display different content, such as photos, status, user activities onto the page, in the order it was originally posted. I have done a lot research but can't seem to find any good examples on how it's done using PHP and MYSQL.
I have include a visual example of what i mean.
I think what you mean is that the different contents are in separate tables like photos, status updates, newconnections etc. And you want the final wall to pull data from all these tables into a single wall in the same chronological order of time. So, what I will suggest is to create a new table for the wall with the following main fields :
Activity_identifier,activity_id and timestamp. Here Activity_identifier is the predefined identity of the activity such as sharing a photo (lets say 1), posting on the wall (lets say 2) etc and timestamp is the time when that activity was recorded by the user, and activity_id is the id of that activity in the corresponding table.
Everytime a new activity is created by the user, while populating the corresponding table for that activity, also record a corresponding entry in this table for wall.
Now your final query to create the wall will just have to call this new table for wall with ORDER BY TIMESTAMP DESC, then the resulting data needs to be joined with other activity tables (use Activity_identifier intelligently here) through the foreign-key activity_id to recreate the wall in the exact chronological order that the user created them.
For incremental call to the wall, maintain a timestamp of last query (lets say xtime) and every call do timestamp > xtime ... ORDER BY TIMESTAMP DESC.
Hope this helps!
Use timestamp column and keep track of each entry's insertion time in your code.
When querying data, ORDER BY timestamp column.
you can simply store a field in database which contains timestamp or creation date when specyfied content was created and while selecting it from DB just order it by mentioned creation_date :)
Use Timestamps fields in your tables (at least that's what i do) alogn with every insert (or update), with a default value of CURRENT_TIMESTAMP, this will let you sort by date (and time) of insertion with simple SQL statements
When they do the query they have an ORDER BY clause. EG:
SELECT `photos`
FROM `submissions`
ORDER BY `dateline` DESC
dateline can be stored as an integer unix time and then used with simple math to set additional parameters. EG:
$sql = "SELECT `photos`
FROM `submissions`
WHERE `dateline` >= " . (date() - 60*60*24*7) . "
ORDER BY `dateline` DESC";
This would give you only submissions from the past week.
Think of it as publishers and subscribers. Each message has a list of receivers in a junction table.
Message
------------
message_id
message_body
Message_receivers
------------
message_id
user_id
Message_photos
------------
message_id
photo
Here's how I get messages for me (user_id 1):
SELECT m.*, mp.*
FROM Message m
JOIN Message_receivers mr
ON mr.message_id = m.message_id AND mr.user_id = 1
LEFT JOIN Message_photos mp
ON mp.message_id = m.message_id
ORDER BY m.message_id DESC
You update these lists when the user posts rather than try to build the list when a user reads their news feed, since even though you may have a lot of posts, you will have more reads.
The Message_receivers records will expire and roll off. The news feed only goes back so far, though if you go to a user's page, you can see all of their posts.

PM system - previewing previous PMs

I am working on a PM system where I'd like to have the previous sent PMs for one conversation, listed above the last received PM. But my question is: how do I go about setting up such a table in a database? I toyed for a while about using an id for each specific conversation, but what would the source for that id be? I can't use auto increment (it seems), because I'm using it for the primary "id" column.
Or maybe there's a completely different way I can experiment with the already available columns (id, from, to, subject, message, sent, read, deleted); but how? Please help a lost man out.
You could add a origin_id column to your table that contains the id of the root/original message, or NULL if it's a new discussion (root).
Then you can get the root messages by filtering those than have origin_id = NULL and then group by origin_id to get the message thread.
Okay, so I have got it partly solved...
I used another table containing the one column which holds the subject of the PM. I also have a new column in the regular "pms" table that holds the same ID to be able to join the tables together.
However, when I select all the PMs to show them in the inbox, I have not found a way to group the conversations in order by if they're read or not. I'm currently using this SQL query:
SELECT *
FROM `pms`
JOIN `pm_conversations` ON (pms.ConvID = pm_conversations.ID)
WHERE pms.To='username'
GROUP BY pm_conversations.ID
ORDER BY pms.ID
I came up with this:
SELECT MAX(pms.ID) as pmIDS,
pms.*,
pm_conversations.*
FROM `pms`
JOIN `pm_conversations` ON (pms.ConvID = pm_conversations.ID)
WHERE `To`='".$UserActive."'
GROUP BY pm_conversations.ID
ORDER BY pmIDS DESC

Categories